なので、今回はそれを簡単に実装できるAsyncTaskクラスを使って通信処理を書き直してみる。
AsyncTaskとは
「エイシンクタスク」って読むらしい。 このクラスはexecute()メソッドを実行することで、下記の順でメソッドが呼ばれる。メソッドごとに実行スレッドが違うのが特徴メソッド名 | 意味 | 使用例 |
---|---|---|
onPreExecute | 非同期処理する前に何かする場合に使用 | ダイアログで通信中を表示 |
doInBackground | 非同期処理 | 通信処理、DB処理、データ読み込み |
onProgressUpdate | 非同期処理中にUI更新する時使用 publishProgress()でこの処理を実行 | %とかバーで処理状態表示 |
onPostExecute | 非同期処理で得られた結果を元にUI更新 | ダイアログ消去 通信処理結果表示 |
使用上の注意
めっちゃ便利そうだけど、何でもかんでもAsyncTaskで平行処理するのはよくないUIが絡むとそれだけコストがかかる。このクラスは必ずUIスレッドを経由するため、UIスレッドを絡めない処理は無難にThreadクラスで処理した方が良い
UI関係の起こりやすく、発見しにくかったバグを2つ紹介する
- AsyncTaskを複数使用するときの処理
- Android2.3以前 execute()メソッドを実行する度に並列処理されている
- Android3.0以降 execute()メソッドを実行すると現在実行中のexecute()が終了してから処理が実行される
- 処理をキャンセルするときの処理 通信中画面遷移したりすると、通信キャンセルせざるおえない場合がある
- Android2.3以前 すぐAsyncTask #onCancelled()が呼ばれる
- Android3.0以降 doInBackground()が終了してからAsyncTask #onCancelled()が呼ばれる
キャンセル処理をAsyncTask #onCancelled()にオーバライドしておき、
AsyncTask #cansel()を実行することで、上記メソッドが呼ばれキャンセルできる仕組みだが、 非同期処理中 ( doInBackground() ) にキャンセルすると下記の2パターンに分岐する
画面遷移のため非同期処理をキャンセルする場合は、onProgressUpdate()にも注意。doInBackground()は途中で終わらないので、画面遷移後の存在しないView更新でヌルポしたりして痛い目にあうw
対処方はdoInBackground()処理にisCancelled()メソッドでこまめに今キャンセル中か確認すること!
実装方法
AsyncTask<Params、Progress、Result>で表される。ジェネリクスはそれぞれ下記の意味
型 | 意味 | 使用箇所 |
---|---|---|
Params | 非同期通信の引数になる。 | execute()の引数で渡す → doInBackground()の引数 |
Progress | 非同期処理中UIスレッド更新に渡す引数 | publishProgress()の引数で渡す → onProgressUpdate()の引数 |
Result | 非同期処理結果のクラス | doInBackground()の戻り値 → onPostExecute()の引数 |
- Params : リクエストするJSONのURL → String
- Progress : 使用しない → Void
- Result : レスポンスのJSONを文字列にしたもの → String
で、あとは非同期処理中に通信処理。通信処理後にListViewの更新かける処理記述
- AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() {
- @Override
- protected String doInBackground(String... params) {
- // TODO !非同期処理はアブストラクトメソッドなので必須!
- return null;
- }
- };
最後に、Activityでexecute()を呼べば完成!※行削減の為コメ消去
- AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() {
- /**通信スレッド処理.
- * 引数のURLからリクエストし、レスポンスをJSONデータ文字列に変換
- * @param url リスエスト先
- * @return JSONデータの文字列
- */
- @Override
- protected String doInBackground(String... url) {
- return getAnimeInfo(url[0]);
- }
- /**UIスレッドの処理.
- * 引数のJSON文字列からタイトル配列を取得し、ListViewに表示
- * @param JSON文字列
- */
- @Override
- protected void onPostExecute(String json) {
- mListView.setAdapter(new ArrayAdapter<String>(
- MainActivity.this,
- android.R.layout.simple_expandable_list_item_1,
- getTitleForJSON(json)));
- }
- };
あ、マニフェストの書き忘れ注意!
- public class MainActivity extends Activity{
- ListView mListView = null;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(mListView = new ListView(this));
- task.execute("http://animemap.net/api/table/osaka.json");
- }
- AsyncTask<String, Void, String> task = new AsyncTask<String, Void, String>() {
- @Override
- protected String doInBackground(String... url) {
- return getAnimeInfo(url[0]);
- }
- @Override
- protected void onPostExecute(String json) {
- mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this,
- android.R.layout.simple_expandable_list_item_1,getTitleForJSON(json)));
- }
- };
- public static String getAnimeInfo(String url) {
- AndroidHttpClient client = AndroidHttpClient.newInstance(null);
- HttpGet get = new HttpGet(url);
- StringBuilder sb = new StringBuilder();
- try {
- HttpResponse response = client.execute(get);
- BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
- String line = null;
- while ((line = br.readLine()) != null) sb.append(line);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- client.close();
- }
- return sb.toString();
- }
- public static String[] getTitleForJSON(String jsonInfo) {
- final ArrayList<String> tmp = new ArrayList<String>();
- try {
- JSONArray json = new JSONObject(jsonInfo)
- .getJSONObject("response")
- .getJSONArray("item");
- for (int i = 0; i < json.length(); i++)
- tmp.add(((JSONObject) json.get(i)).getString("title"));
- } catch (JSONException e){
- e.printStackTrace();
- }
- return tmp.toArray(new String[0]);
- }
- }