RxJava3와 Retrofit2 를 사용하여 통신 모듈을 만들어보자
추가로 여기서 DI는 hilt를 사용했다.
api는 ITunes Api를 사용하겠다. 샘플 코드로 적당한 api인 것 같다.
만들 클래스는 아래와 같다.(당장 api 통신을 하기에는 약간의 부가적인 것도 있지만 확장성을 생각해서 전부 설명하겠다.)
- ITunesService
- ITunesClient
- NetworkModule
ITunesService 이다.
반환형이 Single으로 둔 이유는 RxJava의 Observable중 Single이 한 번의 이벤트에서 사용될때 쓰이기 때문이다.
참고로 나는 여기서 한번 @Query의 어노테이션을 깜박하고 빼먹었는데
`No Retrofit annotation found. (parameter #1)` 의 런타임에러가 발생하니 다음번엔 조심하자!
interface ITunesService {
@GET("/search?")
fun fetchTrackList(
@Query("term") term:String,
@Query("entity") entity:String,
@Query("limit") limit: Int,
@Query("offset") offset:Int
): Single<TrackListResponse>
}
ITunesClient 이다.
class ITunesClient @Inject constructor(
private val iTunesService: ITunesService
) {
fun fetchTrackList(
term: String,
entity: String,
limit: Int,
offset: Int
): Single<TrackListResponse> = iTunesService.fetchTrackList(term, entity, limit, offset)
}
NetworkModule 이다.
OKHttp와 Retrofit을 provide해주고 ITunesService와 ITunesClient를 생성해준다.
참고로 나는 provideRetrofit()쪽에서 addCallAdapterFactory()를 빼먹은 적이 있는데
`Could not locate call adapter for io.reactivex.rxjava3.core.Single...` 와 같은 런타임 에러가 발생한다.
addCallAdapterFactory()를 추가해도 같은 에러가 계속 발생했다...;;
이유는 build.gradle(모듈)에서 dependency를 추가할 때
`implementation "com.squareup.retrofit2:adapter-rxjava3:$retrofit_version"` 가 rxJava3이 아니라 2로 되어있었기 때문이었다. 실제 RxJava 버전과 같은 것인지 반드시 확인하자.
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
const val BASE_URL = "https://itunes.apple.com/"
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
})
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
}
@Provides
@Singleton
fun provideITunesService(retrofit: Retrofit): ITunesService {
return retrofit.create(ITunesService::class.java)
}
@Provides
@Singleton
fun provideITunesClient(iTunesService: ITunesService): ITunesClient{
return ITunesClient(iTunesService)
}
}
이제 사용하려는 repository나 viewModel 쪽에서 호출하면 된다.
아래의 경우처럼 viewModel에서 repository를 통해 호출할 땐 아래 코드처럼 observeOn, subscribeOn, subscribe()등과 같이 적절한 연산자를 이용해서 처리하면 된다. 아래에서 쓴 연산자는 일부의 연산자이니 적절한 연산자를 조사하고 적재적소에 찾아서 쓰는 연습이 필요하다.
disposable.add(
repository.fetchTrackList(
term = "greenday",
entity = "song",
limit = 20,
offset = 0
).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
Timber.e("fetchTrackList success ${it.results}")
}, {
Timber.e("fetchTrackList fail")
})
)
⚠주의
위 코드의 disposable에 집중해보자.
저것은 전역변수로 선언한
private val disposable = CompositeDisposable()
이다.
그리고 ViewModel에 onCleared() 될때 아래와 같이 dispose 시켜야한다.
마치 coroutine이 ViewModelScope{} 이 뷰모델의 lifecycle에서 동작하도록 하는 것이다.
이를 제대로 처리하지 않으면 앱을 종료해도 사용이 끝난 리소스를 반납하지 않아 메모리릭이 발생할 수 있다.
override fun onCleared() {
super.onCleared()
if (!disposable.isDisposed) {
disposable.dispose()
Timber.e("disposable disposed")
}
}
'Programming > Android' 카테고리의 다른 글
[Android] App 코드 난독화와 Gson으로 json 파싱이 안되는 이슈 (0) | 2021.11.16 |
---|---|
'Open Source License' Activity plugin 적용하기 (0) | 2021.10.14 |
RxJava 적용기 (0) | 2021.10.03 |
2021 Droid Knights 컨퍼런스 후기 (0) | 2021.09.25 |
Android Studio에서 Profiler 적용기 (0) | 2021.09.04 |