LinearLayout内双RecyclerView加载Firebase及Google Play内购数据时显示异常求助
我太懂这种随机bug的痛苦了——明明数据都能拿到,UI却时好时坏,连报错都没有,完全摸不着头脑。咱们一步步拆解你的问题,找出根源:
1. 致命问题:后台线程更新UI
BillingClient的queryProductDetailsAsync回调是在后台线程执行的,而Android要求所有UI操作(包括setAdapter)必须在主线程完成。你直接在这个回调里调用displayPremiumData(),相当于在后台线程更新RecyclerView,这会导致UI卡住、不生效甚至隐性崩溃。
修复代码:
在querySkuDetails的回调里,强制切换到主线程再更新UI:
billingClient.queryProductDetailsAsync( params, (billingResult, productDetailsList) -> { if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) { System.out.printf(Locale.ENGLISH, "Unable to query sku details: %d - %s%n", billingResult.getResponseCode(), billingResult.getDebugMessage()); } else { // 先清空旧数据,避免重复累加 prodDetailsList.clear(); for (ProductDetails details: productDetailsList) { prodDetailsList.add(details); } // 切换到主线程更新UI if (getActivity() != null) { getActivity().runOnUiThread(() -> displayPremiumData()); } } });
2. 数据源重复与线程安全问题
你的全局数据源(contentList、skuList、prodDetailsList)没有在每次请求前清空,导致多次调用请求时,旧数据和新数据混在一起,引发Adapter数据混乱,甚至显示异常。
修复代码:
- 在
listFilesFolders的成功回调开头,清空contentList:
.addOnSuccessListener(listResult -> { contentList.clear(); // 新增:清空旧数据 for (StorageReference folder : listResult.getPrefixes()) { parentFolder = folder.getName(); contentList.add(fileStorageRef.child(parentFolder)); } // ... 原有代码 })
- 在
listPremium的onDataChange开头,清空所有相关列表:
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { cProductsList.clear(); skuList.clear(); // 新增:清空旧SKU productList.clear(); // 新增:清空旧查询参数 // ... 原有代码 }
3. RecyclerView布局高度问题
如果你的RecyclerView设置了wrap_content,尤其是第一个用GridLayoutManager的列表,Android可能无法正确计算列表高度,导致显示为空或者只显示部分内容。
修复布局:
改用layout_weight分配两个RecyclerView的空间(假设父布局是垂直LinearLayout):
<LinearLayout android:id="@+id/mainLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/file_recycler_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/product_recycler_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout>
这样两个列表会平分父布局的高度,确保都有足够空间显示内容。
4. BillingClient连接稳定性优化
当BillingService断开连接时,你的代码没有重连逻辑,导致后续无法获取商品数据。添加重连逻辑可以提升稳定性:
修复代码:
@Override public void onBillingServiceDisconnected() { System.out.println("Billing service disconnected, attempting reconnect..."); // 延迟3秒重连,避免频繁请求 new Handler(Looper.getMainLooper()).postDelayed(() -> connectToGooglePlay(), 3000); }
记得导入android.os.Handler和android.os.Looper。
5. Fragment生命周期适配
在onCreateView里调用数据请求可能导致重复请求(比如屏幕旋转时),建议把请求移到onViewCreated,并在onDestroyView里清理资源:
修复代码:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); listFilesFolders(); checkForPremium(); } @Override public void onDestroyView() { super.onDestroyView(); // 清空Adapter避免内存泄漏 filesRv.setAdapter(null); productRv.setAdapter(null); // 清空数据源 contentList.clear(); prodDetailsList.clear(); skuList.clear(); productList.clear(); // 断开BillingClient连接 if (billingClient != null && billingClient.isReady()) { billingClient.endConnection(); } }
调试小技巧
在各个Adapter设置方法里添加日志,确认数据是否正确传递:
private void displayPremiumData() { Log.d("PremiumDebug", "准备显示 " + prodDetailsList.size() + " 个商品"); LinearLayoutManager llm2 = new LinearLayoutManager(getActivity()); productsAdapter = new ProductsAdapter3(getActivity(), billingClient, prodDetailsList, path); productRv.setLayoutManager(llm2); productRv.setAdapter(productsAdapter); Log.d("PremiumDebug", "Adapter设置完成"); }
通过日志可以快速判断是数据没拿到,还是UI更新出了问题。
按照这些步骤修改后,你的两个RecyclerView应该能稳定显示数据了。如果还有问题,可以查看Logcat里的调试日志,看看数据是否正确加载,Adapter是否正常设置。
内容的提问来源于stack exchange,提问作者sammy

