【问题标题】:Best practice to launch AsyncTask from custom view从自定义视图启动 AsyncTask 的最佳实践
【发布时间】:2014-11-13 16:02:36
【问题描述】:

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

一直以来,我都遵循以下准则。到目前为止,它对我来说效果很好。

AsyncTask 需要 onPostExecute UI Fragment

  1. 使用 setRetainInstance(true) 无 UI 片段。
  2. 使用setTargetFragmentgetTargetFragment 技术
  3. 请参考https://stackoverflow.com/a/12303649/72437

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

但是,从View 派生的类的大小写如何?我计划从自定义View 启动AsyncTask。但是,我怎样才能onPostExecute 回到View

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

【问题讨论】:

  • 您是否询问如何在配置更改期间保留位于 View 内部的 AsyncTask
  • 不,不,不......视图派生类不应该知道线程之类的东西甚至存在......它们也不应该能够自己获取数据,它们应该被“喂食” ...所以最佳做法是:根本不要这样做
  • @Selvin 我不会说视图永远不应该知道线程。 Romain Guy 甚至给出了一个视图示例,该视图需要在自定义视图的this Google IO talk 中清理其线程(参见 5:59)。也就是说,你是对的,在很多情况下,将线程放在活动/片段中可能是更好的编程设计。这仅取决于用例。
  • @AlexLockwood 有点像。在我的自定义视图中,某些触摸事件会触发它使用新的位图重绘自身。生成新位图非常耗时。因此,我想知道正确的方法,从自定义视图中正确管理线程。一直以来,我从 Activity & Fragment 管理线程。但是,我从来没有在自定义视图上这样做过。
  • 视图对配置更改一无所知...这是活动和片段应该担心的事情。如果必须保留视图的线程真的,则必须将该逻辑上移到活动/片段中(或在服务中生成位图等)。

标签: android android-fragments android-asynctask


【解决方案1】:

假设您仅将 AsyncTask 用于绘图相关操作(否则您应该重新审视您的逻辑 - 正如 cmets 建议的那样),您可以直接在自定义 View 类中创建 AsyncTask

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 并持有WeakReferenceView
  • 如果您不再对来自 AsyncTask 的结果感兴趣 - 取消它
  • 这个实现只会丢弃取消AsyncTask 可能有用的结果。如果这是问题所在 - 我建议完全从 View 中删除 AsyncTask 并搜索其他解决方案(单独的 ExecutorHandlerThread)。

AsyncTask 中的 onPostExecute 也将从启动它的同一个 looper 线程中调用(在你的情况下,这是主线程,所以无论你是从 Activity 还是 View 启动它都没有关系,或者任何地方否则,这完全取决于管理这些任务的难度)。

【讨论】:

    【解决方案2】:

    我会给你一个大致的想法,你可以在任何你需要的地方继续申请(包括来自 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)

    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/onDetachedFromWindow 期间开始和停止收听(用于查看)

    【讨论】:

      猜你喜欢
      • 2013-07-05
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      • 1970-01-01
      • 2014-03-20
      • 2012-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多