はじめに
HTTPクライアントライブラリFuelを初めて使ってみたの続編です。
Kotlin/Andorid用のHTTPクライアントライブラリであるFuelのRXJavaメソッドを試してみました。
https://github.com/kittinunf/Fuel
以降のコード例はKotlinです。
導入
Fuelの他にRX Java, GSONへをapp/build.gradleに追加します。
dependencies {
・・・
// Fuel
compile 'com.github.kittinunf.fuel:fuel:1.9.0'
compile 'com.github.kittinunf.fuel:fuel-android:1.9.0'
compile 'com.github.kittinunf.fuel:fuel-rxjava:1.9.0'
// RX Java
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// GSON
compile 'com.google.code.gson:gson:2.6.2'
・・・
}
実装
例として、Qiitaのタグ取得APIへGetリクエストを送ってみます。
https://qiita.com/api/v2/docs#タグ
例ではリクエストパラメータはページのみ指定します。
https://qiita.com/api/v2/tags?page=1
このGETリクエストのレスポンスは以下のようなJSONになります。
[
{
"followers_count": 0,
"icon_url": null,
"id": "Cutter",
"items_count": 1
},
{
"followers_count": 0,
"icon_url": null,
"id": "ポインター",
"items_count": 1
}
・・・
]
https://qiita.com/api/v2/tags?page=a
のようにパラメータが不正であったりした場合、エラーレスポンスは以下のようなJSONになります。この場合ステータスコードは400です。
{
"message": "Bad request",
"type": "bad_request"
}
アプリでこのタグデータとエラーレスポンスのJSONを格納するクラスをそれぞれ定義します。
// タグ
data class Tag(val followers_count: String, val icon_url: String, val id: String, val items_count: Int)
// エラー情報を格納
data class QiitaError(val message: String, val type: String)
APIのステータスコードが200以外の場合を例外として処理するための例外クラスを作成します。この例外クラスにAPIのエラー情報を格納します。
class QiitaException(val qiitaError: QiitaError) : Exception()
実際にAPIに対してリクエストを行うクラスを作成します。
Fuelのメソッドrx_responseString()が返す型はSingle<Pair<Response, Result<String, FuelError>>>ですが、
clean architecutreなどデータアクセス層を分離していくことを考え、 メソッドからはSingle<List<Tag>>を返しています。
ステータスコードが200の場合はsubscribe側のonSuccessで処理し、200以外の場合、エラーオブジェクトをsubscribe側のonErrorメソッドで処理します。このためにSingle.createとSigle.errorを使って返り値を作成します。
// データアクセス層のクラス
class Network {
fun getTags(): Single<List<Tag>> {
// 簡略化してパラメータは固定値
return "https://qiita.com/api/v2/tags".httpGet(listOf("page" to "1")).rx_responseString().flatMap { p ->
when (p.first.httpStatusCode) {
// ステータス200は成功
200 -> {
val gson = Gson()
// タグ情報のJSONをオブジェクトへ変換
Single.create<List<Tag>> { e ->
e.onSuccess(gson.fromJson(p.second.get(), object: TypeToken<List<Tag>>(){}.type))
}
}
// 200以外はエラーとする
else -> {
// エラー情報のJSONをオブジェクトへ変換し例外オブジェクトへ格納
val input = InputStreamReader(ByteArrayInputStream(p.first.data))
val gson = Gson()
val e2 = gson.fromJson(input, QiitaError::class.java)
Single.error(QiitaException(e2))
}
}
}
}
}
今回はアクティビティから呼び出します。この例では簡略化してNetworkクラスのインスタンス生成していますが、実際はDaggerでDIしたほうがいいと思います。
・・・
val network = Network()
network.getTags()
// APIへのリクエストは別スレッドで処理
.subscribeOn(Schedulers.newThread())
// APIへの通信後はメインスレッドで処理
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
// onSuccess(List<TAG> -> Unit)
res ->
Log.i(TAG, res.joinToString(","))
// 画面表示など
・・・
},
{
// onError
// onError(Throwable -> Unit)なのでisで型チェック
e ->
if (e is QiitaException) {
Log.i(TAG, e.qiitaError.toString())
// エラーダイアログ表示など
・・・
}
}
)
・・・
エラー処理はもっといいやり方があるかもしれません。何かご存知の方は教えてください。