【问题标题】:LoaderCallbacks.onLoadFinished is not called when loader is resused and contains dataLoad Callbacks.onLoadFinished 在 loader 被重用并且包含数据时不会被调用
【发布时间】:2011-08-15 14:38:36
【问题描述】:

我有 1 个活动和 2 个片段。两个片段都使用自定义 AsyncTaskLoader 从 Web 服务获取一些数据,并且当我使用 Loader 时,它应该跨活动和片段重新创建保留数据。两个片段都会覆盖 onActivityCreated 方法并调用 getLoaderManager().initLoader(0, null, this) 来创建新的或重用现有的加载器。

Activity 首次创建时,默认在 FrameLayout 中添加 Fragment #1,加载数据,内部调用 LoaderCallbacks.onLoadFinished() 方法并显示结果。我有一个按钮,它在单击时用 Fragment #2 替换 Fragment #1,并且 Fragment #1 被推送到 Fragment-backstack。当用户按下 BACK 键时,它会切换回 Fragment #1。

onActivityCreated 在 Fragment #1 上再次被调用,然后显然再次调用 iniLoader()。这次数据已经存在于加载器中,我希望它再次自动调用 LoaderCallbacks.onLoadFinished 方法,因为它已经有可用的数据,如下所述:http://goo.gl/EGFJk

确保加载程序已初始化并处于活动状态。如果加载器尚不存在,则创建一个并(如果活动/片段当前已启动)启动加载器。否则,最后创建的加载器将被重新使用。 在任何一种情况下,给定的回调都与加载程序相关联,并将在加载程序状态更改时调用。 如果在调用点调用者处于启动状态,并且请求的加载器已经存在并且已经生成了它的数据,那么回调 onLoadFinished(Loader, D) 将立即被调用(在这个函数内部),所以你必须为此做好准备。

但是,即使加载器存在并且已经生成了可以交付的数据,该方法也不会被调用。


编辑 #1 用户角度的问题:

  • 用户启动活动并看到带有一些数据的 fragment1
  • 用户单击某项将第一个片段更改为另一个片段,具有不同的 数据
  • 用户点击返回键
  • 用户现在再次查看 fragment1,但没有数据。 (这意味着我需要再次从网络服务中获取它——如果可能的话,我想避免这种情况)

这是我的活动:

public class FragmentTestsActivity extends Activity implements OnClickListener {

    private Button btn1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btn1 = (Button) findViewById(R.id.btn1);
        btn1.setOnClickListener(this);

        Fragment newFragment = new Fragment1();
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.add(R.id.fragmentContainer, newFragment).commit();
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.btn1) {
            showNewFragment();
        }
    }

    public void showNewFragment() {
        // Instantiate a new fragment.
        Fragment2 newFragment = new Fragment2();

        // Add the fragment to the activity, pushing this transaction
        // on to the back stack.
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.fragmentContainer, newFragment);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.addToBackStack(null);
        ft.commit();
    }
}

我的片段#1:

public class Fragment1 extends Fragment implements LoaderCallbacks<String> {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment1, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        LoaderManager.enableDebugLogging(true);        
        getLoaderManager().initLoader(0, null, this);
    }

    private static class TestLoader extends AsyncTaskLoader<String> {

        String result;

        public TestLoader(Context context) {
            super(context);
        }

        @Override
        public String loadInBackground() {
            // Some long-running call to a webservice - replaced with a simple string to test with            
            return "FirstTimeData";
        }

        @Override
        public void deliverResult(String data) {
            result = data;

            if (isStarted()) {
                super.deliverResult(data);
            }
        }

        @Override
        protected void onStartLoading() {
            if (result != null) {
                deliverResult(result);
            }
            if (takeContentChanged() || result == null) {
                forceLoad();
            }
        }

        @Override
        protected void onStopLoading() {
            cancelLoad();
        }     
    }

    @Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        return new TestLoader(getActivity());
    }

    @Override
    public void onLoadFinished(Loader<String> loader, String result) {
        Log.d("Fragment1", "onLoadFinished: " + result);
    }

    @Override
    public void onLoaderReset(Loader<String> loader) {
        // TODO Auto-generated method stub
    }
}

任何人都知道这个问题的解决方案或我在这里做错了什么?非常感谢任何帮助。

【问题讨论】:

标签: java android android-fragments


【解决方案1】:

至少对我来说,正确的答案是将整个 Loader 初始化从 onViewCreatedonActivityCreated 移动到 onStart

之后就可以正常使用了!

【讨论】:

    【解决方案2】:

    从我的角度来看,onLoadFinished 只会在第一次被调用,因为加载已经完成,两个片段只完成一次。您可以做的是将结果存储在活动的属性中,并在第二个片段创建中检查它。

    【讨论】:

    • 我不需要在两个片段之间共享数据,它们有各自的加载器和不同的数据。问题是当用户通过片段回栈返回时,将状态保留在片段中。如果我可以从 Loader 获取现有数据,我就不需要再次询问 web 服务 - 这就是首先使用 Loader 的目的。
    • 我明白了,我以为两个 WS 调用会返回相同的数据。不知道您的问题的答案,但我认为您可以调整体系结构以缓存每个 Web 服务调用的结果,因此第二次它从内存、文件中获取结果,或者如果您要大量使用它甚至是 Sql Lite。
    • 我在这种方法之前所做的是让 StateHolder 对象包含活动中的数据并通过 onRetainNonConfigurationInstance() 传递它,但 sdk 说它已弃用,因此我应该使用带有setRetainInstance(true) 代替-但据我了解,您不能同时使用 setRetainInstance() 和 addToBackStack (这有点奇怪,因为这正是我想要的-保留片段的实例并将其添加到后台堆栈所以用户可以导航回它)
    【解决方案3】:

    更新:经过进一步调查,我发现我原来的答案是错误的。 restartLoader 也会销毁已经存在的加载器。

    不过,我解决了自己的问题。我在onCreateLoader 中创建了一个标准CursorLoader,并在onLoadFinished 中交换了我的CursorAdapter 的光标。我需要在初始化CursorAdapter 之后调用initLoader。现在从backstack返回fragment时数据还在。

    原答案:我找到了两种可能的方法来解决这个问题。

    显然,在从后台堆栈返回 Fragment 之后,在 onActivityCreated(Bundle savedInstanceState) 中第二次调用 initLoader(int id, Bundle args, LoaderCallbacks&lt;D&gt; callback) 时,永远不会调用方法 onLoadFinished(Loader&lt;String&gt; loader, String result)(如您所述)。

    但是,在initLoader 之后或代替initLoader 调用restartLoader(int id, Bundle args, LoaderCallbacks&lt;D&gt; callback) 最终会导致调用onLoadFinished。为了提高性能,我使用布尔参数来确定 Fragment 是否是新实例。然后我调用 restartLoader 只有当 Fragment 从 backstack 返回时。

    据我所知,旧数据保留在加载器中并且不会重新加载,但我不确定。 第二种可能性(尤其是在不使用 backstack 而是创建一个新的事务中的片段实例)是在片段消失之前调用destroyLoader(int id)(例如在onPause中)。

    【讨论】:

      【解决方案4】:

      我已经遇到过这个问题,我无法解释为什么会出现这个错误,但我知道那一行:

      getLoaderManager().initLoader(0, null, this);
      

      不要在我的代码中工作,所以你可以改变它:

      LoaderManager lm = getLoaderManager();
      lm.initLoader(LOADER_ID, null, this);
      

      代码启动方法:

      onCreateLoader
      

      你可以试试……

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-03
        • 2021-12-24
        • 2017-04-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多