为何onDataChange最后执行?如何让它先于onCreateView加载数据
问题本质与解决方案
Firebase Realtime Database的读取是异步网络操作,需要等待服务器响应才能拿到数据,因此onDataChange必然会在Fragment的onCreate、onCreateView之后执行——你无法强制改变这个顺序。从你的日志也能看到:onCreateView执行时列表仍为空,1秒后onDataChange才完成数据加载。
正确的实现思路是:先初始化适配器并绑定到RecyclerView,等onDataChange拿到数据后,更新列表并通知适配器刷新UI。
修改后的完整代码
public class HomeStartFragment extends Fragment implements recyclerInterface { private Context context; private View view; private RecyclerView rv; private List<Century> centuries; private List<Article> articles; private CenturiesAdapter centuriesAdapter; private ArticlesAdapter articlesAdapter; private DatabaseReference mDataBaseCentury, mDataBaseArticle; // 计数器:用于判断两个数据源是否都加载完成 private int dataLoadCounter = 0; private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private String mParam1; private String mParam2; public static HomeStartFragment newInstance(String param1, String param2) { HomeStartFragment fragment = new HomeStartFragment(); Bundle args = new Bundle(); args.putString(ARG_PARAM1, param1); args.putString(ARG_PARAM2, param2); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("MyTaggg", "ONCREATE STARTED"); centuries = new ArrayList<>(); articles = new ArrayList<>(); mDataBaseCentury = FirebaseDatabase.getInstance("https://vitebsk-history-default-rtdb.europe-west1.firebasedatabase.app/").getReference("Century"); mDataBaseArticle = FirebaseDatabase.getInstance("https://vitebsk-history-default-rtdb.europe-west1.firebasedatabase.app/").getReference("Article"); Log.i("MyTaggg", "ListSize in onCreate = " + centuries.size()); getCenturiesFromDB(); getArticlesFromDB(); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } private void getCenturiesFromDB(){ Log.i("MyTaggg", "FUNC STARTED"); // 如果不需要实时监听数据变化,用addListenerForSingleValueEvent更高效 mDataBaseCentury.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { Log.i("MyTaggg", "ONDATACHANGE is working"); centuries.clear(); for(DataSnapshot ds : snapshot.getChildren()){ Century century = ds.getValue(Century.class); if(century != null){ // 替换assert,避免空指针崩溃 centuries.add(century); } } Log.i("MyTaggg","ListSize in func = " + centuries.size()); checkDataLoadComplete(); } @Override public void onCancelled(@NonNull DatabaseError error) { Log.e("MyTaggg", "加载Century数据失败", error.toException()); } }); } private void getArticlesFromDB(){ mDataBaseArticle.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { articles.clear(); for(DataSnapshot ds : snapshot.getChildren()){ Article article = ds.getValue(Article.class); if(article != null){ articles.add(article); } } checkDataLoadComplete(); } @Override public void onCancelled(@NonNull DatabaseError error) { Log.e("MyTaggg", "加载Article数据失败", error.toException()); } }); } // 判断两个数据源是否都加载完成,完成后刷新适配器 private void checkDataLoadComplete(){ dataLoadCounter++; if(dataLoadCounter == 2){ if(centuriesAdapter != null){ centuriesAdapter.notifyDataSetChanged(); } if(articlesAdapter != null){ articlesAdapter.notifyDataSetChanged(); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.i("MyTaggg", "ONCREATEVIEW STARTED"); view = inflater.inflate(R.layout.century_view, container, false); rv = view.findViewById(R.id.recCenturyView); // 初始化适配器,传入空列表(后续数据加载完成后刷新) centuriesAdapter = new CenturiesAdapter(getContext(), centuries, articles, this); articlesAdapter = new ArticlesAdapter(getContext(), articles, this); rv.setAdapter(centuriesAdapter); rv.setLayoutManager(new LinearLayoutManager(getContext())); Log.i("MyTaggg", "ListSize in onCreateView = " + centuries.size()); return view; } }
关键修改说明
- 异步适配:不再尝试让
onDataChange提前执行,而是在数据加载完成后调用notifyDataSetChanged()通知适配器刷新UI。 - 错误处理:补充了
onCancelled的错误日志,方便排查数据加载失败问题。 - 优化监听:用
addListenerForSingleValueEvent替代addValueEventListener,如果不需要实时监听数据变化,减少不必要的回调。 - 双数据源同步:新增计数器,确保两个数据源都加载完成后再刷新适配器,避免UI显示不完整。
内容的提问来源于stack exchange,提问作者kol9n
相关产品推荐
相关产品推荐

