I/O に AsyncTasks を使用するシンプルな Android アプリがあります。よくあるパターン:
- ユーザーがボタンをクリックする
- 応答として、onClick ハンドラーが AsyncTask をインスタンス化し、.execute() します。
- AsyncTask が完了したら、何らかの方法で UI を更新する必要があります
AsyncTask のドキュメントによると、UI の更新を行う正しい方法は、AsyncTask クラスで onPostExecute をオーバーライドすることです。これは、実行後に UI スレッドで呼び出されるため、ウィジェットなどに触れることができます。
ただし、onPostExecute に UI 要素への何らかのハード参照が必要であるというのは、私には間違っているように思えます。I/O タスクと UI コードを分けておきたい。代わりに、不透明なコールバックを AsyncTask に渡す必要があるのは明らかな状況です。コールバックは UI 要素への参照を保持するため、コードの分離と再利用性が維持されます。古典的なデリゲート パターン (または、おそらくリスナー、イベントなど、ここには多くのオプション)。
例として、以下のコードは私には間違っているようです。
class QueryJobsDBTask extends AsyncTask<Void, Void, ArrayList<ContentValues>> {
@Override
protected void onPostExecute(ArrayList<ContentValues> freshJobsData) {
someList.clear();
someList.addAll(freshJobsData);
// BUG why does my DB query class hold UI references?
someAdapter.notifyDataSetChanged();
}
いくつかの調査の結果、ここでデリゲート パターンを実現するには、Handler クラスが最も簡単で軽量な方法のようです。I/O 用に再利用可能な AsyncTasks を記述し、Handler インスタンスを介してインスタンスごとにコンテキスト UI 更新コールバックを指定できます。だから私はこの新しいハンドラ対応の基本クラスを実装しました
public abstract class HandlerAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private Handler preExecuteHandler, postExecuteHandler;
public void setPreExecuteHandler(Handler preExecuteHandler) {
this.preExecuteHandler = preExecuteHandler;
}
public void setPostExecuteHandler(Handler postExecuteHandler) {
this.postExecuteHandler = postExecuteHandler;
}
@Override
protected void onPreExecute() {
if (preExecuteHandler != null) {
preExecuteHandler.sendMessage(Message.obtain());
}
}
@Override
protected void onPostExecute(Result result) {
if (postExecuteHandler != null) {
Message msg = Message.obtain();
msg.obj = result;
postExecuteHandler.sendMessage(msg);
}
}
}
これで、すべての I/O タスクが UI から適切に分割され、必要に応じて Handler インスタンスを介して単純な UI 更新コールバックを指定できるようになりました。これは私にとっては単純明快で、柔軟で、優れているように思えます。
現在のフレームワーク ソリューションはどのように優れていますか? このアプローチには大きな落とし穴がありますか? 私の知る限り、実行時のコード実行とスレッドのトポロジはまったく同じですが、コードの結合が緩いだけです (スタックにいくつかの余分なフレームがあります)。