Help us understand the problem. What is going on with this article?

KotlinでRetrofitとRxを使ってAPIクライアントをサクッと実装する

More than 3 years have passed since last update.

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に以下を追加します

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()

まとめ

Screenshot_20151204-031131.jpg

というわけで僅かなコードでAPIクライアントを実装することができました。
まぁちゃんと使う場合はネットワークがつながってるかの確認やAccessTokenがあるかないか確認して すべてonErrorに入れたかったりすると思いますが
これをJavaでやろうとするとメソッドで一旦引数としてObservable渡さなきゃいけなかったりするし
メソッドチェーンを作ろうと思ったらClassを継承してラップしてみたいなことをしなきゃいけないのを
Kotlinならカジュアルにメソッドを追加できるのは便利ですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした