整个应用程序中的一个 BillingClient 实例

One instance of BillingClient throughout app

我有一个有很多活动的应用程序。 activity 之一用于显示购买选项。

在计费库 (https://github.com/googlesamples/android-play-billing) 的示例应用程序中,使用了 BillingClientLifecycle 和 BillingManager,它们都关联到一个 activity,因此连接是 opened/closed当 activity 是 created/destroyed.

但是在一个有很多活动的应用中,为不同的活动分别做这件事似乎不太理想。我还想在应用程序启动时检查订阅是否有效。

我正在考虑在应用程序的应用程序子类中创建 BillingClient。但是,如果我这样做,我只会打开 BillingClient 连接而不是关闭它(因为那里没有 onDestroy 方法)。有没有人这样做过并遇到任何问题?这是否也违反了最佳实践,是否会导致网络/性能出现任何问题?

看起来这可以用架构组件来完成。 IE。在您的应用程序的 OnCreate 中,调用:

ProcessLifecycleOwner.get().lifecycle.addObserver(billingClient)

然后将 billingClient 注入到需要它的活动中。

'I would suggest not to make a Singleton class that provides BillingClient from it and initialized through application class.'

为什么?因为,这样做,您有机会在整个应用程序中使用时泄漏内存或对象。


另一种方法是将 class 设为 LifecycleObserver,这样一旦您将它绑定到您的 Activity/Fragment 尽管尊重它的 生命周期 并相应地做事。

我创建了如下 class 并在我的一些项目中使用 (在其中,它工作得很好)

查看下面的 class :

InAppBilling.kt

class InAppBilling(val lifecycle: Lifecycle, purchasesUpdatedListener: PurchasesUpdatedListener) :
    LifecycleObserver,
    PurchasesUpdatedListener by purchasesUpdatedListener,
    BillingClientStateListener {

    companion object {
        const val TAG = "InAppBilling"
    }

    init {
        lifecycle.addObserver(this)
    }

    private var mBillingClient: BillingClient? = null

    private fun initClient(): BillingClient {
        return BillingClient
                .newBuilder(context) // You can provide application context here.
                .setListener(this)
                .build()
    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_CREATE)
    fun connectionToBillingServer() {
        mBillingClient = initClient()
        mBillingClient?.startConnection(this)
    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY)
    fun disconnectFromBillingServer() {
        mBillingClient?.endConnection()
        lifecycle.removeObserver(this)
    }

    override fun onBillingServiceDisconnected() {
        if (lifecycle.currentState == Lifecycle.State.CREATED) {
            mBillingClient?.startConnection(this)
        }
    }

    override fun onBillingSetupFinished(responseCode: Int) {
        // Left out because "Unused"
    }
}

以及如何食用它:

在您要使用的 Activity/Fragment 内部 BillingClient:

private var mBillingClient: InAppBilling? = null

//.. And inside of onCreate() method, you'll just need to initialize it like below:
    mBillingClient = InAppBilling(this@Activity.lifecycle, this)

现在,您可以使用此 billingClient 对象 执行您希望从应用内客户端执行的操作。

您需要做的就是将您的特定方法添加到 InAppBilling class 并在您需要的地方使用该方法。

查看来自this gist的class

BillingClient 需要当前 activity,因为它需要当前 window 令牌 才能向用户显示购买对话框。所以每次 activity 改变 window 令牌 也会改变所以你不能用单例 class 这样做,因为在单例 class 你正在泄漏 activity 引用并提供单个 window 令牌,这在您的应用程序会话中无效。

制作一个 BaseActivity 并让所有其他活动扩展基础 activity。在 BaseActivity 中创建计费实例。

无需申请class。与在应用程序中一样,退出应用程序时您不会收到事件。此外,如果您将应用程序置于后台,应用程序实例仍然存在,因此连接将不必要地保持打开状态。

我通读了 billing-1.2.2-sources.jarBillingClientImpl.java 的源代码,我相信将 BillingClient 用作应用程序单例是安全的,即使这意味着永远不会调用 [=13] =].

BillingClientImpl.java 在其构造函数中没有 need/use Activity;它使用 Context,它所做的只是调用 context.getApplicationContext() 来存储应用上下文。 launchBillingFlow 方法确实有一个 Activity 参数,但是 activity 没有被存储;它的唯一目的是使用计费意图调用 activity.startActivity(intent)

BillingClient.startConnection 调用 context.registerReceiver 将自己的 BillingBroadcastReceiver 注册为 BroadcastReceiver,然后调用 context.bindService 绑定服务连接。 (同样,这两个调用都是针对应用程序上下文 mApplicationContext 执行的,而不是针对任何特定的 Activity。)

只要在应用程序的生命周期内需要计费客户端,在 Application.onCreate() 中调用 registerReceiverbindService 并且永远不要调用 [=31] 是安全和可接受的=] 或 unbindService.

如果 registerReceiver and/or bindService 调用使用 Activity 上下文,这将是不安全的,因为 ServiceConnection 会在 Activity被销毁了,但是当应用被销毁时,它的进程终止,它的所有服务连接都被自动清理。

关于计费库的更新 2.x 版本,引自 TrivialDriveKotlin official demo app BillingRepository 来源:

Notice that the connection to [playStoreBillingClient] is created using the applicationContext. This means the instance is not [Activity]-specific. And since it's also not expensive, it can remain open for the life of the entire [Application]. So whether it is (re)created for each [Activity] or [Fragment] or is kept open for the life of the application is a matter of choice.

我想这也适用于第一个版本。