One instance of BillingClient throughout app

I read through the sources of BillingClientImpl.java in billing-1.2.2-sources.jar, and I believe it is safe to use BillingClient as an application singleton, even if this means never calling BillingClient.endConnection().

BillingClientImpl.java doesn't need/use an Activity in its constructor; it uses a Context, and all it does is call context.getApplicationContext() to store the app context. The launchBillingFlow method does have an Activity parameter, but the activity isn't stored; its only purpose is to call activity.startActivity(intent) with the billing intent.

BillingClient.startConnection calls context.registerReceiver to register its own BillingBroadcastReceiver as a BroadcastReceiver, then calls context.bindService to bind a service connection. (Again, both of these calls are executed against the app context mApplicationContext, not on any particular Activity.)

As long as the billing client is required for the lifetime of the app, it's safe and acceptable to call registerReceiver and bindService in Application.onCreate() and to never call unregisterReceiver or unbindService.

This would not be safe if the registerReceiver and/or bindService calls used an Activity context, because the ServiceConnection would leak when the Activity was destroyed, but when the app is destroyed, its process terminates, and all of its service connections are automatically cleaned up.


It looks like this can be done with architecture components. I.e. in your application's OnCreate, call:

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

And just inject the billingClient into the activities that need it.


Regarding the updated 2.x version of the billing library, a quote from the TrivialDriveKotlin official demo app BillingRepository sources:

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.

I guess this applies to the first version too.