Hatena::ブログ(Diary)

Kazzzの日記 このページをアンテナに追加 RSSフィード

2010-10-27

[][]そんなスレッド大丈夫か?


Androidアプリケーションバックグラウンド処理をスマートに解決することができるAsyncTaskだが、気をつけることがある。
f:id:Kazzz:20101027181301p:image
これはテスト用に書いたURLから画像をダウンロードして表示するアプリケーションだが、ボタンを押下されると画像をWWWから読込んでプログレスバー更新する処理をUIスレッドを邪魔しないように、バックグラウンドで実行するためにAsyncTaskを使って書いている。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
final Button btnGo = (Button)this.findViewById(R.id.btnDownload); final EditText edtUrl = (EditText)this.findViewById(R.id.edtURL); edtUrl.setText("http://f.hatena.ne.jp/images/fotolife/K/Kazzz/20101018/20101018174127.png"); final ImageView image = (ImageView)this.findViewById(R.id.ImageView); final ProgressBar progress = (ProgressBar)this.findViewById(R.id.progressbar); btnGo.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { final AsyncTask<String, Integer, Bitmap> at = new AsyncTask<String, Integer, Bitmap>() { @Override protected Bitmap doInBackground(String... params) { return getImage(edtUrl.getText().toString(), AsyncTest.this); //画像を取ってくる(省略) } @Override public void onProgressUpdate(Integer... values) { for ( int p : values) { progress.incrementProgressBy(p); } } @Override protected void onPostExecute(Bitmap result) { image.setImageBitmap(result); } }; at.execute(edtUrl.getText().toString()); } }); }

このアプリケーションで実際にボタンを何度も押下してからDDMSパースペクティブのThreadビューを見ると、以下のようにAsyncTaskが生成したスレッドが複数見えるはずだ。

f:id:Kazzz:20101027181300j:image
私は最初これを見た時に「これがAndroidアプリケーションで最も忌むべきコンテキストのリークか」※と思ったものだが、よくよく調べてみると今回の場合はそうではなかった。

上記でリークしているように見えるAsynTaskスレッドの正体は同クラスソースコードの冒頭を見ればすぐに判る。

  • AsyncTask.java
public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";
private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10;
private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } };
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); :

本クラスでは内部で生成するスレッドの生成にThreadPoolExecutorを使用しており、これが同クラスのプールされているスレッドと処理すべきタスクリストスケジュールする訳だ。
第一パラメタCORE_POOL_SIZEは定数で5であり、スレッド数は最小5がキープされる。従って上のThreadビューで見えていた5つの残存スレッドは大丈夫だ、問題無い。(逆にスレッド上限は128となっている)

しかし、それにしても一旦成長したスレッドプールの最小プール数が5というのはいささか多い気がするのでオーバライドしたい所だが、ご覧の通りsThreadFactoryはスタティックであり、且つprivateであるためこのクラスを見本にして別なクラスを起こした方が良いだろう。


※Activity、Service等Contextの具象クラスの参照をクラスに持つことで発生する参照リークの事。Contextクラスにおけるインスタンスライフサイクルは一般のJavaアプリケーションとは違うこともあり、最も起きやすい参照リークの一つである。
Avoiding Memory Leaks | Android Developers

いのっちいのっち 2010/10/28 10:15 いちばんいいスレッドを頼む

KazzzKazzz 2010/10/28 20:11 スレッドノカタキヲトルノデス。

期待通りのノリでコメントして頂きありがとうございます。
私の日記は仕事がらみで見る方が殆どなので、誰も気づかないかなと思っていました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証