Android Billing Library v4调用querySkuDetailsAsync无法返回SKU详情问题求助
querySkuDetailsAsync回调不触发的问题 结合你描述的情况(v2版本正常、应用已发布、SKU均有效但回调无响应),我整理了几个升级到v4后容易踩的坑,帮你定位问题:
1. 先确认Billing服务是否真的初始化成功
你当前的代码只在初始化成功时打印日志,但有可能isBillingResultSuccessful方法的判断逻辑在v4下失效了——比如v4新增了响应码,或者你对BILLING_UNAVAILABLE这类状态的处理有误。
建议先修改onBillingSetupFinished,不管成功失败都打印完整的BillingResult信息:
@Override public void onBillingSetupFinished(@NonNull BillingResult billingResult) { int responseCode = billingResult.getResponseCode(); String debugMsg = billingResult.getDebugMessage(); activity.logInfo("Billing setup result: code=%d, msg=%s", responseCode, debugMsg); if (isBillingResultSuccessful(billingResult, CONNECTING)) { activity.logInfo("Successfully connected to billing service"); fetchSkuDetails(); } else { activity.logError("Billing setup failed: %d - %s", responseCode, debugMsg); activity.hideProgressBar(); // 避免进度条一直挂着 } }
如果发现初始化其实没成功(比如返回3即BILLING_UNAVAILABLE),那可能是设备Google Play服务版本过低,或者当前地区不支持内购。
2. 检查BillingClient的生命周期是否正确管理
Billing Library v4对客户端实例的生命周期要求更严格,如果你的BillingClient是局部变量(比如在某个方法内临时创建),或者在回调触发前被GC回收了,就会导致回调永远不执行。
确保:
BillingClient是Activity的成员变量(全局持有)- 在Activity的
onDestroy方法中调用billingClient.endConnection()释放资源 - 尽量在主线程初始化
BillingClient,避免子线程带来的线程同步问题
3. 验证SKU参数的准确性
虽然你说SKU都存在,但v4对参数的校验更严格,几个容易忽略的点:
- 大小写敏感:Google Play的SKU是区分大小写的,v2可能兼容了大小写错误,但v4会直接过滤不匹配的SKU,甚至导致整个请求失败
- SKU类型匹配:确认你传入的所有SKU都是
INAPP类型(非订阅),如果混了一个订阅SKU,会导致请求异常 - 参数拼接错误:打印
skuList的所有元素,确认PRODUCT_ID_PREFIX + i的结果和Google Play后台的SKU完全一致(比如有没有多下划线、少数字)
可以先简化测试:把skuList改成只包含一个确定有效的INAPP SKU,看看能不能触发回调。如果可以,再逐个添加其他SKU,找出可能有问题的那个。
4. 检查应用签名与Google Play后台的一致性
升级到v4后,部分开发者遇到过签名验证更严格的情况。如果你的应用在升级过程中更换了签名证书(比如从调试签名换成发布签名,或者使用了App Signing by Google Play),要确保:
- 本地测试用的签名和Google Play后台配置的签名一致
- 如果开启了App Signing,要把上传密钥的SHA-1也添加到Google Play的应用签名配置中
5. 排查回调线程的潜在问题
虽然querySkuDetailsAsync的回调默认会切换到主线程,但如果你的activity实例在回调触发前已经销毁(比如用户切到后台),可能导致回调无法执行。可以在回调中添加空指针判断:
(billingResult, skuDetailsList) -> { if (activity.isFinishing() || activity.isDestroyed()) { activity.logWarn("Activity destroyed, skipping SKU response"); return; } // 后续逻辑 }
最后,修改fetchSkuDetails的回调,强制打印所有结果(包括skuDetailsList为null的情况),这样即使失败也能看到具体错误:
(billingResult, skuDetailsList) -> { activity.hideProgressBar(); int responseCode = billingResult.getResponseCode(); String debugMsg = billingResult.getDebugMessage(); int listSize = skuDetailsList != null ? skuDetailsList.size() : -1; activity.logInfo("SKU response received: code=%d, msg=%s, listSize=%d", responseCode, debugMsg, listSize); if (isBillingResultSuccessful(billingResult, RETRIEVING_PRODUCTS)) { addProducts(skuDetailsList); skuResponseListener.onSkuDetailsResponse(skuDetailsList); } else { activity.logError("Fetch SKU failed: %d - %s", responseCode, debugMsg); } }
内容的提问来源于stack exchange,提问作者Egemen

