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

Android中等待queryProductDetailsAsync完成后设置内购商品价格

解决Android内购商品价格按钮显示异常问题

核心问题分析

你遇到的按钮显示异常,本质是异步回调中存在未处理的空指针风险和UI线程安全隐患:虽然queryProductDetailsAsync的回调官方标注在主线程,但极端场景下可能出现线程偏差;同时直接取列表元素、未判空的字段调用会导致无效数据或崩溃,而Timber日志能正常输出是因为日志对"null"等无效值兼容度更高。

修改后的完整代码

class Zakupy : AppCompatActivity(), PurchasesUpdatedListener {

    private lateinit var binding: ActivityZakupyBinding
    private lateinit var billingClient: BillingClient
    private var price: String = "?"    

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityZakupyBinding.inflate(layoutInflater)
        setContentView(binding.root)
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)

        billingClient = BillingClient.newBuilder(this)
            .enablePendingPurchases()
            .setListener(this)
            .build()

        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    Timber.tag("Mik").d("BillingRespondCode-OK")

                    val productList = QueryProductDetailsParams.newBuilder()
                        .setProductList(ImmutableList.of(
                            QueryProductDetailsParams.Product.newBuilder()
                                .setProductId("add_free")
                                .setProductType(BillingClient.ProductType.INAPP)
                                .build()
                        ))
                        .build()

                    billingClient.queryProductDetailsAsync(productList) { billingResult, productDetailsList ->
                        // 1. 先校验商品列表非空
                        if (productDetailsList.isNotEmpty()) {
                            val productDetails = productDetailsList[0]
                            // 2. 安全获取购买详情,避免空指针
                            productDetails.oneTimePurchaseOfferDetails?.let { offerDetails ->
                                val formattedPrice = offerDetails.formattedPrice
                                    .filter { it.isDigit() || it == ',' || it == '.' }
                                val currencyCode = offerDetails.priceCurrencyCode
                                price = "$formattedPrice $currencyCode"
                                
                                Timber.tag("Mik").d("\ncena: $price")
                                // 3. 强制在主线程更新UI,确保线程安全
                                runOnUiThread {
                                    binding.ButtonKup.text = price
                                }
                            } ?: run {
                                Timber.tag("Mik").d("无商品购买详情")
                                runOnUiThread {
                                    binding.ButtonKup.text = "无法获取价格"
                                }
                            }
                        } else {
                            Timber.tag("Mik").d("商品列表为空")
                            runOnUiThread {
                                binding.ButtonKup.text = "商品不存在"
                            }
                        }
                    }
                } else {
                    Timber.tag("Mik").d("计费服务初始化失败: ${billingResult.debugMessage}")
                    runOnUiThread {
                        binding.ButtonKup.text = "服务异常"
                    }
                }
            }

            override fun onBillingServiceDisconnected() {
                Timber.tag("Mik").d("计费服务断开")
                runOnUiThread {
                    binding.ButtonKup.text = "服务断开"
                }
            }
        })
    }

    // 必须实现PurchasesUpdatedListener接口方法
    override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList<Purchase>?) {
        // 此处添加购买结果处理逻辑
    }
}

关键修改点

  • 全链路空判:对商品列表、购买详情字段做非空校验,避免无效数据或崩溃
  • 主线程强制更新:用runOnUiThread包裹UI赋值操作,彻底规避线程安全问题
  • 异常场景兜底:针对服务初始化失败、商品不存在等情况,给按钮设置友好提示文本
  • 代码可读性优化:用let简化空判断逻辑,避免嵌套if语句

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 13:34:50