일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 어떻게 나답게 살 것인가
- 재택근무
- 소프시스
- 목적 중심 리더십
- 면접
- 안드로이드
- 프래그먼트
- 한달브런치북만들기
- 한달어스
- T자형인재
- 1일1커밋
- 베드테이블
- 캐치마인드
- 자취필수템
- 커스텀린트
- 북한살둘레길
- 목적중심리더십
- 함수형 프로그래밍
- 아비투스
- 소프시스 밤부 좌식 엑슬 테이블
- 좌식테이블
- 끝말잇기
- 한달독서
- 테트리스
- 한단어의힘
- 슬기로운 온라인 게임
- 지지않는다는말
- 브런치작가되기
- 리얼하다
- 베드트레이
- Today
- Total
정상에서 IT를 외치다
[Android, Retrofit, RxJava2] RxJava2 을 사용한 Retrofit 통신 본문
안녕하세요. 블랙진입니다.
안드로이드 통신을 하는데 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
참고 도서
- 커니의 코틀린
'안드로이드' 카테고리의 다른 글
[Android, Anko Commons, Dialog] Anko Commons 를 사용한 다이얼로그 (0) | 2018.05.15 |
---|---|
[Android, RxEventBus] Rx를 사용한 EventBus (0) | 2018.05.10 |
[Android, ConstraintSet] ConstraintSet 을 통한 애니메이션 (4) | 2018.05.03 |
[Android, SpannableStringBuilder] textview 에서 문자 일부 수정 하기 (0) | 2018.04.20 |
[Android, EventBus] 이벤트 버스 사용법 (2) | 2018.04.18 |