1. 概要

このチュートリアルでは、Fuel HTTP Library を見ていきます。これは、作者の言葉を借りれば、Kotlin/Android用の最も簡単なHTTPネットワーキングライブラリです。 さらに、ライブラリはJavaでも使用できます。

ライブラリの主な機能は次のとおりです。

  • 非同期要求とブロック要求の両方の基本HTTP動詞(GET、POST、DELETEなど)のサポート
  • ファイルをダウンロードおよびアップロードする機能( multipart / form-data
  • グローバル構成を管理する可能性
  • 組み込みのオブジェクトシリアル化モジュール(Jackson、Gson、Mhosi、Forge)
  • KotlinのコルーチンモジュールとRxJava2.xのサポート
  • ルーターのデザインパターンを簡単に設定

2. 依存関係

ライブラリはさまざまなモジュールで構成されているため、必要な機能を簡単に含めることができます。次のようなものがあります。

  • RxJavaおよびKotlinのコルーチンをサポートするためのモジュール
  • AndroidおよびAndroidLiveDataアーキテクチャコンポーネントをサポートするためのモジュール
  • 使用するオブジェクトシリアル化モジュールを選択できる4つのモジュール– Gson、Jackson、Moshi、またはForge。

このチュートリアルでは、コアモジュール、コルーチンのモジュール、RxJava、およびGsonシリアル化モジュールに焦点を当てます。

<dependency>
    <groupId>com.github.kittinunf.fuel</groupId>
    <artifactId>fuel</artifactId>
    <version>${fuel.version}</version>
</dependency>
<dependency>
    <groupId>com.github.kittinunf.fuel</groupId>
    <artifactId>fuel-gson</artifactId>
    <version>${fuel.version}</version>
</dependency>
<dependency>
    <groupId>com.github.kittinunf.fuel</groupId>
    <artifactId>fuel-rxjava</artifactId>
    <version>${fuel.version}</version>
</dependency>
<dependency>
    <groupId>com.github.kittinunf.fuel</groupId>
    <artifactId>fuel-coroutines</artifactId>
    <version>${fuel.version}</version>
</dependency>

最新バージョンは、 JFrogBintrayで見つけることができます。

3. リクエストする

リクエストを行うために、Fuelは文字列拡張を提供します。さらに、代わりに、各HTTP動詞のメソッドを持つFuelクラスを使用できます。

Fuelは、PATCHを除くすべてのHTTP動詞をサポートします。 その理由は、 FuelのHttpClientが、PATCHをサポートしないjava.net.HttpUrlConnectionのラッパーであるためです。

この問題を回避するために、HttpClientはPATCHリクエストをPOSTリクエストに変換し、 X-HTTP-Method-Override:PATCH ヘッダーを追加するため、APIが受け入れるように構成されていることを確認する必要がありますデフォルトでは、このヘッダー。

Fuelの機能を説明するために、単純なHTTPリクエストおよびレスポンスサービスである httpbin.org と、テストおよびプロトタイピング用の偽のオンラインAPIであるJsonPlaceholderを使用します。

3.1. GETリクエスト

非同期モードで単純なHTTPGETリクエストの作成を開始しましょう。

"http://httpbin.org/get".httpGet().response {
  request, response, result ->
    //response handling
}

使用する httpGet() 以上私たちにトリプル

The 結果操作の結果(成功または失敗)を含む機能スタイルのデータ構造です。 再訪します結果後の段階でのデータ構造。

ブロッキングモードでリクエストを行うこともできます。

val (request, response, result) = "http://httpbin.org/get"
  .httpGet().response()

返されるパラメータは非同期バージョンと同じですが、この場合、リクエストを行ったスレッドはブロックされていることに注意してください。

また、エンコードされたURLパラメータを使用する可能性があります。

val (request, response, result) = 
  "https://jsonplaceholder.typicode.com/posts"
  .httpGet(listOf("userId" to "1")).response() 
  // resolve to https://jsonplaceholder.typicode.com/posts?userId=1

The httpGet() メソッド(および他の同様のメソッド)は、 リスト URLパラメータをエンコードします。

3.2. POSTリクエスト

httpPost()を使用するか、 Fuelクラスのpost()メソッドを使用して、GETの場合と同じ方法でPOSTリクエストを行うことができます。

"http://httpbin.org/post".httpPost().response{
  request, response, result ->
    //response handling
}
val (request, response, result) = Fuel.post("http://httpbin.org/post")
  .response()

本体がある場合は、 body()メソッドを介してJSON文字列形式で配置できます。

val bodyJson = """
  { "title" : "foo",
    "body" : "bar",
    "id" : "1"
  }
"""
val (request, response, result) = Fuel.post("https://jsonplaceholder.typicode.com/posts")
  .body(bodyJson)
  .response()

3.3. 他の動詞

GETおよびPOSTの場合と同様に、残りの動詞ごとにメソッドがあります。

Fuel.put("http://httpbin.org/put")
Fuel.delete("http://httpbin.org/delete")
Fuel.head("http://httpbin.org/get")
Fuel.patch("http://httpbin.org/patch")

Fuel.patch()は、 X-HTTP-Method-Override:PATCHヘッダーを使用してPOSTリクエストを実行することに注意してください。

4. 構成

ライブラリは、グローバル構成を管理するためのシングルトンオブジェクト FuelManager.instanceを提供します。

ベースパス、いくつかのヘッダー、および一般的なパラメーターを構成しましょう。 また、いくつかのインターセプターを構成しましょう。

4.1. BasePath

basePath 変数を使用して、すべてのリクエストに共通のパスを設定できます。

FuelManager.instance.basePath = "http://httpbin.org"
val (request, response, result) = "/get".httpGet().response()
// will perform GET http://httpbin.org/get

4.2. ヘッダー

さらに、baseHeadersマップを使用して一般的なHTTPヘッダーを管理できます。

FuelManager.instance.baseHeaders = mapOf("OS" to "Debian")

別の方法として、ローカルヘッダーを設定する場合は、リクエストで header()メソッドを使用できます。

val (request, response, result) = "/get"
  .httpGet()
  .header(mapOf("OS" to "Debian"))
  .response()

4.3. パラメータ

最後に、baseParamsリストを使用して共通パラメーターを設定することもできます。

FuelManager.instance.baseParams = listOf("foo" to "bar")

4.4. その他のオプション

FuelManager:を介して管理できるオプションは他にもたくさんあります。

  • デフォルトでnullであるキーストア
  • socketFactory は、ユーザーによって提供されるか、nullでない場合はkeystoreから派生します。
  • hostnameVerifier は、HttpsURLConnectionクラスによって提供されるものを使用するようにデフォルトで設定されています
  • requestInterceptorsおよびresponseInterceptors
  • リクエストのtimeoutおよびtimeoutRead

4.5. 要求/応答インターセプター

インターセプターに関しては、 cUrlLoggingRequestInterceptors()のような提供された要求/応答インターセプターを追加するか、を定義できます。

FuelManager.instance.addRequestInterceptor(cUrlLoggingRequestInterceptor())
FuelManager.instance.addRequestInterceptor(tokenInterceptor())
fun tokenInterceptor() = {
    next: (Request) -> Request ->
    { req: Request ->
        req.header(mapOf("Authorization" to "Bearer AbCdEf123456"))
        next(req)
    }
}

5. 応答処理

以前、操作結果(成功または失敗)を表す機能データ構造[ Result]を導入しました。

Result の操作は簡単です。これは、 ByteArray String、JSON、、または汎用Tに応答を含めることができるデータクラスです。 ] 物体:

fun response(handler: (Request, Response, Result<ByteArray, FuelError>) -> Unit)
fun responseString(handler: (Request, Response, Result<String, FuelError>) -> Unit)
fun responseJson(handler: (Request, Response, Result<Json, FuelError>) -> Unit)
fun <T> responseObject(deserializer: ResponseDeserializable<T>, 
  handler: (Request, Response, Result<T, FuelError>) -> Unit)

これを説明するために、Stringとして応答を取得しましょう。

val (request, response, result) = Fuel.post("http://httpbin.org/post")
  .responseString()
val (payload, error) = result // payload is a String

JSON形式の応答にはAndroidの依存関係が必要であることに注意してください。

<dependency>
    <groupId>com.github.kittinunf.fuel</groupId>
    <artifactId>fuel-android</artifactId>
    <version>${fuel.version}</version>
</dependency>

6. JSONシリアル化/逆シリアル化

Fuelは、4つの方法で応答の逆シリアル化の組み込みサポートを提供します。これらの方法は、ニーズと選択したJSON解析ライブラリに応じて、以下を実装する必要があります。

public fun deserialize(bytes: ByteArray): T?
public fun deserialize(inputStream: InputStream): T?
public fun deserialize(reader: Reader): T?
public fun deserialize(content: String): T?

Gsonモジュールを含めることで、オブジェクトを逆シリアル化およびシリアル化できます。

data class Post(var userId:Int,
                var id:Int,
                var title:String,
                var body:String){

    class Deserializer : ResponseDeserializable<Array<Post>> {
        override fun deserialize(content: String): Array<Post> 
          = Gson().fromJson(content, Array<Post>::class.java)
    }
}

カスタムデシリアライザーを使用してオブジェクトをデシリアライズできます。

"https://jsonplaceholder.typicode.com/posts"
  .httpGet().responseObject(Post.Deserializer()){ 
    _,_, result ->
      val postsArray = result.component1()
  }

またはresponseObject経由内部Gsonデシリアライザーを使用します。

"https://jsonplaceholder.typicode.com/posts/1"
  .httpGet().responseObject<Post> { _, _, result ->
    val post = result.component1()
  }

一方、 Gson()。toJson()を使用してシリアル化できます。

val post = Post(1, 1, "Lorem", "Lorem Ipse dolor sit amet")

val (request, response, result) 
  = Fuel.post("https://jsonplaceholder.typicode.com/posts")
    .header("Content-Type" to "application/json")
    .body(Gson().toJson(post).toString())

Content-Type を設定することが重要です。そうしないと、サーバーが別のJSONオブジェクト内でオブジェクトを受信する可能性があります。

最終的には、同様の方法で、Jackson、Moshi、またはForgeの依存関係を使用してそれを行うことができます。

7. ファイルのダウンロードとアップロード

Fuelライブラリには、ファイルのダウンロードとアップロードに必要なすべての機能が含まれています。

7.1. ダウンロード

download()メソッドを使用すると、ファイルを簡単にダウンロードして、 destination() lambda によって返されるファイルに保存できます。

Fuel.download("http://httpbin.org/bytes/32768")
  .destination { response, url -> 
    File.createTempFile("temp", ".tmp")
  }

進行状況ハンドラーを使用してファイルをダウンロードすることもできます

Fuel.download("http://httpbin.org/bytes/327680")
  .progress { readBytes, totalBytes ->
    val progress = readBytes.toFloat() / totalBytes.toFloat() 
    //...
  }

7.2. アップロード

同様に、 upload()メソッドを使用してファイルをアップロードできます。は、 source()メソッドを使用してアップロードするファイルを示します。

Fuel.upload("/upload").source { request, url ->
  File.createTempFile("temp", ".tmp") 
}

upload()はデフォルトでPOST動詞を使用することに注意してください。 別のHTTP動詞を使用する場合は、次のように指定できます。

Fuel.upload("/upload", Method.PUT).source { request, url ->
  File.createTempFile("temp", ".tmp") 
}

さらに、ファイルのリストを受け入れる sources()メソッドを使用して、複数のファイルをアップロードできます。

Fuel.upload("/post").sources { request, url ->
  listOf(
    File.createTempFile("temp1", ".tmp"),
    File.createTempFile("temp2", ".tmp")
  )
}

最後に、 InputStream:からデータのブロブをアップロードできます

Fuel.upload("/post").blob { request, url ->
  Blob("filename.png", someObject.length, { someObject.getInputStream() })
}

8. RxJavaとコルーチンのサポート

Fuelは、非同期の非ブロッキングコードを記述する2つの方法であるRxJavaとコルーチンのサポートを提供します。

RxJava は、非同期およびイベントベースのプログラムを作成するためのライブラリである ReactiveExtensionsのJavaVM実装です。

オブザーバーパターンを拡張してデータ/イベントのシーケンスをサポートし、同期、スレッドセーフ、同時データ構造を気にせずに宣言的にシーケンスを作成できる演算子を追加します。

Kotlinのコルーチンは軽量スレッドのようなものであるため、並行して実行し、相互に待機して通信することができます。最大の違いは、コルーチンが非常に安価であるということです。 何千ものそれらを作成することができ、メモリの面でほとんど支払うことができません。

8.1. RxJava

RxJava 2.xをサポートするために、Fuelは6つの拡張機能を提供します。

fun Request.rx_response(): Single<Pair<Response, Result<ByteArray, FuelError>>>
fun Request.rx_responseString(charset: Charset): Single<Pair<Response, Result<String, FuelError>>>
fun <T : Any> Request.rx_responseObject(deserializable: Deserializable<T>): 
  Single<Pair<Response, Result<T, FuelError>>>
fun Request.rx_data(): Single<Result<ByteArray, FuelError>>
fun Request.rx_string(charset: Charset): Single<Result<String, FuelError>>
fun <T : Any> Request.rx_object(deserializable: Deserializable<T>): Single<Result<T, FuelError>>

すべての異なる応答タイプをサポートするために、各メソッドは異なるを返すことに注意してください独身 >。

Request を介してより関連性の高いメソッドを呼び出すことにより、「Rx」メソッドを簡単に使用できます。

 "https://jsonplaceholder.typicode.com/posts?id=1"
  .httpGet().rx_object(Post.Deserializer()).subscribe{
    res, throwable ->
      val post = res.component1()
  }

8.2. コルーチン

コルーチンモジュールを使用すると、 Fuelは、応答をコルーチン内にラップしてその結果を処理する拡張関数を提供します。

コルーチンを使用するために、同様のAPIが利用可能になります。たとえば、 responseString() awaitStringResponse():になりました

runBlocking {
    Fuel.get("http://httpbin.org/get").awaitStringResponse()
}

また、 awaitObject()、awaitObjectResult()、またはawaitObjectResponse( ):

runBlocking {
    Fuel.get("https://jsonplaceholder.typicode.com/posts?id=1")
      .awaitObjectResult(Post.Deserializer())
}

Kotlinのコルーチンは実験的なものであることに注意してください。つまり、今後のリリースで変更される可能性があります。

9. APIルーティング

最後になりましたが、ネットワークルートを処理するために、Fuelはルーターのデザインパターンを実装することでサポートを提供します。

ルーターパターンを使用すると、 FuelRouting インターフェイスを使用してAPIの管理を一元化できます。このインターフェイスは、呼び出されたエンドポイントに応じて適切なHTTP動詞、パス、パラメーター、ヘッダーを設定するためのメソッドの組み合わせを提供します。

インターフェイスは、ルーターを構成できる5つのプロパティを定義します。

sealed class PostRoutingAPI : FuelRouting {
    class posts(val userId: String, override val body: String?): PostRoutingAPI() 
    class comments(val postId: String, override val body: String?): PostRoutingAPI()
    override val basePath = "https://jsonplaceholder.typicode.com"

    override val method: Method
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> Method.GET
                is PostRoutingAPI.comments -> Method.GET
            }
        }

    override val path: String
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> "/posts"
                is PostRoutingAPI.comments -> "/comments"
            }
        }

    override val params: List<Pair<String, Any?>>?
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> listOf("userId" to this.userId)
                is PostRoutingAPI.comments -> listOf("postId" to this.postId)
            }
        }

    override val headers: Map<String, String>?
        get() {
            return null
        }
}

使用するHTTP動詞を選択するために、 method プロパティがあります。同様に、 path プロパティをオーバーライドして、適切なパスを選択できます。

さらに、 params プロパティを使用すると、リクエストのパラメータを設定する機会があり、HTTPヘッダーを設定する必要がある場合は、関連するプロパティをオーバーライドできます。

したがって、 request()メソッドを使用して、チュートリアル全体で行ったのと同じ方法で使用します。

Fuel.request(PostRoutingAPI.posts("1",null))
  .responseObject(Post.Deserializer()) {
      request, response, result ->
        //response handling
  }
Fuel.request(PostRoutingAPI.comments("1",null))
  .responseString { request, response, result -> 
      //response handling 
  }

10. 結論

この記事では、Kotlin用のFuel HTTPライブラリと、あらゆるユースケースでのより便利な機能を紹介しました。

ライブラリは常に進化しているため、新しい機能を追跡するために、GitHubリポジトリを確認してください。

いつものように、チュートリアルで言及されているすべてのコードスニペットは、GitHubリポジトリにあります。