You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用notifyDataSetChanged后BillingClient回调阻塞,RecyclerView无显示

问题分析与解决方案

核心原因1:回调线程非主线程

Google Play Billing Library 4.x+版本中,queryProductDetailsAsync的回调运行在后台线程,而notifyDataSetChanged()属于UI操作,必须在主线程执行。直接在回调中调用会导致UI更新被延迟,直到RecyclerView触发重绘(比如滚动),甚至引发线程阻塞。

解决方案:切换到主线程更新UI

通过Activity.runOnUiThread将UI更新操作切到主线程:

billingClient.queryProductDetailsAsync(
        params,
        new ProductDetailsResponseListener() {
            public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                // 切换至主线程执行UI更新逻辑
                getActivity().runOnUiThread(() -> {
                    inventoryAsyncSub.addAll(productDetailsList);
                    subscriptionAdapter.notifyDataSetChanged();
                });
            }
        }
);

核心原因2:列表线程不安全

如果inventoryAsyncSub是普通ArrayList,后台线程执行addAll时,主线程的Adapter同时读取会引发并发修改问题,导致数据不同步,RecyclerView无法正常渲染。

解决方案:保证线程安全

  • 推荐方案:将数据添加操作和UI更新一起移到主线程执行(数据与UI更新逻辑统一,避免并发问题)
  • 备选方案:使用CopyOnWriteArrayList作为inventoryAsyncSub的实现类,天生支持线程安全的读写操作

核心原因3:静态ClickListener引发内存泄漏

Adapter中的static ClickListener clickListener持有Activity/Fragment引用,Fragment退出后,静态引用无法被GC回收,导致Activity实例无法释放,进而引发应用卡住、无响应。

解决方案:移除静态引用,改用非静态监听

修改Adapter的监听实现:

// 移除静态变量
private ClickListener clickListener;
private Context context;
private List<ProductDetails> inventoryAsyncSub;

// 构造方法调整,直接传入监听,移除Activity参数
public SubscriptionAdapter(
        Context context,
        List<ProductDetails> inventoryAsyncSub,
        ClickListener clickListener
) {
    this.context = context;
    this.inventoryAsyncSub = inventoryAsyncSub;
    this.clickListener = clickListener;
}

// ViewHolder中的点击逻辑
@Override
public void onClick(View v) {
    if (clickListener != null) {
        clickListener.onItemClick(getAdapterPosition(), v);
    }
}

// 移除原有setOnItemClickListener方法

Fragment中创建Adapter时直接传入监听:

SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(
        getContext(),
        inventoryAsyncSub,
        (position, view) -> {
            // 处理订阅项点击逻辑
        }
);

额外优化点

  1. ViewHolder性能优化:将findViewById移到ViewHolder构造方法中,避免每次绑定视图重复查找:
public Viewholder(@NonNull View itemView) {
    super(itemView);
    name = itemView.findViewById(R.id.submition_name);
    description = itemView.findViewById(R.id.submition_description);
    pricesBox = itemView.findViewById(R.id.purchase_btn);
    // 提前初始化price控件
    price = pricesBox.findViewById(R.id.item_price);
    itemView.setOnClickListener(this);
}

@Override
public void onBindViewHolder(@NonNull SubscriptionAdapter.Viewholder holder, int position) {
     ProductDetails subScr = inventoryAsyncSub.get(position);
     PurchaseHandlerTools.setItemTitle((Activity) context, holder.name, subScr.getTitle());
     holder.description.setText(subScr.getDescription());
     // 增加空指针判断,避免subscriptionOfferDetails为空时崩溃
     if (!subScr.getSubscriptionOfferDetails().isEmpty()) {
         holder.price.setText(subScr.getSubscriptionOfferDetails().get(0)
                 .getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice());
     }
}
  1. 减少不必要的引用持有:Adapter只需持有Context即可,无需持有Activity实例,降低内存泄漏风险。

内容的提问来源于stack exchange,提问作者Enrico Ancis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 15:20:22