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)です。