※2012/5/14 手を出してから4ヶ月後の使い方を d:id:Nkzn:20120514:1336979844 で紹介しています。
お仕事でHTTPレスポンスを非同期で受け取ってゴニョゴニョするよくある処理をしようとしています。
で、当初はHandler使って非同期処理を実現していたのですが、どうやらJavaの実務経験が乏しい僕では、Threadを直接触るのはまだ不安が残ることが身にしみて分かって来ました。
ということで、今まで敬遠していたAsyncTaskに触ってみようとしたのですが。。。
時代は AsyncTask より AsyncTaskLoader
http://archive.guma.jp/2011/11/-asynctask-asynctaskloader.html
どうやら時代はAsyncTaskLoaderのようです。ヽ(゚∀゚)ノ
コンパチに入っているのでAndroid 1.6以上で利用できますし、どうやらお手軽らしいですし、使ってみることにしましょう。
と、ここで、残念なお知らせが。
↑以外にAsyncTaskLoaderを詳しく説明している日本語サイトがない。
というか英語も公式以外にあんまりない。
AsyncTaskLoader | Android Developers
http://developer.android.com/reference/android/content/AsyncTaskLoader.html
某ましゅいさんには↑がシンプルだろと言われてしまいましたが、ちょっとパッと見ではどこからどこまでがAsyncTaskLoaderの使い方として本質的な処理なのか見えてきませんでした。僕のどしろうとめ(´;ω;`)ブワッ
まあ見つからないものは仕方ないので
作ることにしました。
AsyncTaskの超シンプルな例として、@vvakameさんのJsonPullParserのAndroid版サンプルに目をつけてみます。
jsonpullparser-android-sample
https://github.com/vvakame/JsonPullParser/tree/master/jsonpullparser-android-sample
HTTPで入手してきたJSONを整形してListViewに表示するだけ。分かりやすい。AsyncTaskもよく分かってない僕にも、何となく挙動が掴めます。
これを参考にしつつ強引に手を入れてAsyncTaskLoader化したものが以下になります。
今回はList
MainActivity.java
package net.nkzn.android.sample.asynctaskloader; import java.util.List; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends FragmentActivity implements LoaderCallbacks<List<String>>{ ArrayAdapter<String> mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 結果表示用 ListView listView = (ListView)findViewById(R.id.listview); mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1); listView.setAdapter(mAdapter); // ローダーを初期化 Bundle args = new Bundle(1); //onCreateLoaderに渡したい値はここへ getSupportLoaderManager().initLoader(0, args, this); } /** * ローダーを初期化した際に呼ばれる。 * (今回の例だとonCreate内での初期化) */ @Override public Loader<List<String>> onCreateLoader(int id, Bundle args) { // オブジェクト作って返すだけ StringsDownloadTaskLoader loader = new StringsDownloadTaskLoader(getApplication()); loader.forceLoad(); //これでロードが始まる。AsyncTaskLoader#onStartLoading内に実装するのも可。 return loader; } /** * データ読み込みが完了したときに呼ばれる * @param data AsyncTaskLoader#loadInBackgroundで返した値 */ @Override public void onLoadFinished(Loader<List<String>> loader, List<String> data) { for(String s: data){ mAdapter.add(s); } mAdapter.notifyDataSetChanged(); } /** * よくわかんね */ @Override public void onLoaderReset(Loader<List<String>> loader) { // TODO Auto-generated method stub } }
StringsDownloadTaskLoader.java
package net.nkzn.android.sample.asynctaskloader; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; public class StringsDownloadTaskLoader extends AsyncTaskLoader<List<String>> { /** * コンストラクタ。 * super(Context)を実行できる分には、コンストラクタの引数は何でもOK。 * この例ではMainActivity#onCreateLoaderから呼び出されます。 */ public StringsDownloadTaskLoader(Context context) { super(context); } /** * バックグラウンド処理したいもの。 * パラメータ指定した型でデータを返す必要がある。 */ @Override public List<String> loadInBackground() { List<String> strings = new ArrayList<String>(); try { Thread.sleep(500); //データ読み込みの代わり strings.add("hogehoge"); strings.add("hugahuga"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return strings; } }
JPPのサンプルを参考にしたといっておきながらサラっと逐次処理を消してしまいましたが、作者自身が
@Nkzn 正直最近の端末高速すぎて逐次処理意味ない
2012-01-14 00:54:59 via twicli to @Nkzn
とか元も子もないことを言っていたので問題ないでしょう。
解説もどき
たぶんこれが最小構成に近いんじゃないかなあと思っています。
これが理解できれば@mocelさんのとか夜子ままのスライド*1も理解できるのでは。
↑は夜子ままのスライドっぽくAsyncTaskLoader#forceLoadを直接呼び出していますが、たぶん@mocelさんの真似してAsyncTaskLoader#onStartLoadingの中にforceLoadを実装したほうが幸せになれる気がする。というか、@mocelさんが記事の中で仰っている
キモはコンストラクタと loadInBackground() の辺りですかね。
その他のメソッドはわりと定型的な感じで、よくわからなくてもこう書いておけばうまく動く、程度に把握しといても当面は困らないです。
が正しいのがサンプルを作りながら実感できました。
*1:22〜33P