You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 23:32:13