AndroidによるHTTPアクセス
プロジェクトの準備ができたところで、まず、HTTPアクセスのコードを順に記述していきましょう。
asyncExecute()でURLを受け取れるように改造
まず、リスト2の(1)で生成したURLを利用して非同期でWeb APIにアクセスするということは、asyncExecute()メソッドは、このURL文字列を受け取れるようにしなければなりません。その改造を行いましょう。これは、リスト4の太字のようになります。
private class ListItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(…) {
〜省略〜
String url = WEATHERINFO_URL + "&q=" + q + "&appid=" + APP_ID;
asyncExecute(url); // (1)
}
}
@UiThread
public void asyncExecute(final String url) { // (2)
Looper mainLooper = Looper.getMainLooper();
Handler handler = HandlerCompat.createAsync(mainLooper);
BackgroundTask backgroundTask = new BackgroundTask(handler, url); // (3)
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(backgroundTask);
}
リスト4の(1)のように、あらかじめ生成しておいたurlを引数として渡します。
次に、この引数を受け取れるように、asyncExecute()の引数としてURL文字列を定義します(リスト4の(2))。その際、前回紹介したように、スレッドセーフとするためにfinalを付与します。
さらに、実際に非同期処理を行うのはBackgroundTaskクラスであるため、asyncExecute()内でそのBackgroundTaskをnewする際に、引数として受け取ったurlをそのまま渡しています。それが、リスト4の(3)です。
BackgroundTaskでurlを扱えるようにする
ただし、このままではコンパイルエラーとなります。BackgroundTaskクラスでも、urlを受け取って扱えるように改造しましょう。これは、リスト5のようになります。
private class BackgroundTask implements Runnable {
private final Handler _handler;
private final String _url; // (1)
public BackgroundTask(Handler handler, String url) { // (2)
_handler = handler;
_url = url; // (3)
}
〜省略〜
}
コード内容としては特に問題ないでしょう。コンストラクタでurlを受け取れるように引数をひとつ増やし(リスト5の(2))、それをフィールドに格納しています(リスト5(3))。注意点があるとすれば、リスト5の(1)のように、フィールドで定義するurlに対しては、finalを記述してスレッドセーフにする点です。
HTTPアクセスコード
さて、準備が整いました。いよいよ、HTTPアクセスコードをBackgroundTaskのrun()メソッド内に記述していきましょう。これは、リスト6の内容になります。
public void run() {
HttpURLConnection con = null; // (a)
InputStream is = null; // (b)
String result = ""; // (A)
try {
URL url = new URL(_url); // (1)
con = (HttpURLConnection) url.openConnection(); // (2)
con.setRequestMethod("GET"); // (3)
con.connect(); // (4)
is = con.getInputStream(); // (5)
result = is2String(is); // (B)
}
catch(MalformedURLException ex) {
Log.e(DEBUG_TAG, "URL変換失敗", ex);
}
catch(IOException ex) {
Log.e(DEBUG_TAG, "通信失敗", ex);
}
finally {
if(con != null) { // (c)
con.disconnect(); // (6)
}
if(is != null) { // (d)
try {
is.close();
}
catch(IOException ex) {
Log.e(DEBUG_TAG, "InputStream解放失敗", ex);
}
}
}
PostExecutor postExecutor = new PostExecutor(result); // (C)
_handler.post(postExecutor);
}
Androidで、というより、JavaでHTTPアクセス、特に、GETアクセスを行うソースコードパターンは、以下の6ステップです。
1. URL文字列からURLオブジェクトを生成する。
リスト6の(1)が該当します。これは、接続先URL文字列をもとにURLクラスをnewします。リスト6の(1)では、リスト5においてフィールドで保持するようにした_urlをもとにnewしています。これで、Open WeatherのWeb APIサービスに接続できるようになります。
2. URLオブジェクトからHttpURLConnectionオブジェクトを取得する。
リスト6の(2)が該当します。URLオブジェクトからHttpURLConnectionオブジェクトを取得するには、openConnection()メソッドを実行します。ただし、この戻り値は、URLConnectionですので、HttpURLConnectionにキャストします。
3. HTTPメソッドをGETに指定する。
リスト6の(3)が該当します。これは、HttpURLConnectionのsetRequestMethod()を利用し、引数として「GET」を渡します。
4. GET接続を行う。
リスト6の(4)が該当します。これは、HttpURLConnectionのconnect()を利用します。このメソッドを実行したとき、接続を行うだけでなく、レスポンスデータの取得まで行っています。そのため、HttpURLConnectionオブジェクト内部には、レスポンスデータが格納されています。
5. レスポンスデータを取得する。
リスト6の(5)が該当します。4で説明したように、HTTPアクセス(接続)終了後には、レスポンスデータが格納されていますので、HttpURLConnectionのgetInputStream()メソッドを実行してレスポンスデータを取得します。このメソッド名の通り、戻り値は、InputStreamオブジェクトになる点に注意してください。
6. HttpURLConnectionオブジェクトを解放する。
リスト6の(6)が該当します。このHttpURLConnectionオブジェクトの解放には、disconnect()メソッドを使います。ただし、HTTPアクセスには例外発生がつきものです。その場合でも確実に解放処理が行われるように、finallyブロックでdisconnect()を実行します。そのため、あらかじめ解放対象であるHttpURLConnectionオブジェクトをtryブロックの外側で宣言し(リスト6の(a))、その解放対象のHttpURLConnectionオブジェクトがnullかどうかをチェックした上でdisconnect()を実行します(リスト6の(c))。
同様の考え方が、レスポンスデータを表すInputStreamオブジェクトにも当てはまりますので、リスト6の(b)で宣言を行い、finallyブロックの(d)でnullチェックの上、解放処理を行っています。
このようにして取得したJSONデータを表すInputStreamオブジェクトを文字列に変換しているのがリスト6の(B)です。その際、あらかじめスケルトンプロジェクトに記述したis2String()メソッドが活躍します。戻り値であるresultは、目的のJSON文字列です。ただし、これもtryブロックの中の処理ですので、リスト6の(A)であらかじめ変数宣言をしておきます。
最終的に、このJSON文字列であるresultをUIスレッド上で解析を行い、画面に表示させる必要があります。そのため、UIスレッドで処理を行う、PostExecutorをnewする際に渡すようにしています。それが、リスト6の(C)です。
