Android:升级支持库后使用 findFragmentById() 出现 NullPointerException

Android: NullPointerException using findFragmentById() after ugrading support libraries

我刚刚将一个旧的 Android 项目从 Eclipse 切换到 Android Studio,在这个过程中我从旧的支持库版本 19.0.1 更新到 22.2.0。在此之后,我在以前的工作代码中遇到了一些错误。我下载了中间的所有 SDK 并检查了错误首先出现的位置,它似乎是 21.1.0 及更高版本。我查看了那个版本的 release notes,但找不到任何感兴趣的内容。

我的错误是我在这里得到 NullPointerException。请注意,此方法是 Activity 的一部分,而不是父 Fragment.

public synchronized void onRecipesFiltered(ArrayList<Recipe> filteredRecipes)
{   
    FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
        getSupportFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

    // filteredRecipesListFragment becomes null
    filteredRecipesListFragment.onRecipesFiltered(filteredRecipes);
}

我得到的 Fragment 是一个 ListFragment,它是 XML 文件的一部分,该文件在应用程序启动时被膨胀。似乎这个 ListFragment 在 inflation 期间添加到 FragmentManager 内部 Fragment。这可能就是为什么我在获取 ActivityFragmentManager 时得到空指针的原因。因此,我的问题是:

如何在 Activity 中获取 ListFragment?是否需要保留对父 Fragment 的引用以通过其 FragmentManager 获取 ListFragment?在 FragmentPagerAdapter#getItem() 中返回 new MyFragment() 时,父 Fragment 被夸大了,没有其他地方,因此在 Activity.[=38= 中获得正确的引用有点棘手]

还有一件事需要注意:我看到一些消息告诉我应该更新到 JDK 1.7,我做到了 (OpenJDK 1.7)。我不记得我做了什么,但在 "External Libraries" 下我看到 <1.7> (/usr/lib/jvm/java-1.7.0-openjdk-amd64)。不确定这是否重要。

尝试替换:

FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
    getSupportFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

至:

 FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
    getChildFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

getChildFragmentManager() documentation

上阅读有关 getChildFragmentManager() 的更多信息

我按照 this 的回答解决了问题。似乎不太可能以一种漂亮的方式从 ViewPager 中获取 Fragments,因此它们必须在创建后作为参考保存,并在需要时获取。

我把答案的主要代码复制在这里,以防万一。主要思想是将 Fragment 保留在 SparseArray 中,以便在需要时获取它们。它们必须在 creation/before 删除后插入和删除。

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return ...;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(...); 
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

完成此操作后,我可以使用 getChildFragmentManager() 将我的方法重写为以下内容,如@Skizo 所述:

public synchronized void onRecipesFiltered(ArrayList<Recipe> filteredRecipes)
{
    MyPagerAdapter myPagerAdapter = (MyPagerAdapter) mMyViewPager.getAdapter();
    FilteredRecipesFragment filteredRecipesFragment = (FilteredRecipesFragment)
            myPagerAdapter.getRegisteredFragment(PagerConstants.PAGE_FILTER_RECIPES);

    if (filteredRecipesFragment == null)
    {
        // Fragments that has not yet been instantiated will be returned as null. Cannot do anything about it.
        // Should be fairly unlikely. See:
        // 
        return;
    }

    FilteredRecipesListFragment listFragment = (FilteredRecipesListFragment)
            filteredRecipesFragment.getChildFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);
    listFragment.onRecipesFiltered(filteredRecipes);
}