정상에서 IT를 외치다

[Android, Retrofit, RxJava2] RxJava2 을 사용한 Retrofit 통신 본문

안드로이드

[Android, Retrofit, RxJava2] RxJava2 을 사용한 Retrofit 통신

Black-Jin 2018. 5. 8. 14:34
반응형

안녕하세요. 블랙진입니다.


안드로이드 통신을 하는데 Retrofit 을 많이 사용합니다. 아래는 Retrofit 에 대한 문서입니다.


- 한글

http://devflow.github.io/retrofit-kr/


- 영어

https://square.github.io/retrofit/




저는 RxJava2 를 사용한 Retrofit 통신에 대해 다뤄 볼까 합니다.


0. 환경설정


app 폴더 안에 있는 build.gradle 에 아래와 같이 설정 합니다.


- retrofit (최신 버전을 확인해 주세요)

//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'


- 로그를 보기 위한 okHttp

//okHttp
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'
implementation 'com.squareup.okhttp3:okhttp:3.8.1'


- retrofit 에서 받은 응답을 Observable 로 변환 (retrofit 과 같은 버전으로 해주시면 됩니다)

//rx retrofit

implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'


- Rx 를 사용하기 위한 rxJava2  (최신 버전을 확인해 주세요)

//rx android
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.3'



5개의 파일을 만듭니다. (ApiProvider , MyApi, MyModel, MyModelItem, MainActivity)


1. ApiProvider.kt

fun provideMyApi(): MyApi
= Retrofit.Builder()

// 통신할 서버의 주소를 입력합니다.
.baseUrl("http://www.example.com")

// 네트워크 요청 로그를 표시해 줍니다.
.client(provideOkHttpClient(provideLoggingInterceptor()))


// 받은 응답을 옵서버블 형태로 변환해 줍니다.
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())

// 서버에서 json 형식으로 데이터를 보내고 이를 파싱해서 받아옵니다.
.addConverterFactory(GsonConverterFactory.create())

.build()


.create(MyApi::class.java)


// 네트뭐크 통신에 사용할 클라이언트 객체를 생성합니다.
private fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
val b = OkHttpClient.Builder()
// 이 클라이언트를 통해 오고 가는 네트워크 요청/응답을 로그로 표시하도록 합니다.
b.addInterceptor(interceptor)
return b.build()
}

// 네트워크 요청/응답을 로그에 표시하는 Interceptor 객체를 생성합니다.
private fun provideLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY

return interceptor
}




2. MyApi

interface MyApi {

/**
* POST -> POST 통신할 주소를 입력합니다.

* Field -> POST 통신을 위한 파라미터를 입력합니다.

*

* Return 값은 Observable<MyModel> 형태로 되어 있지만

* Model이 없는 경우 Observable<Any> 로 적으시면 됩니다.
*/
@FormUrlEncoded
@POST("/example/post/address")
fun postGetMyExample(@Field("field_1") field_1: String,
@Field("field_2") field_2: String): Observable<MyModel>

}




3. MyModel

class MyModel(val result: Boolean, val description: String, val data: MyModelItem)


3-1 MyModelItem

class MyModelItem(@field:SerializedName("parm_1") val parm1: String,
@field:SerializedName("parm_2") val parm2: String,
@field:SerializedName("parm_3") val parm3: String)


위 Model 은 만약 아래와 같은 데이터가 올 경우 받기 위한 형식입니다.


( model class 변수명과 서버 데이터의 변수명이 다른 경우 @field:SerializedName 어노테이션을 통해 일치 시켜줄 수 있습니다. )


1
2
{ "result" : ture, "description" : "정상에서 IT를 외치다", "data" : {"parm_1" : " ", "parm_2" : " ", "parm_3" : " "} }
cs





4. MainActivity

import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

class MainActivity: AppCompatActivity() {

private val api by lazy { provideMyApi() }

private val disposable = CompositeDisposable()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

disposable.add(api.postGetMyExample("인자_1","인자_2")
// REST API 를 통해 받은 응답에서 Result 값을 추출합니다.
.flatMap {
if (it.result) {

// 결과를 다음 스트림으로 전달합니다.
Observable.just(it.data)

} else {

// 검색 결과 false
// 에러를 발생시켜 에러 메시지를 표시하도록 합니다.
// 곧바로 에러 블록이 실행됩니다.

Log.d("MyTag", it.description)

Observable.error(IllegalStateException("return result is false"))
}
}

// 이 이후에 수행되는 코드는 모두 메인 스레드에서 실행합니다.
.observeOn(AndroidSchedulers.mainThread())

// 구독할 때 수행할 작업을 구현합니다.
.doOnSubscribe { }

// 스트림이 종료될 때 수행할 작업을 구현합니다.
.doOnTerminate { }

// 옵서버블을 구독합니다.
.subscribe({ data ->
// data 를 받아 처리합니다.
// 작업 중 오류가 발생하면 이 블록은 호출되지 않습니다.

}) {
// 에러블록
// 네트워크 오류나 데이터 처리 오류 등
// 작업이 정상적으로 완료되지 않았을 때 호출됩니다.
Log.e("MyTag", "${it.message}")

})
}

override fun onDestroy() {
super.onDestroy()

// Activity 가 종료되면
// 사용중인 통신을 중단합니다.
disposable.clear()
}
}

Observable 을 사용시 io.reactives 에서 import 되도록 해야합니다.


만약 Model (3, 3-1) 을 설정하지 않으셨다면 아래 부분을 지우고

.flatMap {
if (it.result) {

// 결과를 다음 스트림으로 전달합니다.
Observable.just(it.data)

} else {

// 검색 결과 false
// 에러를 발생시켜 에러 메시지를 표시하도록 합니다.
// 곧바로 에러 블록이 실행됩니다.

Log.d("MyTag", it.description)

Observable.error(IllegalStateException("return result is false"))
}
}


subscribe 부분에서 response.body 정보를 받아 처리하시면 됩니다.

// 옵서버블을 구독합니다.
.subscribe({ it ->
// response.body 를 받아 처리합니다.
// 작업 중 오류가 발생하면 이 블록은 호출되지 않습니다.
Log.d("MyTag", it.toString)

}) {
// 에러블록
// 네트워크 오류나 데이터 처리 오류 등
// 작업이 정상적으로 완료되지 않았을 때 호출됩니다.
Log.e("MyTag", "${it.message}")

})



Retrofit 을 Rx 식으로 사용하면 여러 에러를 미연에 방지할 수 있습니다.


1. Model 의 인자와 변수명이 다르거나 없어도 앱이 죽지 않습니다.

class MyModel(val result: Boolean, val description: String, val data: MyModelItem)

예를 들어 result 의 변수명은 Boolean  이지만 서버로 부터 String 형식으로 오거나 데이터가 없어도 Rx 식은


에러블록으로 이동 되고 앱이 Crash 되지 않습니다.



2. Retrofit 통신에서 UI 를 변화 시키는 도중  Activity 가 사라지면서 발생하는 에러를 막을수 있습니다.

override fun onDestroy() {
super.onDestroy()

// Activity 가 종료되면
// 사용중인 통신을 중단합니다.
disposable.clear()
}

위 와같이 Activity 가 종료되면 disposable.clear() 함수로 통신을 중단하여 Activity 가 사라질 때 화면 UI 를 바꾸면서 발생하는 에러를 방지할 수 있습니다.



3. 무엇보다 Rx 식을 사용하면 가독성이 좋아 코드의 유지 보수가 편해집니다.





참고 사이트


- https://thdev.tech/androiddev/2016/11/13/Android-Retrofit-Intro.html


참고 도서


- 커니의 코틀린

반응형
Comments