Superdry Memorandom :-p

旧「superdry memorandum :-D」です

android developers blogのAndroid’s HTTP Clientsのまとめ

Android developers blogがまた更新されてました。androidでのhttpクライアントについてです。メモがてらざっとまとめます。ネットワークについては弱く用語にも疎いのでおかしな言い回しがあったら 各リンクで原文の方を確認してみてください。

端的に言うと、Ecrair/Froyoの場合はApache HTTP Clientが、Gingerbread以降はHttpURLConnectionがおすすめのようです。

AndroidのHTTPクライアントの使い分け


HttpURLConnection と Apache HTTP Clientの2種類あってこれをどう使い分けるか。

Apache HTTP Clientについて

DefaultHttpClientAndroidHttpClientwebブラウザに最適なHTTPクライアントで幅広いAPIがあり、実装もバグが少なく安定している。しかしAPI自体が大きすぎるので互換性を維持したまま改善するのはむずかしく、Android開発チームは積極的に取り組んでいない。

HttpURLConnectionについて

HttpURLConnectionはほとんどのアプリに適した軽量のHTTPクライアントであり、もともと質が低かったがAndroid開発チームも積極的に取り組んでおり、向上している。

Froyo以前ではHttpURLConnectionはいくつかのバグがあり、特にInputStreamでclose()を呼ぶとコネクションプールがおかしくなる現象があった。これにはコネクションプーリングを無効することで避けられる。

private void disableConnectionReuseIfNecessary() {
    // HTTP コネクションの再利用はfroyo以前だとバグがある
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
        System.setProperty("http.keepAlive", "false");
    }
}

Gingerbread以降から透過なレスポンス圧縮が追加された。HttpURLConnectionは送信されるリクエストに以下のヘッダーを付与し、対応するレスポンスを扱えるようになった。

Accept-Encoding: gzip

レスポンスの圧縮をサポートしているクライアントの場合はWebサーバを設定することで効果が出る。もしレスポンス圧縮が問題となった場合は、ココを参照して無効とすること。

HTTPの「Content-Length」ヘッダーが圧縮したサイズを返すので、 無圧縮のバッファサイズを得ようとgetContentLength() を使うことは間違い。代わりに、InputStream.read()が-1を返すまでレスポンスからバイトを読むことでサイズを得ることができる。

他にも、GingerbreadでHTTPSについて改良している。HttpsURLConnectionは、多数のHTTPSホストが1つのIPアドレスを共有することを可能にする Server Name Indication (SNI) で接続することを試みている。これにより、圧縮とセッションチケットを可能としている。もしコネクションが切れた場合、自動的に(圧縮とセッションチケットなしで)再試行される。互換性を維持しつつ最新のサーバに接続する場合、HttpsURLConnectionは効率がいい。

In Ice Cream Sandwichでは、レスポンスキャッシュが追加される予定。キャッシュ機構がある場合、HTTPリクエストは下記の3つの方法のうちいずれかで処理される。

  • フルキャッシュレスポンス:ローカル・ストレージから直接提供される。ネットワーク接続を確立する必要はないので、このレスポンスはすぐに利用可能。
  • 条件付きキャッシュレスポンス:ウェブサーバによって監視されて最新であるべき。クライアントが「Give me /foo.png if it changed since yesterday」といったリクエストを贈るとサーバはコンテンツの更新または「304 Not Modified」のどちらかで応答する。もしコンテンツが変わってなければダウンロードはされない。
  • アンキャッシュレスポンス:webから提供される。このレスポンスは後にキャッシュに格納される予定。

バイス上にキャッシュするHTTPレスポンス*1を使用したい場合はリフレクションを使う。下記サンプルコードは、Ice Cream Sandwich以前のリリースでは影響ないように、Ice Cream Sandwichでレスポンスキャッシュを有効にする。

private void enableHttpResponseCache() {
    try {
        long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
        File httpCacheDir = new File(getCacheDir(), "http");
        Class.forName("android.net.http.HttpResponseCache")
            .getMethod("install", File.class, long.class)
            .invoke(null, httpCacheDir, httpCacheSize);
    } catch (Exception httpResponseCacheNotAvailable) {
    }
}

HTTPレスポンスでキャッシュヘッダをセットするように、Webサーバも設定する必要がある。

結局どうなの?

  • Ecrair/Froyoの場合、バグが少ないのでApache HTTP Clientがおすすめ。
  • Gingerbread以降はHttpURLConnectionがおすすめ。APIがシンプルでサイズが小さいためAndroid向き。
  • 新しく作るならHttpURLConnectionがベスト。透過圧縮とレスポンスキャッシングが、ネットワークの使用量を減らし通信速度を改善し、バッテリーにも優しいため。

*1:原文ではHTTPSとなっているけど…