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
相关产品推荐
相关产品推荐

