Android 应用内计费全解析与实战教程
1. Android 应用内计费概述
应用内购买为 Android 应用提供了通过向用户销售虚拟产品和订阅来创收的途径。使用 Google Play 应用内计费库可以为应用添加应用内购买支持,主要步骤包括创建和初始化计费客户端,然后调用其上的方法执行诸如购买、列出可用产品以及消费现有购买等任务。
2. 连接到 Google Play 计费库
在成功创建计费客户端后,需要初始化与 Google Play 计费库的连接。具体操作如下:
- 调用计费客户端实例的
startConnection()
方法来建立连接。
- 由于连接是异步执行的,因此需要实现
BillingClientStateListener
以接收指示连接是否成功的回调。
- 还应添加代码来重写
onBillingServiceDisconnected()
方法,当与计费库的连接丢失时会调用此方法,可用于向用户报告问题并重新尝试连接。
示例代码如下:
private BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// 连接成功
} else {
// 连接失败
}
}
@Override
public void onBillingServiceDisconnected() {
// 现有连接丢失
}
});
3. 查询可用产品
当计费环境初始化并准备就绪后,下一步是请求可供购买的产品或订阅的详细信息。操作步骤如下:
- 创建一个
QueryProductDetailsParams
实例,并配置产品 ID 和类型(
ProductType.SUBS
用于订阅,
ProductType.INAPP
用于托管产品)。
- 将该实例传递给计费客户端的
queryProductDetailsAsync()
方法。
示例代码如下:
QueryProductDetailsParams queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
ImmutableList.of(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("one_button_click")
.setProductType(BillingClient.ProductType.INAPP)
.build()))
.build();
billingClient.queryProductDetailsAsync(queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(
@NonNull BillingResult billingResult,
@NonNull List<ProductDetails> productDetailsList) {
if (!productDetailsList.isEmpty()) {
// 处理匹配的产品列表
} else {
// 未找到匹配的产品
}
}
}
);
4. 启动购买流程
当用户查询并选择要购买的产品或订阅后,就可以启动购买流程。具体操作是调用计费客户端的
launchBillingFlow()
方法,并传入当前活动和一个配置了购买项目的
ProductDetail
对象的
BillingFlowParams
实例。
示例代码如下:
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
ImmutableList.of(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
)
)
.build();
billingClient.launchBillingFlow(this, billingFlowParams);
5. 完成购买
购买成功后,
PurchasesUpdatedListener
处理程序将接收到一个包含每个项目的
Purchase
对象的列表。可以通过调用
Purchase
实例的
getPurchaseState()
方法来验证项目是否已购买。
示例代码如下:
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
// 购买完成
} else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
// 付款仍在处理中
}
对于非消耗性商品的购买,完成后必须进行确认,以防止向用户退款。对于消耗性购买,需要在商品被消耗时通知 Google Play,以便用户可以再次购买。
6. 查询以前的购买记录
在处理应用内计费时,检查用户是否已经购买了某个产品或订阅是常见需求。可以通过调用计费客户端实例的
queryPurchasesAsync()
方法并实现
PurchaseResponseListener
来生成用户以前特定类型购买的列表。
示例代码如下:
QueryPurchasesParams queryPurchasesParams =
QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build();
billingClient.queryPurchasesAsync(queryPurchasesParams,
new PurchasesResponseListener() {
@Override
public void onQueryPurchasesResponse(@NonNull BillingResult billingResult,
@NonNull List<Purchase> list) {
// 处理购买列表
}
}
);
7. 应用内购买示例项目介绍
这个示例项目的简单概念是,在一个应用中,必须先购买一个应用内产品,才能点击一个按钮。每次点击按钮时,该应用内产品都会被消耗,这就要求用户每次想要点击按钮时都重新购买该产品。
8. 创建 InAppPurchase 项目
具体步骤如下:
1. 启动 Android Studio,从欢迎屏幕中选择“New Project”选项。
2. 在新项目对话框中选择“Empty Views Activity”模板,然后点击“Next”按钮。
3. 在“Name”字段中输入
InAppPurchase
,并指定一个在 Google Play 生态系统中唯一标识应用的包名(例如,
com.<your company>.InAppPurchase
)。
4. 在点击“Finish”按钮之前,将“Minimum API level”设置为 API 26: Android 8.0 (Oreo),并将“Language”菜单设置为 Java。
5. 项目创建完成后,按照相关步骤将项目转换为使用视图绑定。
9. 向项目添加库
在编写代码之前,需要向项目构建配置中添加一些库,包括标准的 Android 计费客户端库和 Google 的 Guava Core Java 库中的
ImmutableList
类。具体操作是修改
Gradle Scripts -> build.gradle.kts (Module: app)
文件,添加以下依赖:
dependencies {
implementation ("com.android.billingclient:billing:6.0.1")
implementation ("com.google.guava:guava:24.1-jre")
implementation ("com.google.guava:guava:27.0.1-android")
}
然后点击编辑器面板顶部的“Sync Now”链接以提交这些更改。
10. 设计用户界面
用户界面将由现有的
TextView
和两个
Button
组成,具体设计步骤如下:
1. 将
activity_main.xml
文件加载到编辑器中,将两个
Button
视图拖放到布局中,使一个位于
TextView
上方,另一个位于下方。
2. 选择
TextView
,将其
id
属性更改为
statusText
。
3. 点击工具栏中的“Clear all Constraints”按钮,然后按住 Shift 键选择所有三个视图。
4. 右键单击最上方的
Button
视图,选择“Center -> Horizontally in Parent”菜单选项。再次重复此步骤,选择“Chains -> Create Vertical Chain”。
5. 将最上方按钮的
text
属性更改为“Consume Purchase”,
id
更改为
consumeButton
,并将
onClick
属性配置为调用名为
consumePurchase
的方法。
6. 选择最下方的按钮,重复上述步骤,将
text
设置为“Buy Product”,
id
设置为
buyButton
,
onClick
回调设置为
makePurchase
。
11. 将应用添加到 Google Play 商店
具体步骤如下:
1. 按照“创建、测试和上传 Android 应用捆绑包”章节中概述的步骤,登录 Play 控制台,创建一个新应用,并设置一个新的内部测试轨道,包括指定测试人员的电子邮件地址。
2. 返回 Android Studio,为项目生成一个签名的发布应用捆绑包。
3. 生成捆绑包文件后,将其上传到内部测试轨道并推出进行测试。
12. 创建应用内产品
在 Play 控制台中选择应用,向下滚动左侧面板中的选项列表,直到出现“Monetize”部分。在该部分中,选择“Products”下列出的“In-app products”选项。在“in-app products”页面上,点击“Create product”按钮。在新产品屏幕上,输入以下信息,然后保存新产品:
- 产品 ID:
one_button_click
- 名称:
A Button Click
- 描述:
This is a test in-app product that allows a button to be clicked once.
- 默认价格:设置为您首选货币的最低可能价格。
13. 启用许可证测试人员
在测试应用内计费时,启用内部测试轨道测试人员的许可证测试可以在不花费真钱的情况下进行测试购买。具体操作如下:
1. 在 Play 控制台中返回主屏幕,选择“Setup -> License testing”选项。
2. 在许可证测试屏幕中,添加为内部测试轨道添加的测试人员,将“License response”设置为
RESPOND_NORMALLY
,并保存更改。
14. 初始化计费客户端
编辑
MainActivity.java
文件,进行以下更改以开始实现应用内购买功能:
import androidx.annotation.NonNull;
import android.util.Log;
import com.android.billingclient.api.*;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private BillingClient billingClient;
private ProductDetails productDetails;
private Purchase purchase;
static final String TAG = "InAppPurchaseTag";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
billingSetup();
}
private void billingSetup() {
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.i(TAG, "OnBillingSetupFinish connected");
queryProduct();
} else {
Log.i(TAG, "OnBillingSetupFinish failed");
}
}
@Override
public void onBillingServiceDisconnected() {
Log.i(TAG, "OnBillingSetupFinish connection lost");
}
});
}
}
15. 查询产品
为确保产品可供购买,需要创建一个配置了在 Play 控制台中指定的产品 ID 的
QueryProductDetailsParams
实例,并将其传递给计费客户端的
queryProductDetailsAsync()
方法。
示例代码如下:
private void queryProduct() {
QueryProductDetailsParams queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
ImmutableList.of(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("one_button_click")
.setProductType(BillingClient.ProductType.INAPP)
.build()))
.build();
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(
@NonNull BillingResult billingResult,
@NonNull List<ProductDetails> productDetailsList) {
if (!productDetailsList.isEmpty()) {
productDetails = productDetailsList.get(0);
runOnUiThread(() -> {
binding.buyButton.setEnabled(true);
binding.statusText.setText(productDetails.getName());
});
} else {
Log.i(TAG, "onProductDetailsResponse: No products");
}
}
}
);
}
16. 启动购买流程
当用户点击购买按钮时,调用
makePurchase()
方法启动购买流程。
示例代码如下:
public void makePurchase(View view) {
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
ImmutableList.of(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
)
)
.build();
billingClient.launchBillingFlow(this, billingFlowParams);
}
17. 处理购买更新
购买过程的结果将通过在初始化阶段分配给计费客户端的
PurchaseUpdatedListener
报告给应用。
示例代码如下:
private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(BillingResult billingResult,
List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
completePurchase(purchase);
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Log.i(TAG, "onPurchasesUpdated: Purchase Canceled");
} else {
Log.i(TAG, "onPurchasesUpdated: Error");
}
}
};
private void completePurchase(Purchase item) {
purchase = item;
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
runOnUiThread(() -> {
binding.consumeButton.setEnabled(true);
binding.statusText.setText("Purchase Complete");
});
}
18. 消耗产品
当用户点击“消耗”按钮时,需要确保产品被消耗,以便在购买另一次按钮点击之前只能执行一次点击。
示例代码如下:
public void consumePurchase(View view) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult,
@NonNull String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
runOnUiThread(() -> {
binding.consumeButton.setEnabled(false);
binding.statusText.setText("Purchase consumed");
});
}
}
};
billingClient.consumeAsync(consumeParams, listener);
}
19. 恢复以前的购买
为了解决在购买后未消耗就退出应用,重启应用时购买丢失的问题,可以配置一个
QueryPurchasesParams
实例来搜索未消耗的应用内产品,并将其传递给计费客户端的
queryPurchasesAsync()
方法。
示例代码如下:
private void reloadPurchase() {
QueryPurchasesParams queryPurchasesParams = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build();
billingClient.queryPurchasesAsync(
queryPurchasesParams,
purchasesListener
);
}
private final PurchasesResponseListener purchasesListener = new PurchasesResponseListener() {
@Override
public void onQueryPurchasesResponse(@NonNull BillingResult billingResult,
@NonNull List<Purchase> list) {
if (!list.isEmpty()) {
purchase = list.get(0);
binding.consumeButton.setEnabled(true);
} else {
binding.consumeButton.setEnabled(false);
}
}
};
通过以上步骤和代码示例,你可以全面了解 Android 应用内计费的原理和实现方式,并通过示例项目进行实践操作。
Android 应用内计费全解析与实战教程
20. 各步骤流程图
为了更清晰地展示整个 Android 应用内计费的流程,下面给出 mermaid 格式的流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(创建计费客户端):::process
B --> C(连接到 Google Play 计费库):::process
C --> D{连接是否成功}:::decision
D -->|是| E(查询可用产品):::process
D -->|否| C(连接到 Google Play 计费库):::process
E --> F{是否有可用产品}:::decision
F -->|是| G(启动购买流程):::process
F -->|否| E(查询可用产品):::process
G --> H{购买是否成功}:::decision
H -->|是| I(完成购买):::process
H -->|否| G(启动购买流程):::process
I --> J{是否为消耗性产品}:::decision
J -->|是| K(消耗产品):::process
J -->|否| L(确认非消耗性购买):::process
K --> M(恢复以前的购买):::process
L --> M(恢复以前的购买):::process
M --> N([结束]):::startend
21. 关键技术点总结
-
计费客户端的创建与连接
:使用
BillingClient.newBuilder()创建计费客户端,并通过startConnection()方法异步连接到 Google Play 计费库,同时需要实现BillingClientStateListener来处理连接结果和断开情况。 -
产品查询
:通过
QueryProductDetailsParams配置产品信息,调用queryProductDetailsAsync()方法查询可用产品,使用ProductDetailsResponseListener处理查询结果。 -
购买流程
:使用
BillingFlowParams配置购买信息,调用launchBillingFlow()方法启动购买流程,购买结果通过PurchasesUpdatedListener处理。 -
购买完成处理
:根据
Purchase对象的getPurchaseState()方法判断购买状态,对于非消耗性产品需要确认购买,对于消耗性产品需要调用consumeAsync()方法消耗产品。 -
恢复购买
:使用
QueryPurchasesParams配置查询信息,调用queryPurchasesAsync()方法恢复以前的购买,使用PurchasesResponseListener处理查询结果。
22. 常见问题及解决方法
| 问题描述 | 解决方法 |
|---|---|
| 连接到 Google Play 计费库失败 |
检查网络连接,确保应用的包名、签名等信息在 Google Play 控制台配置正确,可在
onBillingServiceDisconnected()
方法中重新尝试连接。
|
| 查询不到可用产品 | 检查产品 ID 是否在 Google Play 控制台正确配置,确保产品已发布且处于可用状态。 |
| 购买流程无法启动 |
检查
BillingFlowParams
配置是否正确,确保
ProductDetail
对象有效。
|
| 购买完成后无法消耗产品 |
检查
ConsumeParams
中的购买令牌是否正确,确保购买状态为已购买。
|
23. 代码优化建议
-
错误处理
:在各个回调方法中,除了简单的日志输出,还可以根据不同的错误码给用户更友好的提示信息。例如,在
onPurchasesUpdated()方法中,根据不同的BillingResponseCode显示不同的错误提示。 -
性能优化
:对于一些频繁调用的方法,如
queryProductDetailsAsync(),可以考虑添加缓存机制,避免重复查询。 -
代码复用
:将一些通用的方法封装,提高代码的复用性。例如,将创建
BillingFlowParams的逻辑封装成一个独立的方法。
24. 总结
通过本文,我们全面了解了 Android 应用内计费的相关知识,包括从创建项目到实现应用内购买的整个流程。从创建
InAppPurchase
项目,到添加必要的库、设计用户界面,再到在 Google Play 控制台配置应用和应用内产品,最后通过代码实现产品查询、购买、消耗和恢复等功能。
同时,我们还通过流程图清晰地展示了整个流程,总结了关键技术点,分析了常见问题及解决方法,并给出了代码优化建议。希望这些内容能帮助开发者更好地实现 Android 应用内计费功能,为应用带来更多的收益。
在实际开发中,开发者可以根据自己的需求对示例代码进行修改和扩展,例如添加更多的产品类型、优化用户界面等。不断实践和探索,才能更好地掌握 Android 应用内计费的技术。
Android应用内计费全解析与实战
超级会员免费看
7077

被折叠的 条评论
为什么被折叠?



