当 loader 被重用并包含数据时,不会调用 LoaderCallbacks.onLoadDone



我有 1 个活动和 2 个片段。这两个片段都使用自定义的 AsyncTaskLoader 从 Web 服务获取一些数据,并且由于我使用的是加载器,它应该在活动和片段重新创建中保留数据。这两个片段都覆盖了onActivityCreated方法,并调用getLoaderManager((.initLoader(0,null,this(,它可以创建一个新的加载器或重用现有的加载器。

首次创建活动时,默认情况下它会在 FrameLayout 中添加片段 #1,加载数据,在内部调用 LoaderCallbacks.onLoadDone(( 方法并显示结果。我有一个按钮,可以在单击时将片段 #1 替换为片段 #2,并将片段 #1 推送到片段后退堆栈。当用户点击 BACK 键时,它会切换回片段 #1。

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

确保加载程序已初始化并处于活动状态。如果加载程序尚不存在,则创建一个加载程序并(如果活动/片段当前已启动(启动加载程序。否则,将重用上次创建的加载程序。 在任一情况下,给定的回调都与加载程序相关联,并将在加载程序状态更改时调用。如果在调用时调用方处于启动状态,并且请求的加载器已经存在并生成了其数据,则回调 onLoadDone(Loader, D( 将立即调用(在此函数内部(,因此您必须为此做好准备。

但是,即使加载程序存在并且已生成准备交付的数据,也永远不会调用该方法。


编辑 #1从用户角度看问题:

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

这是我的活动:

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
    }
}

有人知道这个问题的解决方案或我在这里做错了什么吗?任何帮助将不胜感激。

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

之后它工作正常!

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

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

尽管如此,我还是解决了自己的问题。我在onCreateLoader中创建了一个标准CursorLoader,并在onLoadFinished中交换了CursorAdapter的光标。初始化CursorAdapter我需要调用initLoader。现在,当片段从后退堆栈返回时,数据仍然存在。

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

显然,当从后台堆栈返回片段后onActivityCreated(Bundle savedInstanceState)第二次调用initLoader(int id, Bundle args, LoaderCallbacks<D> callback)时,永远不会调用方法onLoadFinished(Loader<String> loader, String result)(如您所描述的(。

但是,在initLoader之后或代替restartLoader(int id, Bundle args, LoaderCallbacks<D> callback)调用最终将导致调用onLoadFinished。为了提高性能,我使用布尔参数来确定片段是否是新实例。然后,仅当从后备堆栈返回片段时,我才调用restartLoader

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

我已经

遇到了这个问题,我无法真正解释为什么会有这个错误,但我知道那行:

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

在我的代码中不起作用,所以你可以为此更改它:

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

代码启动方法:

onCreateLoader

你可以试试...

最新更新