Google Billing 中 handlePurchase 的 billingClient 问题
数据小许
结论
billingClient 不能在当前匿名内部类中直接引用,因为它的局部变量声明位于 purchaseUpdateListener 之后,在匿名内部类所在的位置还不可见。
建议把 BillingClient 保存为 Billing 类的成员变量,并在监听器中通过 Billing.this.billingClient 访问。这样也方便统一管理连接和资源释放。
推荐实现
public class Billing {
private final Context context;
private final List<String> skuList;
private BillingClient billingClient;
public Billing(Context context, List<String> skuList) {
this.context = context.getApplicationContext();
this.skuList = skuList;
}
public interface Callback {
void onSkuDetailsList(
BillingClient billingClient,
List<SkuDetails> skuDetailsList
);
void onResponse(Purchase purchase);
void onError();
}
public void myPurchase(final Callback callback) {
PurchasesUpdatedListener purchaseUpdateListener =
new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(
BillingResult billingResult,
List<Purchase> purchases
) {
if (billingResult.getResponseCode()
== BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
callback.onResponse(purchase);
handlePurchase(Billing.this.billingClient, purchase);
}
} else {
callback.onError();
}
}
};
billingClient = BillingClient.newBuilder(context)
.enablePendingPurchases()
.setListener(purchaseUpdateListener)
.build();
}
private void handlePurchase(
BillingClient billingClient,
Purchase purchase
) {
if (billingClient == null) {
return;
}
// 在这里验证并处理购买结果。
}
}
Billing.this.billingClient 表示访问外层 Billing 对象的成员变量。这里只写 billingClient 通常也能正常工作,但显式写出外层对象后,变量来源更清楚。
为什么原代码无法编译
Java 的局部变量只有从声明位置开始才可见。原代码的顺序如下:
PurchasesUpdatedListener purchaseUpdateListener = ... {
handlePurchase(billingClient, purchase);
};
final BillingClient billingClient = ...;
编译器处理匿名内部类时,局部变量 billingClient 尚未声明,因此无法解析这个名称。
直接交换两段代码同样无法解决,因为创建 BillingClient 时还要把 purchaseUpdateListener 传给 setListener():
.setListener(purchaseUpdateListener)
两者在初始化时相互依赖,因此把客户端改为成员变量最直接。
也可以让监听器调用实例方法
如果 handlePurchase() 只供当前 Billing 对象内部使用,就不必反复传入 BillingClient:
private void handlePurchase(Purchase purchase) {
if (billingClient == null) {
return;
}
// 使用成员变量 billingClient 处理 purchase。
}
然后在监听器中调用:
handlePurchase(purchase);
这种写法更简洁,因为 BillingClient 本来就是这个类需要管理的状态。
注意事项
构建 BillingClient 只是创建了对象。查询商品、发起购买或确认购买之前,通常还要调用 startConnection() 建立连接,并等待 onBillingSetupFinished() 返回成功后再继续操作。
还需要注意以下几点:
- 不要在每次调用
myPurchase()时都无条件创建新的BillingClient,除非旧实例已经正确关闭。 - 不再使用客户端时,调用
billingClient.endConnection()。 onPurchasesUpdated()不应只把Purchase返回给界面层。购买成功后,还要根据商品类型和业务流程进行验证、确认或消耗。- 确认商品权益已经正确发放后,才能执行购买确认。
- 如果使用较新的 Google Play Billing Library,部分类型或构建参数可能与示例不同,应以项目实际依赖版本的 API 为准。
备注:内容仅供参考。
