Google Play Billing集成问题:支付环境异常与SharedPreferences失效
集成Google Play Billing遇到的三类问题及代码分析
一、问题清单
- 测试环境异常:已上传应用包到Internal Testing,且应用已发布生产环境,但测试时不显示测试支付选项,仅出现真实支付渠道,产生真实扣费
- SharedPreferences逻辑未生效:补充购后去广告的SharedPreferences逻辑后,
handlePurchase()与restorePurchase()方法调用该逻辑后广告仍无法移除 - 应用崩溃:完成购买支付流程后点击两次“Okay”按钮,应用崩溃,无法通过Studio调试定位原因
二、完整代码
public class HelpActivity extends AppCompatActivity { private BillingClient billingClient; private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { if (purchases.size() > 0) { for (Purchase purchase : purchases) { handlePurchase(purchase); } } else { Toast.makeText(HelpActivity.this, "Unable to handle purchase", Toast.LENGTH_SHORT).show(); } } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) { Toast.makeText(HelpActivity.this, "Purchase Canceled", Toast.LENGTH_SHORT).show(); } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED ) { if (purchases.size() > 0){ for (Purchase purchase : purchases) { handlePurchase(purchase); } Toast.makeText(HelpActivity.this, "You already own the item", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(HelpActivity.this, "Unable to handle purchase(Item Owned)", Toast.LENGTH_SHORT).show(); } } } }; private MaterialButton removeAdsBtn; private MaterialButton restorePurchaseBtn; private ProductDetails productDetails; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_help_avtivity); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); Window window = this.getWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.setStatusBarColor(this.getResources().getColor(R.color.black)); } restorePurchaseBtn = findViewById(R.id.restoreAdsBtn); removeAdsBtn = findViewById(R.id.removeAdsBtn); setupBillingClient(); restorePurchaseBtn.setOnClickListener(view -> restorePurchases()); removeAdsBtn.setOnClickListener(view -> initiateBillingFlow()); } private void handlePurchase(Purchase purchase) { if (!purchase.isAcknowledged()) { billingClient.acknowledgePurchase(AcknowledgePurchaseParams .newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(), billingResult -> { if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { Toast.makeText(HelpActivity.this, "Thank you for the purchase", Toast.LENGTH_SHORT).show(); Toast.makeText(HelpActivity.this, "Restarting the app, Please wait ...", Toast.LENGTH_SHORT).show(); SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("ads_removed", true); editor.apply(); Intent intent = new Intent(HelpActivity.this, MainActivity.class); startActivity(intent); } }); } } public void setupBillingClient() { billingClient = BillingClient.newBuilder(this).setListener(purchasesUpdatedListener).enablePendingPurchases().build(); establishConnection(); } void establishConnection() { billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(@NonNull BillingResult billingResult) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { QueryPurchase(); } } @Override public void onBillingServiceDisconnected() { establishConnection(); } }); } public void QueryPurchase() { QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(ImmutableList.of(QueryProductDetailsParams.Product.newBuilder().setProductId("removeads_rickroll").setProductType(BillingClient.ProductType.INAPP).build())).build(); billingClient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { if (productDetailsList != null && !productDetailsList.isEmpty()) { productDetails = productDetailsList.get(0); } } } }); } public void initiateBillingFlow() { if (productDetails != null) { ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = ImmutableList.of( BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); BillingResult billingResult = billingClient.launchBillingFlow(HelpActivity.this, billingFlowParams); } } public void restorePurchases() { billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener((billingResult, list) -> { }).build(); final BillingClient finalBillingClient = billingClient; billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingServiceDisconnected() { establishConnection(); } @Override public void onBillingSetupFinished(@NonNull BillingResult billingResult) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { finalBillingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult1, list) -> { if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) { if (list.size() > 0) { Toast.makeText(HelpActivity.this, "Successfully restored", Toast.LENGTH_SHORT).show(); SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("ads_removed", true); editor.apply(); Intent intent = new Intent(HelpActivity.this, MainActivity.class); startActivity(intent); } else { Toast.makeText(HelpActivity.this, "Oops, No purchase found.", Toast.LENGTH_SHORT).show(); SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("ads_removed", false); editor.apply(); } } }); } } }); } protected void onResume() { super.onResume(); billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult, list) -> { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { for (Purchase purchase : list) { if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged()) { handlePurchase(purchase); } } } } ); } }
三、问题修复方案
1. 测试环境异常修复
测试支付不生效核心是账号与渠道配置问题:
- 确保你的测试Google账号已添加到Google Play Console的Internal Testing许可测试者列表
- 测试时必须使用该测试账号登录设备,且应用需从Google Play Internal Testing渠道安装(不能直接运行Studio编译的安装包)
- 对应内购商品
removeads_rickroll需在Console中开启测试价格,确保测试账号能获取测试权限 - 代码无需额外配置测试环境,
BillingClient会自动根据安装渠道识别测试模式
2. SharedPreferences逻辑未生效修复
代码中已写入SharedPreferences,但广告未移除是因为缺少读取判断逻辑:
- 在广告加载的核心位置(如MainActivity或广告初始化类)添加读取逻辑:
SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE); boolean adsRemoved = preferences.getBoolean("ads_removed", false); if (adsRemoved) { // 隐藏或跳过广告加载逻辑 } else { // 正常加载广告 }
- 优化
restorePurchases()方法,复用全局billingClient避免重复创建导致连接异常:
public void restorePurchases() { if (billingClient.isReady()) { billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult1, list) -> { if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) { boolean hasPurchases = list.size() > 0; Toast.makeText(HelpActivity.this, hasPurchases ? "Successfully restored" : "Oops, No purchase found.", Toast.LENGTH_SHORT).show(); updateAdRemoveStatus(hasPurchases); if (hasPurchases) restartApp(); } }); } else { establishConnection(); } } // 抽离重复代码,避免冗余 private void updateAdRemoveStatus(boolean isRemoved) { SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE); preferences.edit().putBoolean("ads_removed", isRemoved).apply(); } private void restartApp() { Intent intent = new Intent(HelpActivity.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); }
3. 应用崩溃修复
点击两次“Okay”崩溃是因为重复启动Activity+未处理BillingClient并发问题:
- 修改
handlePurchase(),添加finish()关闭当前页面,避免栈中残留多个实例:
private void handlePurchase(Purchase purchase) { if (!purchase.isAcknowledged()) { billingClient.acknowledgePurchase(AcknowledgePurchaseParams .newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(), billingResult -> { if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { Toast.makeText(HelpActivity.this, "Thank you for the purchase", Toast.LENGTH_SHORT).show(); Toast.makeText(HelpActivity.this, "Restarting the app, Please wait ...", Toast.LENGTH_SHORT).show(); updateAdRemoveStatus(true); restartApp(); } }); } else { updateAdRemoveStatus(true); restartApp(); } }
- 优化
onResume(),增加billingClient.isReady()判断,避免连接未就绪时调用方法导致崩溃:
@Override protected void onResume() { super.onResume(); if (billingClient != null && billingClient.isReady()) { billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult, list) -> { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { for (Purchase purchase : list) { if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { if (!purchase.isAcknowledged()) { handlePurchase(purchase); } else { updateAdRemoveStatus(true); } } } } } ); } }
内容的提问来源于stack exchange,提问作者sagar raval
相关产品推荐
相关产品推荐

