2

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 更新コールバックを指定できるようになりました。これは私にとっては単純明快で、柔軟で、優れているように思えます。

現在のフレームワーク ソリューションはどのように優れていますか? このアプローチには大きな落とし穴がありますか? 私の知る限り、実行時のコード実行とスレッドのトポロジはまったく同じですが、コードの結合が緩いだけです (スタックにいくつかの余分なフレームがあります)。

4

2 に答える 2

2

これは、小さなプロジェクトで UI/バックグラウンド タスクを分離するための洗練されたソリューションですが、Runnables を渡す方がさらに洗練されています。AsyncTask は Thread/Handler のラッパーであるため、舞台裏で既に行われているスレッド メッセージングを倍加していることに注意してください。ここでの欠点は、AsyncTasks を再利用できるように設計する場合、実行している IO がすべてスレッドセーフであることを確認する必要があることです。これは、さまざまな AsyncTasks の間で、誰がアクティブであるか、またはどれにアクセスしているかに関する通信がないためです。資力。バックグラウンド タスクを起動するだけでなく、キューに入れる必要がある場合は、IntentService の方が適している可能性があります。

于 2012-09-13T18:49:51.947 に答える
2

目的とユースケースほど優越性の問題ではありません。AsyncTasks は通常、Activity 内でプライベート クラスとして記述される (または匿名でインラインで宣言される) ため、更新が必要なさまざまな UI 要素への Activity の参照を継承します。

AsyncTask が十分なサイズおよび/または複雑さを持ち、独自のクラスに引き出され、他のクラスで再利用できる場合は、分離を改善するために Handlers を使用するよりも優れたアイデアです。AsyncTask はそれが定義された Activity に固有の何かを達成しているため、多くの場合は必要ないだけです。単純なものの場合、対応するハンドラー コードは AsyncTask 自体よりも大きくなる可能性があります。

于 2012-09-13T18:41:08.053 に答える