RetrofitはAPI Clientを作成するためのライブラリです。
インターフェースを定義するだけで作れるのでとても楽ちんです。
またRetrofitはRxJavaを使って非同期の処理をいい感じに書くようにすることもできますしHTTP通信にはOkHttpをJSONのパースにはmoshiを使ってAPIクライアントを実装します。
実際どうやって使うのかをやっていこうと思います。
環境
Mac OSX
Android Studio 2.0
Kotlin 1.0.0-beta-2423
Kotlin pluginをAndroid Studioに入れておいてください
プロジェクトの作成
とりあえずBlankActivityのあるプロジェクトを適当に作成します。
作成されたJavaソースコードををKotlinに変換します。
上のメニューからCode->Convert Java File to Kotlin Fileで変換できます。
app/build.gradle
に以下を追加します
+apply plugin: 'kotlin-android'
dependencies {
...
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ compile 'io.reactivex:rxkotlin:0.24.100'
+ compile 'io.reactivex:rxandroid:1.0.1'
+ compile 'io.reactivex:rxjava:1.0.15'
+ compile 'com.squareup.okhttp:okhttp:2.0.0'
+ compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
+ compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
+ compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
+ compile 'com.squareup.retrofit:converter-moshi:2.0.0-beta2'
}
+buildscript {
+ ext.kotlin_version = '1.0.0-beta-2423'
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-+plugin:$kotlin_version"
+ classpath "org.jetbrains.kotlin:kotlin-android-+extensions:$kotlin_version"
+ }
+}
とりあえずビルドが通るところまでは確認しておきましょう
実装
Retrofitのインターフェイスを作成します。
今回はRxを使うのが目的なのでインターフェイスもRx用にします。
interface SplatoonService {
@GET("/schedule.json")
fun schedule(): Observable<ScheduleResponse>
}
ポイントは戻り値がrx.Observableであることです。
ScheduleResponseはイカのようにしています
data class ScheduleResponse(
var updateTime: Long,
var schedule: List<Schedule>
) : Serializable
data class Schedule(
var startTime: Long,
var endTime: Long,
var regular: Match,
var ranked: Match
): Serializable
data class Match(
var maps: List<Map>,
var rulesJP: String = "ナワバリバトル",
var rulesEN: String = "Turf War"
): Serializable
data class Map(
var nameJP: String,
var nameEN: String
): Serializable
APIクライアントのインスタンスを生成します。
fun client(): SplatoonService {
val moshi = Moshi.Builder()
.build()
val okClient = OkHttpClient()
val builder = Retrofit.Builder()
.client(okClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl("https://splatoon.ink")
.build()
return builder.create(SplatoonService::class.java)
}
使い方
使い方は次の通りです。
client().schedule().
subscribeOn(Schedulers.newThread()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(object : Subscriber<ScheduleResponse>(){
override fun onNext(r: ScheduleResponse?) {
...
}
override fun onError(e: Throwable?) {
...
}
})
このコードはUIThreadで実行されることを想定して
.subscribeOn(Schedulers.newThread())
で別スレッドで実行し
.observeOn(AndroidSchedulers.mainThread())
でコールバックをMainThreadで実行されるようにしています。
冗長なところをちょっと良くしてみる
ただこれだとせっかくのkotlinなのにsubscribeのところが冗長になってしまいます。
ラムダ式使ってもっといい感じに書けるように拡張関数を使ってみました。
public fun <T> Observable<T>.onError(block : (Throwable) -> Unit): KSubscription<T> {
return KSubscription(this).onError(block)
}
public fun <T> Observable<T>.onCompleted(block : () -> Unit): KSubscription<T> {
return KSubscription(this).onCompleted(block)
}
public fun <T> Observable<T>.onNext(block : (T) -> Unit): KSubscription<T> {
return KSubscription(this).onNext(block)
}
public fun Subscription.onError(block: (Throwable) -> Unit): Subscription {
return this
}
public class KSubscription<T>(val observable: Observable<T>) {
private var error: (Throwable) -> Unit = { throw it }
private var completed: () -> Unit = {}
private var next: (T) -> Unit = {}
fun onError(block: (Throwable) -> Unit): KSubscription<T> {
error = block
return this
}
fun onCompleted(block: () -> Unit): KSubscription<T> {
completed = block
return this
}
fun onNext(block: (T) -> Unit): KSubscription<T> {
next = block
return this
}
fun subscribe(): Subscription = observable.subscribe(object : Subscriber<T>(){
override fun onError(e: Throwable?) {
if ( e == null ) {
return
}
error.invoke(e)
}
override fun onCompleted() {
completed.invoke()
}
override fun onNext(t: T) {
next.invoke(t)
}
})
}
さっきのコードをこんな風に書くことができます。
client().schedule().
subscribeOn(Schedulers.newThread()).
observeOn(AndroidSchedulers.mainThread()).
onNext {
...
}.
onError {
...
}.
subscribe()
まとめ
というわけで僅かなコードでAPIクライアントを実装することができました。
まぁちゃんと使う場合はネットワークがつながってるかの確認やAccessTokenがあるかないか確認して すべてonErrorに入れたかったりすると思いますが
これをJavaでやろうとするとメソッドで一旦引数としてObservable渡さなきゃいけなかったりするし
メソッドチェーンを作ろうと思ったらClassを継承してラップしてみたいなことをしなきゃいけないのを
Kotlinならカジュアルにメソッドを追加できるのは便利ですね。