我有一个使用 ActionBar 选项卡滑动选项卡的活动,基于android 开发人员示例。
每个选项卡显示一个 Fragment,每个 Fragment(实际上是一个 SherlockFragment)通过自定义 AsyncTaskLoader 加载不同类型的远程 api 请求。
问题是,如果您在要离开的选项卡的片段(旧片段)正在加载结果时点击一个选项卡以移动 2 个选项卡/页面,则该结果将传递到您移动到的选项卡的片段(新片段)。就我而言,这会导致 ClassCastException,因为预期的结果是不兼容的类型。
在代码中,情况的要点是:
装载机:
public class FooLoader extends AsyncTaskLoader<Foo>
public class BarLoader extends AsyncTaskLoader<Bar>
片段:
public class FooFragment extends Fragment implements LoaderManager.LoaderCallbacks<Foo> {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Foo> onCreateLoader(int id, Bundle args) { return new FooLoader(); }
...
}
public class BarFragment extends Fragment implements LoaderManager.LoaderCallbacks<Bar> {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Bar> onCreateLoader(int id, Bundle args) { return new BarLoader(); }
...
}
标签管理代码如上例所示。在 Foo 和 Bar 选项卡之间有第三个选项卡(称为 Baz)。当我们在 FooFragment 在其 LoaderManager 上调用 initLoader 之后但在调用 FooFragment.onLoadFinished 之前通过点击 Bar 选项卡从 Foo 选项卡跳到 Bar 选项卡时,我们最终会在调用 BarFragment.onLoadFinished 时出现 ClassCastException:
java.lang.ClassCastException: com.example.Foo cannot be cast to com.example.Bar
at com.example.BarFragment.onLoadFinished(BarFragment.java:1)
at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427)
at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:562)
at com.example.BarFragment.onCreate(BarFragment.java:36)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1437)
...
为什么会发生这种情况,如何预防?从调试日志中可以看出,Bar 片段中正在重复使用相同的 LoaderManager(尽管 Baz 片段有自己的),但我不知道为什么会发生这种情况。
更新:在每个片段中使用不同的加载程序 ID 确实消除了崩溃(或者似乎 - 我真的不知道为什么)但我宁愿不这样做。在其中一个片段中,我实际上是动态创建 ID,并且不想假设不会发生冲突。另外,这个解决方案对我来说很奇怪——加载器 ID 应该是每个片段的本地(否则,为什么在正常情况下我可以在不同的片段中拥有具有相同 ID 的加载器?)
看来我也可以通过调用setOffscreenPageLimit(2)
ViewPager 来消除崩溃,这样当我们切换到 Bar 视图时,Foo 视图不会被丢弃。但这是一种解决方法,而不是通用解决方案。
完整代码:我创建了一个演示错误的示例应用程序。它包含一个用于强制错误的 monkeyrunner 脚本(尽管它可能不适用于所有屏幕尺寸)。