我创建了以下 BillingManager 类来处理我的 Android 应用程序的 Google Play 计费库:
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);
}
}
这是我希望如何在我的 FragmentDialog 类之一中使用它的示例:
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));
});
}
问题:
我在尝试使用从 Google Play 结算库检索到的产品价格设置按钮文本时遇到 IndexOutOfBoundsException。导致问题的行是:
button.setText(billingManager.getProductDetailsList().get(0).getOneTimePurchaseOfferDetails().getFormattedPrice());
问题源于我的 BillingManager 类中的异步 setConnection 和 getProductDetails 方法。当片段启动并尝试访问产品详细信息以设置按钮文本时,这些操作尚未完成,导致列表为空或未初始化。
如何解决此问题并确保仅在建立与 Google Play Billing 的连接并且成功检索产品详细信息后才设置按钮的文本?
您可以创建一个界面
BillingStatusListener
interface BillingStatusListener {
void onProductListFetched(List<YourType> productList);
void onBillingSetupFinishedSuccessfully();
}
让你的
FragmentDialog
实现这个接口
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
}
}
最后,让你的
BillingManager
接受上面接口的实现并根据需要使用它
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);
}
}