woshidan's blog

あいとゆうきとITと、とっておきの話。

HttpClientまわりのクラスについてメモ。

AndroidでWebサーバにGETリクエストやPOSTリクエストを投げてデータを受け取る処理の勉強をしていて、メソッドの書き方などは読んだのだけど、

HttpClientまわり、どういうメソッドやクラスが登場しているかがいまいちすっきりしなかったので少し調べてメモしてみた。

かなりざっくりしているので、詳しいことは詳しいところへ行って読んでください。

どうやって書いたらいいのってコードを見たい人はこちら。

http://terurou.hateblo.jp/entry/20110702/1309541200

 

 

とりあえず、以下は単純にGETリクエストを投げる場合を主に書きます。

 

HttpClientまわりの登場人物

  • Uri.Builder
  • HttpGet/HttpPost
  • HttpClient/DefaultHttpClient
  • ResponseHandler
  • EntityUtils
  • ConnectionManager

 

Uri.Builder

見たままですが、HTTPリクエストの宛先のURIを作る。

scheme(),authority()などのメソッドを持っています、とか見ていても少し分かりにくかったので、実際メソッドによってどんな感じにURIが出来るのか、http://www.serendip.ws/archives/5107から引用します。

コード

Uri.Builder builder = new Uri.Builder();
builder.scheme("http");
builder.authority("www.serendip.ws");
builder.path("/hoge/foo");
builder.appendQueryParameter("key1", "val1");
builder.appendQueryParameter("key2", "Encodeされたテキスト");
builder.fragment("fragment(フラグメント)");

TextView tv = (TextView) findViewById(R.id.result);
tv.setText(builder.build().toString());

出力結果

http://www.serendip.ws/hoge/foo?key1=val&key2=Encode%E3%E81...("Encodeされたテキスト"がエンコーディングされたもの)#fragment((%E3%83…("フラグメント"がエンコーディングされたもの))

 

appendQueryParameter()がGETリクエストのURIを作っていくときに便利です。

fragmentメソッドURIフラグメント(リソース内の部分指定をする)を追加すると書いてあったのがよくわからなかったのですが、URIの#の後ろの部分をフラグメントというらしい。

たとえば、

Yukiの枝折: Android:UriとUri.BuilderのAPI

で説明されている例で一番簡単なものだと、

http://autority/path#a=aのa=aの部分。

 

結果の#以降の部分

#fragment(%E3%83…(フラグメントを(何に)変換してる))

とbuilder.fragment("fragment(フラグメント)”);

の部分がこれにあたってるみたい。

 

HttpGet/HttpPost

このへんはさっきURIビルダーで作ったURIとHTTPメソッド

HttpGet httpGet = new HttpGet(Uri);

という具合にむすびつけたリクエストを扱う感じ。

URIは直接

HttpGet httpGet = new HttpGet("http://express.heartrails.com/api/json?method=getAreas”);

という感じに直接入れてもいい。

(ちなみにこのリクエストを投げているAPIはこちらhttp://express.heartrails.com/のもの。)

 

HttpPostの場合はUri.Builderでパラメータを追加するわけにはいかないので、

HttpPost httpPost = new HttpPost(URI);

とリクエストを投げたいURIを持ったHttpPostオブジェクトをひとまず作る(後述のEntityを作った後でも前でもどっちでもいい)。

そのあとで、

送りたいデータをEntityの中に詰めて、setEntityメソッドでHttpPostオブジェクトにセットする。

 

参考にしたhttp://319ring.net/blog/archives/1667からの引用

(書いてる人は頭がよろしくないのでさらにコメントを足してある)

ArrayList params = new ArrayList();
params.add(new BasicNameValuePair("subject", "件名"));
params.add(new BasicNameValuePair("body", "本文です。こんにちは!"));
// HttpPostインスタンスを生成した後、
HttpPost httpPost = new HttpPost("http://www.example.com/ “);
// HttpPostインスタンスにsetEntityメソッドを使って先ほどの配列をセット
// (ただしエンコード変換用のメソッドUrlEncodedFormEntityを使うこと)
// パラメータを設定
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));

 

HttpClient/DefaultHttpClient

HttpClientは上までのクラスやメソッドで作ったHttpリクエストを送る役。

実際にデータの送受信を行うので、通信関係やI/O関係の例外をキャッチする必要があって、

try { … } catch () { … } の構文の中にいる。

具体的には

//httpClientはHttpClientクラスのインスタンスとする
httpClient.execute(HttpGetクラスなどのインスタンス(=HTTPリクエストの情報),ResponseHandler);

という感じで,投げたいHttpリクエストとリクエストに対するレスポンスが返って来たときの処理

を書いた関数をexecute()の中に入れて使う。

 

DefaultHttpClientはHttpClientのサブクラスで、

HttpClientのインスタンスはほとんどexecute()くらいしか出来ないが、

DefaultHttpClientの場合、createCookieStore()など、

HTTPで通信を使うときにはこれするよねーってメソッドが追加されているので、

特に理由がない場合はDefaultHttpClientのほうがよさそう。

 

そういえば、HttpURLConnection and Apache HTTP Client

だとApacheのほうが古いOS(Android 2.2以前)でいくつかバグが有るらしいと

AndroidDeveloperサイトあたりに書いてあったのだけど、

2.3以降でHttpURLConnectionのほうを使えばいいのでしょうか。

 

ResponseHandler

HttpClient/DefaultHttpClientのexecute()メソッドで送ったHTTPリクエストに対する結果が帰って来た場合の処理を書く。 

このメソッドの返り値がHttpClient.execute()の戻り値になる。

ResponseHandler() {
  @Override
  public String handleResponse(HttpResponse response)
  throws ClientProtocolException, IOException {
    return EntityUtils.toString(response.getEntity());
  }
}

HttpResponse responseはHTTPリクエストを出した結果サーバから返って来ているデータ+通信情報の固まりみたいなゆるふわな理解をしている。それで、その中には、目当てのデータのかたまりであるEntityの他に通信のステータスコード(404とか)など、通信状態などの情報が入っている(たぶん)。

なので、getEntity()メソッドでデータの固まりのEntity()だけを取り出している。

Entity()の形だと処理しにくいから、次にメモするEntityUtilsクラスのメソッドで加工してから返す。

 

あと、ResponseHandlerの<>の中身(ジェネリックの型)とhandleResponseの返り値の型は一致する必要が有る(いちおう)。

 

EntityUtils

受け取ったレスポンスから取り出したEntityをなじみのある形(String型など)に変換するときに使うクラス。

インスタンスを生成した覚えがないのに一体お前は何なんだと思ったら

Static helpers gor dealing with entities.と書いてありました。申し訳ありません。

return EntityUtils.toString(response.getEntity(),”UTF-8(文字コード)”);

のようにレスポンスから取り出したEntityと必要なら文字コードなど変換形式に関する情報を引数に入れて使う。

変換するオブジェクトのクラスや形式はいくつか選べるけれど、とりあえず、自分の場合はJSON文字列を受け取りたかったので、これでよし。

後は受け取った側のメソッドJSONオブジェクトに直したりする。

 

ConnectionManager

通信の処理なので、通信が終わったらshutdown()しないとリークが起きますよ、という話。

try {
  //HttpClient.execute()などを記述
} catch (…) {
} finally {
  //  最終的に処理が流れ着く場所
  //  このへんでhttpClient.getConnectionManager().shutdown();
  //  をしておかないと、メモリリークが起きる。
}

他にやることがたくさんあった気がするけど、とりあえず忘れる前にまとめておいた記事をアップしてすっきりしておく。