从自定义视图启动AsyncTask的最佳实践

时间:2014-11-13 16:02:37

标签: android android-fragments android-asynctask

产生耗时的计算线程是很常见的。稍后,我们需要使用计算结果更新ActivityFragment

我一直遵循以下准则。到目前为止,它对我很有用。

AsyncTask需要onPostExecute UI Fragment

  1. 使用setRetainInstance(true)无UI片段。
  2. 使用setTargetFragmentgetTargetFragment技术
  3. 请参阅https://stackoverflow.com/a/12303649/72437
  4. AsyncTask需要onPostExecute UI Activity

    1. 使用setRetainInstance(true)无UI片段。
    2. 使用onAttachonDetach存储对Activity的引用。谷歌似乎不鼓励使用getActivityhttp://developer.android.com/guide/components/fragments.html
    3. 请参阅https://stackoverflow.com/a/16305029/72437
    4. 但是,从View派生的类的情况怎么样?我计划从自定义AsyncTask启动View。但是,我怎样才能onPostExecute回到View

      我问的原因是,在我的自定义视图中,某些触摸事件会触发它使用新的位图重绘自身。生成新位图非常耗时。因此,我计划启动AsyncTask,生成这样的位图,然后传回自定义View。但是,配置更改可能会导致重新创建自定义View。因此,我需要确保我的AsyncTask在onPostExecute期间可以有正确的View引用。

2 个答案:

答案 0 :(得分:3)

假设您仅使用AsyncTask进行与绘图相关的操作(否则您应该重新审视您的逻辑 - 正如评论所示),您可以直接在自定义{{AsyncTask中创建View 1}} class:

class MyView extends View {

    private MyAsyncTask currentTask = null;

    // View details

    @Override
    public void onAttachedToWindow() {
        currentTask = new MyAsyncTask(this);
        currentTask.execute();
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (currentTask != null) {
            currentTask.cancel(true);
            currentTask = null;
        }
    }

    static class MyAsyncTask extends AsyncTask<Void, Void, Bitmap> {

        private WeakReference<MyView> viewRef;

        MyAsyncTask(MyView view) {
            viewRef = new WeakReference<>(view);
        }

        // background task implementation

        @Override
        public void onPostExecute(Bitmap bitmap) {
            MyView view = viewRef.get();
            if (view == null) {
                return;
            }

            // you now can safely update your view - we're on UI thread
        }

    }

}

这是如何安全实施的样子。它有一些缺点和重要的部分:

  • 您的AsyncTask在任何时候都不应该强烈引用View(这就是为什么将类声明为static并将WeakReference保持为View的原因})
  • 当您对AsyncTask的结果不感兴趣时​​ - 取消它
  • 这个实现只会丢弃被取消的AsyncTask可能有用的结果。如果这是问题所在 - 我建议您完全从AsyncTask删除View并搜索其他解决方案(单独ExecutorHandlerThread)。

同样的onPostExecute AsyncTask将从启动它的同一个looper线程中调用(在你的情况下是主线程,所以如果从{{{}开始它并不重要1}}或Activity,或者其他任何地方,一切都取决于管理这些任务的难度。)

答案 1 :(得分:0)

我会向您提供一个大致的想法,即您可以在任何需要的地方继续申请(包括ThreadThreadExecutor(而不是仅仅依赖AsyncTask);

您可以直接使用库来处理events,其中事件在公共“总线”中调度,任何想要的类都可以注册到总线并收听这些事件:

为此,我会将你引用给Otto,它很好用且功能非常强大:http://square.github.io/otto/

或者您可以使用LocalBroadcastManager https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html自行实现,如下所示:

LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context)&lt;那是为了得到它。

onEventComplete:(发生在您的主题或AsyncTask上

Intent i = new Intent("DOWNLOAD_COMPLETE");
// ps. feel free to attach extras to the intent, in order to pass data back to your activity/fragment/view
lbm.sendBroadcast(i); 

然后在您的活动/片段/视图中创建一个接收器

BroadcastReceiver receiver = new BroadcastReceiver(){
    @Override
    public void onReceive (Context context, Intent intent){
       // feel free to read the extras from the intent with data here and update your view
    }
};

<强> onStartListening:

lbm.registerReceiver(receiver, new IntentFilter("DOWNLOAD_COMPLETE"));

<强> onStopListening:

lbm.unregisterReceiver(receiver);

然后你必须onStart / onStoponResume / onPauseonAttachedToWindow / {{期间开始和停止聆听1}}(对于观点)