最近我将我的一个应用程序迁移到 Google Play 应用内结算 v3。自发布以来,我仅在 Samsung 设备 上收到一些崩溃报告,这些报告都与
BillingClient.onBillingServiceDisconnected()
被调用有关。
当前代码如下所示:
val billingClient = BillingClient.newBuilder(context)
.setListener(updatedListener)
.enablePendingPurchases()
.build()
billingClient.startConnection(
object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// The billing client is ready. You can query purchases here.
querySkuDetails()
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
initBilling() // all code here is wrapped in this method
}
}
)
我显然重新初始化了
BillingClient
并在错误情况下再次调用 startConnection()
。那么崩溃就是
java.lang.IllegalStateException:
at android.os.Parcel.createException (Parcel.java:2096)
at android.os.Parcel.readException (Parcel.java:2056)
at android.os.Parcel.readException (Parcel.java:2004)
at android.app.IActivityManager$Stub$Proxy.registerReceiver (IActivityManager.java:5557)
at android.app.ContextImpl.registerReceiverInternal (ContextImpl.java:1589)
at android.app.ContextImpl.registerReceiver (ContextImpl.java:1550)
at android.app.ContextImpl.registerReceiver (ContextImpl.java:1538)
at android.content.ContextWrapper.registerReceiver (ContextWrapper.java:641)
at com.android.billingclient.api.zze.zza (zze.java:5)
at com.android.billingclient.api.zzd.zza (zzd.java:5)
at com.android.billingclient.api.BillingClientImpl.startConnection (BillingClientImpl.java:58)
at de.memorian.gzg.presentation.base.IAPHelper.initBilling (IAPHelper.java:40)
at de.memorian.gzg.presentation.base.IAPHelper$initBilling$1.onBillingServiceDisconnected (IAPHelper.java:53)
at com.android.billingclient.api.BillingClientImpl$zza.onServiceDisconnected (BillingClientImpl.java:11)
at android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:2060)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run (LoadedApk.java:2099)
at android.os.Handler.handleCallback (Handler.java:883)
at android.os.Handler.dispatchMessage (Handler.java:100)
at android.os.Looper.loop (Looper.java:237)
at android.app.ActivityThread.main (ActivityThread.java:7857)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1076)
Caused by: android.os.RemoteException:
at com.android.server.am.ActivityManagerService.registerReceiver (ActivityManagerService.java:16726)
at android.app.IActivityManager$Stub.onTransact (IActivityManager.java:2250)
at com.android.server.am.ActivityManagerService.onTransact (ActivityManagerService.java:3357)
at android.os.Binder.execTransactInternal (Binder.java:1021)
at android.os.Binder.execTransact (Binder.java:994)
我想知道我在
onBillingServiceDisconnected()
中做错了什么,所以我在谷歌上搜索了一段时间,没有找到任何明确的建议,但// implement your own retry logic
。那是例如Google 说什么。这里的重试逻辑到底是什么?正如您在堆栈跟踪中看到的那样,按照 Google 评论的建议,再次调用 startConnection()
会导致崩溃。 这里 Google 说我应该忽略它,因为 Play 服务最终会稍后调用 onBillingSetupFinished()
。
您如何处理此案?
没有找到我的问题如何处理失败案例的具体答案。我重构了我的代码,所以我基本上忽略了对
onBillingServiceDisconnected()
的调用,只向用户显示错误消息。
现在每次尝试购买时都会检查是否
BillingClient
已初始化BilligClient
是 ready
只有在这些成功后才尝试购买。
以前我在应用程序初始化中执行过一次以上所有操作。如果连接失败,现在,当用户再次单击购买项目时,我将简单地重试(使用 try catch)。这可能无法解决崩溃问题,但至少可以为用户提供更好的体验和控制。
我也在我的 Google Play 仪表板中收到了类似的堆栈跟踪,但不是崩溃,而是 ANR。我解决这个问题的方法是将重新初始化计费的调用移至后台线程。
请注意,当有连接时会调用
onBillingServiceDisconnected
,但它会丢失。您可以通过在应用程序打开时清除 Google Play 的数据来进行测试。如果此时不重试,连接将会丢失。
当您尝试连接但失败时,会调用带有错误代码的
onBillingSetupFinished
。之前不存在任何联系。令人困惑的是,您还应该根据错误代码在此处重试。
您可以通过实现
Retry逻辑来处理
onBillingServiceDisconnected()
方法:
private static long reconnectMilliseconds = 1000;
@Override
public void onBillingServiceDisconnected() {
retryBillingServiceConnection();
}
private void retryBillingServiceConnection() {
new Handler().postDelayed(() ->
connectToBillingService(),
reconnectMilliseconds);
reconnectMilliseconds = reconnectMilliseconds * 2;
}
他们正试图以指数延迟重新连接:
private const val RECONNECT_TIMER_START_MILLISECONDS = 1L * 1000L
private const val RECONNECT_TIMER_MAX_TIME_MILLISECONDS = 1000L * 60L * 15L // 15 minutes
...
override fun onBillingSetupFinished(billingResult: BillingResult) {
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
// The billing client is ready. You can query purchases here.
// This doesn't mean that your app is set up correctly in the console -- it just
// means that you have a connection to the Billing service.
reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS
defaultScope.launch {
querySkuDetailsAsync()
refreshPurchases()
}
}
else -> retryBillingServiceConnectionWithExponentialBackoff()
}
}
...
/**
* This is a pretty unusual occurrence. It happens primarily if the Google Play Store
* self-upgrades or is force closed.
*/
override fun onBillingServiceDisconnected() {
retryBillingServiceConnectionWithExponentialBackoff()
}
...
/**
* Retries the billing service connection with exponential backoff, maxing out at the time
* specified by RECONNECT_TIMER_MAX_TIME_MILLISECONDS.
*/
private fun retryBillingServiceConnectionWithExponentialBackoff() {
handler.postDelayed(
{ billingClient.startConnection(this@BillingDataSource) },
reconnectMilliseconds
)
reconnectMilliseconds = min(
reconnectMilliseconds * 2,
RECONNECT_TIMER_MAX_TIME_MILLISECONDS
)
}