【问题标题】:Execute AsyncTask in a headless Fragment在无头片段中执行 AsyncTask
【发布时间】:2016-05-16 08:26:00
【问题描述】:

我读过在Activity 中执行AsyncTask 的缺点是任务不能很好地应对配置更改,因为AsyncTask 的生命周期与Activity 生命周期无关。因此,如果任务应该更新 Activity 的 UI,但同时 Activity 由于配置更改而被销毁,则 AsyncTask 不知道新创建的 Activity 的任何内容,因此它没有机会更新它。这也可能导致内存泄漏。

解决方法建议它使用无头FragmentFragment 没有 UI),只执行AsyncTask,因为它可以在配置更改后继续存在。不过,我不明白这怎么可能是一种可行的方法,因为Activity 及其观点在任何情况下都被破坏了。示例:

  public MyFragment extends Fragment {
        private SomeAsync myAsync = new SomeAsync();
        private WeakReference<View> someActivityView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true);
        }

        public void setActivityView(View View) {
            someActivityView = new WeakReference<View>(view);
        } 

        ...

        class SomeAsync extends AsyncTask<Void, Integer, Object> {

        protected Object doInBackground(Void... args) {
         ...
        }

        protected void onPostExecute(Object result) {
             doUpdate(someActivityView, result);
         }

         }
       }

someActivityView 是属于ActivityView,它被设置为要更新的Fragment。 我认为someActivityView 不会在配置更改的情况下保留,而Activity 会再次创建,因此在这种情况下使用无头Fragment 有什么好处?

【问题讨论】:

  • 您不应该直接更新活动视图。创建一个回调并在活动中实现它。现在在片段中使用 getActivity() 并将其转换为回调并使用它。

标签: android android-fragments android-activity android-asynctask


【解决方案1】:

是的,你读到的关于 AsyncTask 的内容是正确的,是的 viewLess 片段是一种解决方法。

对片段做某事的总体思路是,它可以访问当前活动的活动。这样,您应该只存储此视图的 id 并在需要时在活动中找到它,而不是使用 public void setActivityView(View View) { 存储实际视图。

我将复制您的代码并对其进行一些更改以显示结果。但是还有其他方法可以做到这一点。最后我会简单地谈谈它们。

  public MyFragment extends Fragment {
        private SomeAsync myAsync = new SomeAsync();
        private int viewId = -1;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true);

            // restore the view id
            if(savedInstanceState != null && savedInstanceState.containsKey("myViewId")){
                  viewId = savedInstanceState.getInt("myViewId");
            }
        }

        @Override
        public void onSaveInstanceState (Bundle outState) {
             super.onSaveInstanceState(outState);
             // save the view ID
             if(viewId > 0) {
                outState.putInt("myViewId", viewId);
             }
        }

        public void setActivityView(View View) {
            viewId = view.getId();
        } 

        ...

        class SomeAsync extends AsyncTask<Void, Integer, Object> {

        protected Object doInBackground(Void... args) {
         ...
        }

        protected void onPostExecute(Object result) {
             Activity activity = getActivity();
             if(viewId > 0 && activity != null) {
                 View view = activity.findViewById(viewId);
                 doUpdate(view, result);
             }
         }

         }
       }

是的,有必要保存 ID,因为还有其他原因可能会终止您的活动(例如,进入后台并且系统内存不足);

替代方案:

  1. 将视图ID直接放入片段setArguments(bundle)中。
  2. 使用接收AsyncTask 的方法使活动实现接口。然后onPostExecute你调用getActivity,转换到这个接口,直接把结果传给activity。
  3. (我最喜欢这个替代方案,因为它最干净):不要使用 AsyncTask 并且不要将处理结果与 UI 组件绑定在一起。创建具有数据、处理和 UI 分离的适当结构。 UI 元素只能根据对数据元素的订阅进行更新。有几种模式可以帮助区分责任。 MVP、MVC、MVVM 等。选择一个。

【讨论】:

  • 如果我使用 AsyncTask,前两个选项中的一个是必不可少的。我不认为第三种选择是我问题的整体解决方案。即使视图由单独的类管理,也应该实现回调来通知它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-24
  • 2012-01-07
  • 1970-01-01
  • 2014-04-20
  • 1970-01-01
  • 2016-01-07
  • 1970-01-01
相关资源
最近更新 更多