この手の記事、何番煎じだよ!って感じだけど。。。
なにげに自分で1から組んだことなかったから経験値が欲しくてやってみた。
素のRetrofitを使うよりRxを噛ませたほうがコールバックとかが楽だしね!
この実装が正解かはよくわからないが、将来自分が簡単に実装できるようにコピペ出来るようにしておく!w
実装
依存
// RX
implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
// Retrofit
def retrofit = "2.5.0"
def moshi = "1.8.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit"
implementation "com.squareup.moshi:moshi:$moshi"
implementation "com.squareup.moshi:moshi-kotlin:$moshi"
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation "com.squareup.okhttp3:okhttp:3.12.0"
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
// Log
implementation "com.jakewharton.timber:timber:4.7.1"
ConverterはMoshiで。Moshiの使い勝手が未だにあまりわかってないけど、、、。Gsonでよくね?w
通信内容を見たいからstethoも入れておく。
TimberはDebugTree入れておくとログがとっても見やすく!(今回はやらないけど)
API基底クラス
// API基底
abstract class ApiClient {
abstract val baseUrl: String
// Retrofitのインスタンス
val retrofit: Retrofit by lazy {
getBaseRetrofit()
.baseUrl(baseUrl)
.client(requireNotNull(getOkHttpClientBuilder(baseOkHttp).build()))
.build()
.let { requireNotNull(it) }
}
// 基底となるRetrofit
private fun getBaseRetrofit(): Retrofit.Builder = Retrofit.Builder()
.addConverterFactory(getConverterFactory())
.addCallAdapterFactory(geAdapterFactory())
// 基底となるOkHttp
private val baseOkHttp = getCommonHttpClientBuilder()
.addInterceptor(Interceptor { chain ->
val original = chain.request()
//header
val request = original.newBuilder()
.header("Accept", "application/json")
.method(original.method(), original.body())
.build()
return@Interceptor chain.proceed(request)
})
.addInterceptor { it.proceed(addUserAgent(it.request())) }
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
// OkHttpを改変用(Interceptorを個別設定したいときとか)
protected open fun getOkHttpClientBuilder(base: OkHttpClient.Builder): OkHttpClient.Builder =
base
// ConverterFactoryを改変用(Moshi,Gsonとか)
protected open fun getConverterFactory(): Converter.Factory = MoshiConverterFactory.create(
Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
)
// AdapterFactory改変用(Rx,Coroutinesとか)
protected open fun geAdapterFactory(): CallAdapter.Factory = RxJava2CallAdapterFactory.create()
private fun getCommonHttpClientBuilder(): OkHttpClient.Builder {
return if (BuildConfig.DEBUG) {
OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
Timber.tag("OkHttp").d(it)
}).apply {
level = HttpLoggingInterceptor.Level.BODY
})
.addNetworkInterceptor(StethoInterceptor())
} else {
OkHttpClient.Builder()
}
}
private fun addUserAgent(req: Request): Request {
val agent = "AppName/" + BuildConfig.VERSION_NAME + " Android/" + BuildConfig.VERSION_NAME
return req.newBuilder().header("User-Agent", agent).build()
}
}
とりあえず、複数のサービスのAPIを叩く想定。
あとRxじゃないパターンも有るのかな?と、、、
てかCoroutinesでやってみたいから一旦この形で(時間あれば Coroutinesで試して次の記事で書く予定)
APIクラス
// Rxで通信するAPIクラス
class RxApiClient : ApiClient() {
override val baseUrl: String = "https://randomuser.me/"
val service: RxApiService = retrofit.create(RxApiService::class.java)
}
基本的にApiClientを継承してBaseUrlを設定するだけ。
Interceptorを追加したいときはgetOkHttpClientBuilderをオーバーライドしてあげる。
ネットワーク監視とかあるといいかもね。
今回は以下のサービスを利用させてもらった。
Service
// Rx用のサービス
interface RxApiService {
@GET("api")
fun getRandomUser(): Single<RandomUser>
}
Singleで返すようにする。
使ってみる
//ViewModel
class MainViewModel : RxViewModel() {
private val rxApiClient = RxApiClient()
fun getRxDeRandomUser() {
rxApiClient.service.getRandomUser()
.subscribeOn(Schedulers.io())
.map {
val user = it.results[0]
"""name : ${user.name.title} - ${user.name.first} - ${user.name.last}
gender : ${user.gender}
email : ${user.email}
phone : ${user.phone}
cell : ${user.cell}
""".trimIndent()
}.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
Log.d("hoge", "getRxDeRandomUser $it")
},
onError = {
Log.d("hoge", "onError getRxDeRandomUser $it")
}
).addTo(disposables)
}
}
なんとなくデータ加工して返すようにしてみた。
RxとRetrofit掛け合わせると結構スッキリ書けるよね!
あ、DataClass書かなかったけど、よしなに定義で!w