HTTP通信処理
次に、loadInBackgroundメソッドのなかで行う、HTTP通信処理を実装しましょう。
HTTP通信を行う方法も非同期処理と同様に、いくつかの選択肢があります。ただWeb APIを呼び出す通信はシンプルなため、どの方法であっても大きな問題はありません。本稿では、Android SDKに含まれている、Apache HttpComponentsというライブラリのHttpClientオブジェクトを利用することにします。Apache HttpComponentsは、HTTP通信をサポートするオープンソースのクラスライブラリです。
次に示すloadInBackgroundの処理は、HTTP通信が正常に完了すれば、その応答データを文字列として返し、もし通信に失敗すれば、nullを返すものです。
@Override
public String loadInBackground() {
HttpClient httpClient = new DefaultHttpClient();
try {
String responseBody = httpClient.execute(new HttpGet(this.url), // (1)
// UTF-8でデコードするためhandleResponseをオーバーライドする (2)
new ResponseHandler<String>() {
@Override
public String handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
// レスポンスコードが、
// HttpStatus.SC_OK(HTTP 200)の場合のみ、結果を返す
if (HttpStatus.SC_OK ==
response.getStatusLine().getStatusCode()){
return EntityUtils.toString(
response.getEntity(), "UTF-8"); // (3)
}
return null;
}
});
return responseBody;
}
catch (Exception e) {
Log.e(this.getClass().getSimpleName(),e.getMessage());
}
finally {
// 通信終了時は、接続を閉じる (4)
httpClient.getConnectionManager().shutdown();
}
return null;
}
ここでは、HttpClientインターフェースを実装した、基本となるDefaultHttpClientクラスを使っています。HttpClientを利用する上でのポイントは、以下の4つです。数字は、ソースコードのコメント内の数字に対応しています。
- HTTP GETメソッドを使う
- handleResponseを利用する
- UTF-8でデコードする
- 通信終了したら接続を閉じる
Web APIでのデータを取得するには、一般的にHTTP GETメソッドを利用します。HTTP GETを実行するには、HttpGetクラスのインスタンスを、HttpClientインターフェースのexecuteメソッドの引数に指定します。
DefaultHttpClientでHTTP通信の応答を受信するには、handleResponseというオブジェクトを利用するのが基本です。応答データがテキストであれば、一連の処理をかんたんに記述することができます。ただし、受信したテキストに日本語などを含む場合は、文字コードを指定して変換する必要があります。
HTTP通信の応答時に、handleResponseメソッドがコールバックされますので、そこでEntityUtilsというオブジェクトを利用してテキストに変換します。
通信の最後では、shutdownメソッドで接続を終了します。接続を終了するまでは、複数のリクエストを発行できますが、このHttpAsyncLoaderクラスでは、1度のリクエストで終了するようにしています。
AsyncTaskLoaderの初期化と呼び出し
非同期で行うHTTP通信の実装ができましたので、今度はLoaderの初期化と、Loaderを実際に呼び出すコード、コールバックのメソッドを追加していきましょう。Loaderは、ActivityまたはFragmentから呼び出します。
@Override
protected void onCreate(Bundle savedInstanceState) {
~中略~
// Bundleにはパラメータを保存する(1)
Bundle bundle = new Bundle();
// 大阪府の天気予報を返すAPIのURLを格納する(2)
bundle.putString("url", "http://www.drk7.jp/weather/json/27.js");
// LoaderManagerの初期化(3)
getLoaderManager().initLoader(0, bundle, this);
}
@Override
public Loader<String> onCreateLoader(int id, Bundle bundle) {
// HttpAsyncLoaderの生成(4)
HttpAsyncLoader loader = new HttpAsyncLoader(this, bundle.getString("url"));
// Web APIの呼び出し(5)
loader.forceLoad();
return loader;
}
@Override
public void onLoadFinished(Loader<String> loader, String body) {
// 実行結果の書き出し(6)
if ( loader.getId() == 0 ) {
if (body != null) {
Log.d("ボディ", body);
}
}
}
@Override
public void onLoaderReset(Loader<String> loader) {
// 今回は何も処理しない
}
まずはLoaderの初期化です。Loaderの初期化は、Activityが生成されるonCreateメソッドのタイミングで行います。
コメント(1)のBundleクラスは、一時的にデータを保存するためのクラスです。連想配列のように、ある値と対応する文字列のペアで、データを格納することができます。
ここでは、Bundleオブジェクトを、Loaderに引き渡すパラメータの受け渡しに利用しています。(2)では、作成したBundleオブジェクトに、urlという名前で、Web APIのURLを設定しています。
(3)では、LoaderManagerクラスのinitLoaderメソッドを利用し、このActivityに対応づけるLoaderの初期化を行っています。なおLoaderManagerのインスタンスの取得は、直接生成するのではなく、getLoaderManagerメソッドを利用します。LoaderManagerのインスタンスは、Activityごとに1つ割り当てられ、何度getLoaderManagerを実行しても、同じLoaderManagerインスタンスが戻されます。
initLoader(int id, Bundle bundle, LoaderCallback)
id: Loaderを識別するID値(onCreateLoaderメソッドの第1引数に渡される)
bundle: パラメータ格納用(onCreateLoaderメソッドの第2引数に渡される)
LoaderCallback: LoaderCallbackインターフェースを実装したクラス
initLoaderメソッドで、指定したIDのLoaderを初期化、生成します。引数には、複数のLoaderを区別するためのID、Loaderに渡すパラメータを保存したbundleオブジェクト、コールバックを受け取るActivityを指定します。
