加载器被重用并包含数据时,不会调用LoaderCallbacks.onLoadFinished

时间:2011-08-15 14:38:36

标签: java android android-fragments

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

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

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

  

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

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


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

  • 用户启动活动并看到fragment1包含一些数据
  • 用户点击将第一个片段更改为另一个片段的内容,但不同 数据
  • 用户点击BACK键
  • 用户现在再次查看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
    }
}

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

4 个答案:

答案 0 :(得分:1)

对我来说,正确的答案是将整个Loader初始化从 onViewCreated onActivityCreated 移至 onStart

之后它工作正常!

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

然而我解决了自己的问题。我在CursorLoader中创建了一个标准onCreateLoader,并在CursorAdapter中交换了onLoadFinished的光标。我需要在初始化initLoader后致电CursorAdapter 。现在,当从backstack返回片段时,数据仍然存在。

原始回答:我找到了两种解决此问题的方法。

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

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

据我所知,旧数据在加载器中持续存在并且没有重新加载,但我不确定。第二种可能性(特别是当不使用backstack而是创建一个新的时候事务中的片段实例)是在片段消失之前调用 destroyLoader(int id) (例如在onPause中)。

答案 3 :(得分:-1)

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

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

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

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

代码启动方法:

onCreateLoader
你可以试试......