将订阅添加到您的Android应用程序中。第3部分。检查用户是否已订阅
#kotlin #android #mobile #tristan

目录

  1. Resources I used
  2. Before we start
  3. Start coding
  4. What is the next tutorial about?

我在Google Playstore上的应用程序

github代码

我使用了的资源

在我们开始之前

  • 在您进一步阅读之前,您必须完成以下2件事

1)正确设置了您的Google开发人员的重申:可以找到指令HERE

2)添加Google计费库:可以找到库HERE。您需要将此库添加到您的gradle.build文件中,然后将您的应用发布到生产,内部测试或封闭测试中。

确定用户是否已订阅

  • 因此,假设您已经完成了所有前面提到的事情,我们现在可以担心如何确定用户是否已订阅。

  • 我们要做的第一件事是创建一个名为BillingClientWrapper的类,而本类是我们将所有代码直接与Google Play计费库

  • 直接进行交互的代码。

在BillingClientWrapper 中创建状态

  • 现在,我们需要创建一个将持有我们订阅的信息的mutableState对象。然后,我们需要将其映射到状态对象并将其暴露于我们的其余代码中:
class BillingClientWrapper(
    context: Context
): PurchasesUpdatedListener{

    // Current Purchases
    private val _purchases =
        MutableStateFlow<List<Purchase>>(listOf())
    val purchases = _purchases.asStateFlow()

}

  • 从mutableState到状态的映射,然后将其视为称为purchases的公共变量,是一个很好的编码实践。它使我们能够在代码之间定义清晰的边界。

目前我们可以忽略PurchasesUpdatedListener接口。但是不用担心,我们很快就会谈论

初始化billingclient

// Initialize the BillingClient.
    private val billingClient = BillingClient.newBuilder(context)
        .setListener(this)
        .enablePendingPurchases()
        .build()
  • BillingClient是Google Play帐单库和应用程序其余部分之间通信的主要接口。 BillingClient提供了许多常见的计费操作的便利方法,无论是同步还是异步。强烈建议我们一次打开一个主动的billingclient连接,以避免单个事件的多次购买updateDlistener回调

.setListener(this)中,this指的是PurchasesUpdatedListener接口。由于它是一个接口,因此我们必须实现其方法,即onPurchasesUpdated方法:

 override fun onPurchasesUpdated(
        billingResult: BillingResult, //contains the response code from the In-App billing API
        purchases: List<Purchase>? // a list of objects representing in-app purchases
    ) {

        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK
            && !purchases.isNullOrEmpty()
        ) {
            // Post new purchase List to _purchases
            _purchases.value = purchases

            // Then, handle the purchases
        for (purchase in purchases) {
         acknowledgePurchases(purchase)// dont need to worry about
            }
        } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
            // Handle an error caused by a user cancelling the purchase flow.

            Timber.tag("BILLINGR").e("User has cancelled")
        } else {
            // Handle any other error codes.
        }
    }

  • 上面的代码负责更新我们的_purchases变量,并且每次用户购买时都会运行。我还想指出我标记为acknowledgePurchases(purchase)// dont need to worry about的代码的部分。目前,我们不必担心此代码。因为它用于检测购买对象的不同状态(我将在未来的教程中谈论)。但是,如果您仍然想自己检查代码,HERE是。

连接到Google Play

  • 我们创建了一个BillingClient后,您需要建立与Google Play的连接。要连接到Google Play,请致电StartConnection()。连接过程是异步的,我们必须实施一个BillingClientStateListener,一旦客户的设置完成,就可以接收回调,并准备提出进一步的请求:
   /*******CALLED TO INITIALIZE EVERYTHING******/
    // Establish a connection to Google Play.
    fun startBillingConnection(billingConnectionState: MutableLiveData<Boolean>) {

        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    Timber.tag("BILLINGR").d("Billing response OK")
        // The BillingClient is ready. You can query purchases and product details here
       queryPurchases()// we talk about this in the next paragraph
                } else {


           Timber.tag("BILLINGR").e(billingResult.debugMessage)
                }
            }

            override fun onBillingServiceDisconnected() {

                Timber.tag("BILLINGR").d("Billing connection disconnected")
                startBillingConnection(billingConnectionState)
            }
        })
    }

  • 在上方的代码块中,您可以看到我们使用billingClient.startConnection(object : BillingClientStateListener创建一个匿名类。这意味着我们正在创建BillingClientStateListener接口的实例。这意味着我们必须覆盖这是两种方法。

1)onbillingsetupfined():在与Google Play的连接完成时,我们可以查询用户的购买。

2)当与Google Play账单服务的连接丢失时,请使用OnBillingServicedIsconnected():。发生这种情况时,我们只是尝试重新连接到startBillingConnection(billingConnectionState)

的递归电话

querypurchases()方法

  • 现在我们可以谈论queryPurchases()
fun queryPurchases() {
        if (!billingClient.isReady) {

            Timber.tag("BILLINGR").e("queryPurchases: BillingClient is not ready")
        }

        // QUERY FOR EXISTING SUBSCRIPTION PRODUCTS THAT HAVE BEEN PURCHASED
        billingClient.queryPurchasesAsync(
            QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build()
        ) { billingResult, purchaseList ->
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                if (!purchaseList.isNullOrEmpty()) {
                    _purchases.value = purchaseList
                } else {
                    _purchases.value = emptyList()
                }

            } else {
                Timber.tag("BILLINGR").e(billingResult.debugMessage)
            }
        }
    }

  • 如果您认为此代码似乎与BillingClientStateListener相似,那么您是对的。但是,作为documentation states,有一些情况(例如,在应用程序外购买订阅),通过致电BillingClient.queryperypersasync()。

  • 接下来,我们将使用QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build()查询Google库中用户所做的任何订阅。

存储库层

  • 现在,我们可以创建一个存储库层,该存储库将用作过滤器,并且只能返回应用程序所需的订阅。
class SubscriptionDataRepository(billingClientWrapper: BillingClientWrapper) {

    // Set to true when a returned purchase is an auto-renewing basic subscription.
    //hasRenewablePremium IS HOW WE WILL DETERMINE IF THERE IS A SUBSCRIPTION OR NOT
    val hasRenewablePremium: Flow<Boolean> = billingClientWrapper.purchases.map { value: List<Purchase> ->
        value.any { purchase: Purchase ->  purchase.products.contains(PREMIUM_SUB) && purchase.isAutoRenewing}
    }
   companion object {
        // List of subscription product offerings
        private const val PREMIUM_SUB = "your_subscription_id"
    }
}
  • 上面代码的重要内容是isAutoRenewingcontains(PREMIUM_SUB)isAutoRenewing是存储在Purchase对象上的标志,以确定订阅是否活动。 contains(PREMIUM_SUB)被用作过滤器,您需要将自己的subscription_id放在订阅中。可以在Monetization -> Subscriptions下的Google Play控制台内找到订阅ID。

ViewModel层

  • 现在我们在ViewModel中,我们需要做6件事:

1)初始化billingclientwrapper

2)初始化存储库

3)创建状态以保存存储库数据

4)创建一种查询存储库的方法

5)启动连接

6)使用销售效果调用querypercerasesasync()

1)初始化billingclientwrapper

  • 我们知道与Google Play帐单库生活在BillingClientWrapper内的所有代码。因此,我们的ViewModel内部的第一件事是初始化它:
class BillingViewModel(application: Application): AndroidViewModel(application){

    var billingClient: BillingClientWrapper = BillingClientWrapper(application)

}

  • 请注意我们如何使用AndroidViewModel(application)而不是传统的ViewModel()。我们这样做是为了允许我们的BillingViewModel访问应用程序上下文,我们用来初始化BillingClientWrapper

2)初始化存储库

  • 下一步是使用初始化的billingclient并将其传递给存储库:
private var repo: SubscriptionDataRepository =
        SubscriptionDataRepository(billingClientWrapper = billingClient)

3)创建状态以保存存储库数据

  • 我们现在需要一个对象来保持我们从存储库中获得的状态并将其暴露在我们的角度上:
data class BillingUiState(
    val subscribed:Boolean = false
)

class BillingViewModel(application: Application): AndroidViewModel(application){

    private val _uiState = mutableStateOf(BillingUiState())
    val state:State<BillingUiState> = _uiState


    private var billingClient: BillingClientWrapper = BillingClientWrapper(application)

    private var repo: SubscriptionDataRepository =
        SubscriptionDataRepository(billingClientWrapper = billingClient)

}

4)创建一种查询我们存储库的方法

 fun refreshPurchases(){

        viewModelScope.launch {
            repo.hasRenewablePremium.collect { collectedSubscriptions ->
                _uiState.value = _uiState.value.copy(
                    subscribed = collectedSubscriptions
                )
            }
        }

    }

  • 您可以从上面的代码中看到,我们正在使用存储在存储库中的流中的viewModelsCope将collect{}从存储库中的流量进行。

5)开始连接

  • 现在我们已经设置了所有方法,我们需要初始化BillingViewModel的init {}块中的所有内容:
init {
        billingClient.startBillingConnection(billingConnectionState = _billingConnectionState)
    }
init{

  refreshPurchases()

}

  • 从技术上讲,当我们的BillingViewModel初始化时,我们的代码将能够识别用户是否购买了任何订阅。但是,我们还没有考虑更多情况,例如文档中描述的HERE中所述的情况。正如文档所述,要处理这些情况,我们需要在我们的onresume()方法中调用billingclient.querypercerasesasync()。

6)使用销售效应调用QueryPurchasesasync()

  • 我们可以使用Side-effects在我们的onresume()中完成billingclient.querypercersasync()。更具体地说,我们将使用DisposableEffect,该DisposableEffect使我们能够在onResume()方法中创建,添加和删除生命周期。实际上,我们可以在构图中的内部进行操作:
@Composable 
fun AddObserver(viewModel:BillingViewModel){
val lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
        // Create an observer that triggers our remembered callbacks
        // for sending analytics events
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME) {

                Timber.tag("LIFEEVENTCYCLE").d("WE ARE RESUMING")
                viewModel.refreshPurchases()
            }
        }

        // Add the observer to the lifecycle
        lifecycleOwner.lifecycle.addObserver(observer)

        // When the effect leaves the Composition, remove the observer
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }


}

  • 为了使此代码工作,我们首先需要更新BillingViewModel内部的refreshPurchases()方法:
     fun refreshPurchases(){

        viewModelScope.launch {
            billingClient.queryPurchases()
            repo.hasRenewablePremium.collect { collectedSubscriptions ->
                //val value = collectedSubscriptions.hasRenewablePremium
                _uiState.value = _uiState.value.copy(
                    subscribed = collectedSubscriptions
                )
            }
        }

    }

  • 请注意,上面的代码现在调用billingClient.queryPurchases(),该代码将更新billingclientwrapper内部的_purchases变量,该变量将更新存储库内部的hasRenewablePremium变量,然后将其收集到bilingviewModel中,然后在bilingviewModel中显示,然后将其显示以显示为bilingviewModel,然后将其显示为此。用作可变state.subscribed

下一个教程是什么

  • 在下一个教程中,我们将实施代码以允许用户购买我们的订阅

结论

  • 感谢您抽出宝贵的时间阅读我的博客文章。如果您有任何疑问或疑虑,请在下面发表评论或在Twitter上与我联系。