ListActivityを継承したActivityクラスで、AsyncTaskで重い処理を行った後リストを更新するには(改版)

「ListActivityを継承したActivityクラスで、AsyncTaskで重い処理を行った後リストを更新するには」の記事を書きましたが、Twitterでブログを書いたとつぶやいたところ、フォロワーより

  1. AsyncTaskを継承したクラスのコンストラクタの引数にContextとListActivityの両方のthisを入れるのはなぜ?
  2. ListActivityはクリックリスナーを持っているのに、AsyncTaskを継承したクラスのコンストラクタの引数にわざわざ、OnItemLongClickListenerのthisを入れるのはなぜ?

というツッコミをいただきました。

まず、2に関してですが、自分が今開発しているアプリでは、リストの項目をタップして音声ファイルを再生して、項目をロングタップして、メニューを開いて、なんらかの処理を行うため、ActivityがOnItemLongClickListenerインターフェースを実装する必要があります。そのため、AsyncTaskを継承したクラスのコンストラクタの引数にOnItemLongClickListenerのthisを入れました。しかし、よくよく考えたら、継承するクラスや実装するインターフェースのthisを引数にする必要もありませんね。コンストラクタの引数には、ListActivityクラスを継承して、OnItemLongClickListenerインターフェースを実装したクラスのthisを渡せばよさそうです。以前は、時間がかかる処理をHandlerを使って行っていたのですが、最近、AsyncTaskを使うようになりました。しかし、AsyncTask側からUIにアクセスするのにContext型の変数しか使えないんではと思い込んでいて、ListActivityクラスを継承して、OnItemLongClickListenerインターフェースを実装したクラスの型の変数を使うのには考えが及びませんでした。(と言っても、先のブログ記事では、ListActivityやOnItemLongClickListener型の変数を使っていましたが (^^); )

そこで、前回書いたブログの内容を書き換えます。

ListActivityを継承して、OnItemLongClickListenerインターフェースを実装したクラスをFooActivityとします。

AsyncTaskを継承したクラスHogeAsyncTaskで、コンストラクタを

public HogeAsyncTask(FooActivity activity) {
    mActivity = activity;
    helper = new CreateProductHelper(mContext);
    dataDao = new DataDao(helper);
}

と定義して、FooAcitvityでは、

new HogeAsyncTask(FooActivity.this).execute();

で、AsyncTaskの処理を呼びます。そして、リスト更新は、AsyncTaskを継承したクラスのonPostExecute内で、

dataList = dataDao.select();
if (dataList != null) {
    mAdapter = new CustomAdapter(mActivity, dataList);
    mActivity.setListAdapter(mAdapter);
    ListView lv = mActivity.getListView();
    lv.setOnItemLongClickListener(mActivity);
}

で行います。前回の記事のmContextをmActivityに、mListActivityをmActivityに、mOnItemLongClickListenerをmActivityに書き換えています。これはContexがActivityのスーパークラス、AcitivityがListActivityのスーパークラスであり、mActivity型の変数のクラスがOnItemLongClickListenerを実装しているため可能です。なお、ここで書いたコードは断片コードですが、CreateProductHelperはSQLiteOpenHelperを継承したクラス、CustomAdapterはArrayAdapterを継承したクラスです。

ListActivityを継承したActivityクラスで、AsyncTaskで重い処理を行った後リストを更新するには

Androidアプリ開発において、時間のかかる処理を行うのにAsyncTaskを利用することがありますが、ListActivityで、リストの項目の削除、全削除をAsyncTaskで行い、削除後リストを更新するには一工夫が必要です。何も考えない場合、UIスレッドで、AsyncTask#executeの後、リスト更新を行ってしまいがちですが、AsyncTaskは非同期処理のため、executeを呼んだ直後にリスト更新が行われ、思うような動作になりません。

AsyncTaskでは、時間のかかる処理を終了後、onPostExecuteメソッドで終了処理を行います。このメソッド内で、リスト更新を行えばいいのですが、リスト更新処理内で、ArrayAdapterを継承したクラスのコンストラクタを呼んだり、ListActivity#setListActivity、ListView#setOnItemLongClickListenerを呼ぶには、AsyncTaskを継承したクラスに、Context、ListActivity、OnItemLongClickListener型のメンバ変数が渡されないといけません。そこで次のように実装をします。

AsyncTaskを継承したクラスHogeAsyncTaskで、コンストラクタを

public HogeAsyncTask(Context context, ListActivity listActivity,
      OnItemLongClickListener onIetmLongClickListener) {
    mContext = context;
    mListActivity = listActivity;
    mOnItemLongClickListener = onIetmLongClickListener;
    helper = new CreateProductHelper(mContext);
    dataDao = new DataDao(helper);
}

と定義して、ListActivityクラスを継承して、OnItemLongClickListenerインターフェースを実装したクラスFooAcitvityでは、

new HogeAsyncTask(FooActivity.this, FooActivity.this, FooActivity.this).execute();

で、AsyncTaskの処理を呼びます。そして、リスト更新は、AsyncTaskを継承したクラスのonPostExecute内で、

dataList = dataDao.select();
if (dataList != null) {
    mAdapter = new CustomAdapter(mContext, dataList);
    mListActivity.setListAdapter(mAdapter);
    ListView lv = mListActivity.getListView();
    lv.setOnItemLongClickListener(mOnItemLongClickListener);
}

で行います。なお、ここで書いたコードは断片コードですが、CreateProductHelperはSQLiteOpenHelperを継承したクラス、CustomAdapterはArrayAdapterを継承したクラスです。

Aug 21, 2012 追記
内容を修正したものを、「ListActivityを継承したActivityクラスで、AsyncTaskで重い処理を行った後リストを更新するには(改版)」に書きました。