JSONデータの表示
ここまでで、一度アプリを再起動し、都市リスト画面をタップしてみてください。お天気詳細画面が表示されますが、特に何も変化がありません。画面上変化がありませんが、確実に裏でhttpアクセスを行い、お天気JSONデータを取得してきています。しかし、せっかく取得したJSONデータを表示する処理が記述されていないので、このような結果になっています。そこで、次に、その部分を記述していきます。
UIスレッドとバックグラウンド
この処理はonPostExecute()メソッドに記述しますが、なぜ、doInBackground()ではだめなのでしょうか。それは、doInBackground()とonPostExecute()とでは実行しているスレッドが違うからなのです。doInBackground()は名前の通りバックグラウンドスレッドで実行し、onPostExecute()はUIスレッドで実行されます。そして、画面への表示などの処理はUIスレッドで実行されるメソッドに記述する必要があるのです。
onPostExecute()に限らず、AsyncTaskクラスのAPIリファレンスページを参照すると、さまざまなメソッドが用意されていますが、それぞれ実行するタイミングとスレッドを理解しておく必要があります。これらを図にすると以下のようになります。
- onPreExecute():doInBackground()の前にUIスレッド上で実行される
- onPostExecute():doInBackground()の後にUIスレッド上で実行される
- onProgressUpdate():doInBackground()中にpublishProgress()メソッドを呼び出したタイミングにてUIスレッド上で実行される
コンストラクタの追加
では、onPostExecute()へソースコードを記述していきますが、その際、お天気情報を表示する画面部品をあらかじめWeatherInfoReceiverが取得しておく必要があります。これは、コンストラクタで受け取り、フィールドに格納することにします。以下のソースコードをWeatherInfoReceiverに追記してください。
private class WeatherInfoReceiver extends AsyncTask<String, String, String> { private String _cityName; private TextView _tvCityName; private TextView _tvWeatherTelop; private TextView _tvWeatherDesc; public WeatherInfoReceiver(String cityName, TextView tvCityName, TextView tvWeatherTelop, TextView tvWeatherDesc) { _cityName = cityName; _tvCityName = tvCityName; _tvWeatherTelop = tvWeatherTelop; _tvWeatherDesc = tvWeatherDesc; } ~省略~ }
コンストラクタを追記したため、このクラスをnewしているところを以下のように変更します。
protected void onCreate(Bundle savedInstanceState) { ~省略~ TextView tvCityName = (TextView) findViewById(R.id.tvCityName); TextView tvWeatherTelop = (TextView) findViewById(R.id.tvWeatherTelop); TextView tvWeatherDesc = (TextView) findViewById(R.id.tvWeatherDesc); WeatherInfoReceiver receiver = new WeatherInfoReceiver(cityName, tvCityName, tvWeatherTelop, tvWeatherDesc); receiver.execute(cityId); }
onPostExecuteへの追記
では、準備が整いましたので、onPostExecute()へ以下のソースコードを記述します。
public void onPostExecute(String result) { String desc = ""; String dateLabel = ""; String telop = ""; try { JSONObject rootJSON = new JSONObject(result); // (1) JSONObject descriptionJSON = rootJSON.getJSONObject("description"); // (2) desc = descriptionJSON.getString("text"); // (3) JSONArray forecasts = rootJSON.getJSONArray("forecasts"); // (4) JSONObject forecastNow = forecasts.getJSONObject(0); dateLabel = forecastNow.getString("dateLabel"); // (3) telop = forecastNow.getString("telop"); // (3) } catch(JSONException ex) { } _tvCityName.setText(_cityName + "の" + dateLabel + "の天気: "); // (5) _tvWeatherTelop.setText(telop); // (5) _tvWeatherDesc.setText(desc); // (5) }
ここで注意するのは、onPostExecute()の引数です。この引数resultはdoInBackground()の戻り値です。ここでは、httpアクセスで取得してきたお天気情報JSON文字列です。このJSON文字列をJSONの仕様に従って解析すればそれぞれのお天気情報が取得できます。
リスト7のtryブロック内の処理がそれに当たります。ここでは、JSONObjectクラスを使用してJSONデータを取得しています。JSONObjectは、new時に引数でJSON文字列を渡します(リスト7の(1))。その後、JSONObjectの各種メソッドを利用して、入れ子になっているJSONオブジェクトを取得したり(リスト7の(2))、JSON配列を取得したりできます(リスト7の(4))。最終的には、getString()メソッドで名前を指定することで、JSONオブジェクト直下の文字列を取得します(リスト7の(3))。
JSONから取り出したお天気情報をコンストラクタでフィールドに格納した画面部品にそれぞれ表示すれば出来上がりです(リスト7の(5))。
この状態でアプリを再起動してください。無事、お天気情報が表示されるはずです。
まとめ
今回は、Web API連携とその時に必須の考え方である非同期処理を解説しました。次回は音声ファイルといったメディア再生を扱います。