android - 从 FragmentManager 检索的 Fragment 有时会将其 RecyclerView Adapter 设置为 null
问题描述
在我的应用程序中,我有一个Fragment
负责显示新闻项目列表的应用程序。它需要一个String
参数来确定从哪个 url 提取数据。
我Fragment
用这个代码设置:
private void setFragment(String pageToLoad, NewsFeedFragment newsFeedFragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if(newsFeedFragment == null) {
transaction.replace(R.id.container, NewsFeedFragment.newInstance(pageToLoad), pageToLoad);
}
else {
transaction.replace(R.id.container, newsFeedFragment, pageToLoad);
}
mPageToLoad = pageToLoad;
}
在我的父母Activity
中,我跟踪当前正在查看哪个“页面”:
protected void onSaveInstanceState(@NonNull Bundle outState) {
if(mPageToLoad != null) {
outState.putString("pageToLoad", mPageToLoad);
}
super.onSaveInstanceState(outState);
}
在我的父Activity
onCreate
方法中,我检查是否已创建 NewsFeedFragment 的实例并将其添加到FragmentManager
如下:
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
if (savedInstanceState.containsKey("pageToLoad")) {
String pageToLoad = savedInstanceState.getString("pageToLoad");
if(pageToLoad != null) {
NewsFeedFragment newsFeedFragment = (NewsFeedFragment) getSupportFragmentManager().findFragmentByTag(pageToLoad);
if(newsFeedFragment != null) {
setFragment(pageToLoad, newsFeedFragment);
}
else {
setFragment(pageToLoad, null);
}
}
}
}
}
这在 99% 的情况下运行良好,应用程序正确恢复并显示NewsFeedFragment
添加的最后一个实例。但是,我有一个问题似乎是随机发生的RecyclerView
Adapter
,NewsFeedFragment
有时null
在从FragmentManager
使用该findFragmentByTag(pageToLoad)
方法检索片段时出现。
在NewsFeedFragment
是RecyclerView
Adapter
一个class
变量:
public NewsPageAdapter mNewsPageAdapter;
NewsFeedFragment的onActivityCreated
方法如下:
public void onActivityCreated(Bundle savedInstanceState) {
if(mNewsPageAdapter == null) {
Log.i(TAG, "mNewsPageAdapter is null"); // This is logged when issue occurs
}
if(savedInstanceState == null || mNewsPageAdapter == null) {
new LoadFirstPageTask().execute(); // Fetches news items from web service, creates mNewsPageAdapter, and then calls setupRecyclerView() method
}
else {
setupRecyclerView(savedInstanceState);
}
}
最后,这是 NewsFeedFragment setupRecyclerView 方法:
private void setupRecyclerView(Bundle savedInstanceState) {
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mNewsPageAdapter);
}
根据我所描述的内容,任何人都可以提供任何见解,说明为什么NewsPageAdapter
有时从 Thenull
中Fragment
检索FragmentManager
?
谢谢
解决方案
好的,所以你这样做new LoadFirstPageTask().execute();
了,我猜这最终会调用 setupRecyclerView?
我认为您在这里使解决方案复杂化了。
所有这些条件、业务逻辑和决策都在一个可变组件(片段)中做出,其生命周期非常复杂,并不总是您所期望的;您还将异步数据的维护与此结构相结合,这会产生难以确定和追踪的意外副作用。
与为所述适配器获取、处理和生成数据相比,创建适配器“便宜”。
您似乎没有在任何地方提及 ViewModel,您使用的是 viewModel 吗?或者任何其他类型的模式,比如 Presenter、Interactors、useCases?
AsyncTasks
也已弃用,虽然我不主张每次弃用类时都跑到“重构”山上,但我认为如果你将 AsyncTask 抽象为协程,你可以获得更好、更稳定、可测试和可读的解决方案(例如,所有这些都由您的 ViewModel 管理)。
换句话说,您的 Fragment 和 Activity 不应该处理有关“我是否需要加载此数据”的逻辑;这是别人的责任。
关于你的代码。
好的,既然我已经对您的工作方式进行了咆哮,让我们更深入地研究您现有的 Java 代码。
当使用 findFragmentByTag(pageToLoad) 方法从 FragmentManager 检索 Fragment 时,NewsFeedFragment 中的 RecyclerView Adapter 有时为 null。
每当我们在崩溃中看到“有时”时,第一个嫌疑人应该是计时/线程。同步代码可能会失败,但它通常比异步代码更可预测几个数量级。
如果它“有时”为空,则负责更改此行为的任务并不总是在需要时准备好;或此任务运行所需的条件,在检查它时并不总是您所期望的,依此类推。
首先将您的想法重新架构成一个单独的组件。
让 Fragment 尽快创建它的适配器(可以是空的数据),无论是否有数据。
让片段向另一个组件请求数据。当数据可用时,将其发送到 Fragment,Fragment 将依次将其设置在适配器中。如果数据在您请求时已经存在(因为您“缓存”了它),您就不必等待。
我还将“最后查看的页面”存储在同一组件中,因此您无需保存状态并将其传递给片段。相反,片段要求“当前数据”并且组件已经知道它是什么。
总而言之,将您的所有部分放在一起有点困难,因为我们,读者,没有所有的代码,也没有您的要求导致您找到这个解决方案。
推荐阅读
- clang - 使用 Clang/Ninja 产生所有可能的错误 / 继续使用 Ninja / Ninja 相当于 make -k
- linq - LINQ 通过选择新加入组
- sshd - sshd 按地址和 IP 限制组
- ios - iOS:通用链接仅适用于 Safari
- php - 使用简码添加产品时,Woocommerce 变体订阅未显示在购物篮中
- android - 将 YouTubePlayerSupportFragment 片段添加到片段事务时类型不匹配
- c# - 在网站上创建文本并检索它?C#
- css - 如何使引导徽章的颜色略有不同?
- rust - 如何在类似函数的过程宏中计算类型的实例并返回它?
- reactjs - 将路由器反应到动态页面并返回到 useEffect 中的先前状态