最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Display Product data from BillingClient - Stack Overflow

programmeradmin0浏览0评论

I have made the following BillingManager class to handle the Google Play Billing Library for my Android app:

public class BillingManager {

    private List<ProductDetails> productDetailsList;

    private PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
        // To be implemented in a later section.
    };

    private BillingClient billingClient = BillingClient.newBuilder(MyApplication.getAppContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
            .build();

    public void establishConnection() {
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.i("BillingManager", "Connected to Google Play.");
                    getProductDetails();
                }
            }
            @Override
            public void onBillingServiceDisconnected() {
                Log.i("BillingManager", "Disconnected from Google Play.");
                establishConnection();
            }
        });
    }

    private void getProductDetails() {
        QueryProductDetailsParams queryProductDetailsParams =
                QueryProductDetailsParams.newBuilder()
                        .setProductList(List.of(
                                QueryProductDetailsParams.Product.newBuilder()
                                        .setProductId("full_version")
                                        .setProductType(BillingClient.ProductType.INAPP)
                                        .build()))
                        .build();

        billingClient.queryProductDetailsAsync(
                queryProductDetailsParams,
                (billingResult, productDetailsList) -> {
                    this.productDetailsList = productDetailsList;
                }
        );

    }

    public List<ProductDetails> getProductDetailsList() {
        return productDetailsList;
    }

    public void launchPurchaseFlow(Activity activity, ProductDetails productDetails) {
        List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                List.of(
                        BillingFlowParams.ProductDetailsParams.newBuilder()
                                // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
                                .setProductDetails(productDetails)
                                .build()
                );

        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build();

    // Launch the billing flow
        BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    }
}

And this is an example of how I would want to use it in one of my FragmentDialog class:

BillingManager billingManager = new BillingManager();
    billingManager.establishConnection();

    MaterialButton button = view.findViewById(R.id.button);

// This causes an outOfBounds exception
button.setText(billingManager.getProductDetailsList().get(0).getOneTimePurchaseOfferDetails().getFormattedPrice());

    button.setOnClickListener(v -> {
        billingManager.launchPurchaseFlow(getActivity(), billingManager.getProductDetailsList().get(0));
    });

}

Question:

I'm encountering an IndexOutOfBoundsException when trying to set the text of a button with a product price retrieved from the Google Play Billing Library. The line causing the issue is:

button.setText(billingManager.getProductDetailsList().get(0).getOneTimePurchaseOfferDetails().getFormattedPrice());

The problem stems from the asynchronous establishConnection and getProductDetails methods within my BillingManager class. When the fragment starts and attempts to access the product details to set the button's text, these operations haven't completed yet, resulting in an empty or uninitialized list.

How can I solve this and ensure that the button's text is set only after both the connection to Google Play Billing has been established and the product details have been successfully retrieved?

I have made the following BillingManager class to handle the Google Play Billing Library for my Android app:

public class BillingManager {

    private List<ProductDetails> productDetailsList;

    private PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
        // To be implemented in a later section.
    };

    private BillingClient billingClient = BillingClient.newBuilder(MyApplication.getAppContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
            .build();

    public void establishConnection() {
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.i("BillingManager", "Connected to Google Play.");
                    getProductDetails();
                }
            }
            @Override
            public void onBillingServiceDisconnected() {
                Log.i("BillingManager", "Disconnected from Google Play.");
                establishConnection();
            }
        });
    }

    private void getProductDetails() {
        QueryProductDetailsParams queryProductDetailsParams =
                QueryProductDetailsParams.newBuilder()
                        .setProductList(List.of(
                                QueryProductDetailsParams.Product.newBuilder()
                                        .setProductId("full_version")
                                        .setProductType(BillingClient.ProductType.INAPP)
                                        .build()))
                        .build();

        billingClient.queryProductDetailsAsync(
                queryProductDetailsParams,
                (billingResult, productDetailsList) -> {
                    this.productDetailsList = productDetailsList;
                }
        );

    }

    public List<ProductDetails> getProductDetailsList() {
        return productDetailsList;
    }

    public void launchPurchaseFlow(Activity activity, ProductDetails productDetails) {
        List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                List.of(
                        BillingFlowParams.ProductDetailsParams.newBuilder()
                                // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
                                .setProductDetails(productDetails)
                                .build()
                );

        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build();

    // Launch the billing flow
        BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    }
}

And this is an example of how I would want to use it in one of my FragmentDialog class:

BillingManager billingManager = new BillingManager();
    billingManager.establishConnection();

    MaterialButton button = view.findViewById(R.id.button);

// This causes an outOfBounds exception
button.setText(billingManager.getProductDetailsList().get(0).getOneTimePurchaseOfferDetails().getFormattedPrice());

    button.setOnClickListener(v -> {
        billingManager.launchPurchaseFlow(getActivity(), billingManager.getProductDetailsList().get(0));
    });

}

Question:

I'm encountering an IndexOutOfBoundsException when trying to set the text of a button with a product price retrieved from the Google Play Billing Library. The line causing the issue is:

button.setText(billingManager.getProductDetailsList().get(0).getOneTimePurchaseOfferDetails().getFormattedPrice());

The problem stems from the asynchronous establishConnection and getProductDetails methods within my BillingManager class. When the fragment starts and attempts to access the product details to set the button's text, these operations haven't completed yet, resulting in an empty or uninitialized list.

How can I solve this and ensure that the button's text is set only after both the connection to Google Play Billing has been established and the product details have been successfully retrieved?

Share Improve this question asked Jan 18 at 13:43 Tennis HealTennis Heal 231 silver badge3 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

You can create an interface BillingStatusListener

interface BillingStatusListener {
   void onProductListFetched(List<YourType> productList);
   void onBillingSetupFinishedSuccessfully();
}

Make your FragmentDialog implement this interface

class FragmentDialog implements BillingStatusListener {

   @Override
   void onProductListFetched(List<YourType> productList) {
      // Setup your button text here
      // You are sure to have data here, and no Null Pointer exceptions

   }
}

Lastly, make your BillingManager accept an implementation of the interface above and use it as required

public class BillingManager {

    private List<ProductDetails> productDetailsList;

    private PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
        // To be implemented in a later section.
    };

   private BillingStatusListener billingStatusListener; // Initialise this somewhere with the FragmentDialog class as the implementation

    private BillingClient billingClient = BillingClient.newBuilder(MyApplication.getAppContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
            .build();

    public void establishConnection() {
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.i("BillingManager", "Connected to Google Play.");
                    getProductDetails();
                }
            }
            @Override
            public void onBillingServiceDisconnected() {
                Log.i("BillingManager", "Disconnected from Google Play.");
                establishConnection();
            }
        });
    }

    private void getProductDetails() {
        QueryProductDetailsParams queryProductDetailsParams =
                QueryProductDetailsParams.newBuilder()
                        .setProductList(List.of(
                                QueryProductDetailsParams.Product.newBuilder()
                                        .setProductId("full_version")
                                        .setProductType(BillingClient.ProductType.INAPP)
                                        .build()))
                        .build();

        billingClient.queryProductDetailsAsync(
                queryProductDetailsParams,
                (billingResult, productDetailsList) -> {
                    this.productDetailsList = productDetailsList;
                    // you will surely have data here
                    // so let's notify the interface
                    billingStatusListener.onProductListFetched(productDetailList);
                }
        );

    }

    public List<ProductDetails> getProductDetailsList() {
        return productDetailsList;
    }

    public void launchPurchaseFlow(Activity activity, ProductDetails productDetails) {
        List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                List.of(
                        BillingFlowParams.ProductDetailsParams.newBuilder()
                                // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
                                .setProductDetails(productDetails)
                                .build()
                );

        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build();

    // Launch the billing flow
        BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    }
}
发布评论

评论列表(0)

  1. 暂无评论