我有 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
}
}
有人知道这个问题的解决方案或我在这里做错了什么吗?任何帮助将不胜感激。
至少对我来说,正确的答案是将整个加载器初始化从onViewCreated或onActivityCreated移动到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
你可以试试...