android - 加载器将结果传送到错误的 fragment

标签 android android-fragments android-viewpager android-loadermanager android-loader

我有一个使用 ActionBar 选项卡滑动选项卡的 Activity ,基于 the android developer example .

每个选项卡显示一个 fragment ,每个 fragment (实际上是一个 SherlockFragment)通过自定义 AsyncTaskLoader 加载不同类型的远程 api 请求。

问题是,如果您点击一个选项卡以移动 2 个选项卡/页面,而您要离开的选项卡的 fragment (旧 fragment )正在加载结果,则该结果将传送到您移动的选项卡的 fragment 到(新 fragment )。在我的例子中,这会导致 ClassCastException,因为预期的结果是不兼容的类型。

在代码中,情况的要点是:

加载程序:

public class FooLoader extends AsyncTaskLoader<Foo>
public class BarLoader extends AsyncTaskLoader<Bar>

fragment :

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(); }
    ...
}

选项卡管理代码如the aforementioned example .在 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)
...

为什么会发生这种情况,如何预防?从调试日志看来,同一个 LoaderManager 正在 Bar fragment 中重新使用(尽管 Baz fragment 有自己的 fragment ),但我不知道为什么会发生这种情况。

更新:在每个 fragment 中使用不同的加载程序 ID 确实消除了崩溃(或者似乎 - 我真的不知道为什么)但我宁愿不这样做。在其中一个 fragment 中,我实际上动态创建了 ID,并且不想假设不会发生冲突。此外,该解决方案对我来说很奇怪 - 加载程序 ID 应该是每个 fragment 的本地(否则,为什么在正常情况下我可以在不同 fragment 中使用具有相同 ID 的加载程序?)

似乎我也可以通过在我的 ViewPager 上调用 setOffscreenPageLimit(2) 来消除崩溃,这样当我们切换到 Bar View 时 Foo View 就不会被丢弃。但这是一种变通方法,而不是通用解决方案。

完整代码:我创建了一个 example application demonstrating the error .它包括一个 monkeyrunner 脚本来强制错误(尽管它可能不适用于所有屏幕尺寸)。

最佳答案

不要使用 0 作为您的 ID。至于LoaderManager知道他们注定是同一个装载机。

您可以在 XML 资源文件中定义唯一 ID

<item type="id" name="loader_foo" />
<item type="id" name="loader_bar" />

并从 R 访问它们。

loaderManager.initLoader(R.id.loader_foo, null, new LoaderCallbacks(){});

<a href="https://developer.android.com/reference/android/app/LoaderManager.html#initLoader%28int,%20android.os.Bundle,%20android.app.LoaderManager.LoaderCallbacks%3CD%3E%29" rel="noreferrer noopener nofollow">LoaderManager</a> 的文档说“标识符的范围仅限于特定的 LoaderManager 实例”。和 LoaderMananger 的实例与 Activities 相关联.

要生成唯一 ID,您可以手动为它们分配具有较大间隔的 ID。

private static final int LOADER_FOO = 1;
private static final int LOADER_BAR = 100;

for(int i = 0; i < 10; ++i){
    loaderManager.initLoader(LOADER_FOO + i, null, new LoaderCallbacks(){});
}

关于android - 加载器将结果传送到错误的 fragment ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15490904/

相关文章:

android - View 寻呼机中的 webview 方向发生变化

android - 如何在不使用支持库的情况下实现水平滑动?

android - 恢复 fragment 时如何维护在ViewPager中 fragment 的onCreateView中创建的 View 实例?

php - Android 值未插入我的 mysql 数据库中

android - 在android中按用户的任意键后在EditText中追加 "@gmail.com"

android - 在 fragment View 之间切换

android - 有没有办法在android中创建预览 slider

android - 绘制 9 补丁不起作用 - 无法编辑图像

android - 无法在 addTextChangedListener() 中更新 View

android - 在 kotlin 中找不到 getChildFragmentManager()