最近老大提出了一个需求,在应用里面加一个内购。由于之前没做过这块,所以百度一番。网上都是讲的使用一大堆的utils、还要加一个aidl文件。感觉挺麻烦的。最终让我找到了:com.android.billingclient:billing:1.0 。使用该依赖,解决了一大堆的utils。
好了,开始介绍下使用步骤:
一、代码部分
①、在AndroidManifest.xml中添加内购权限。
这个是必须的。
②、加入依赖。
compile 'com.android.billingclient:billing:1.0'
//如果用到isGooglePlayServicesAvailable方法需要导入这个包,这个方法也可以去掉。
implementation 'com.google.android.gms:play-services-basement:11.8.0'
③、代码封装:
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 对isGooglePlayServicesAvailable方法进行了说明,因为这个方法是要导入一个包才能使用的。
* api "com.google.android.gms:play-services-location:11.8.0"也可以不用
*/
@SuppressWarnings("ALL")
public class GoogleBillingUtil {
private static final String TAG = "```GoogleBillingUtil";
private static final boolean IS_DEBUG = true;
//googleplay应用内商品栏,添加商品后得到
private String[] inAppSKUS = new String[]{"","",""};//内购ID,必填
private String[] subsSKUS = new String[]{"","",""};//订阅ID,必填
public static final String BILLING_TYPE_INAPP = BillingClient.SkuType.INAPP;//内购
public static final String BILLING_TYPE_SUBS = BillingClient.SkuType.SUBS;//订阅
private static BillingClient mBillingClient;
private static BillingClient.Builder builder ;
private static OnPurchaseFinishedListener mOnPurchaseFinishedListener;
private static OnStartSetupFinishedListener mOnStartSetupFinishedListener ;
private static OnQueryFinishedListener mOnQueryFinishedListener;
private boolean isAutoConsumeAsync = true;//是否在购买成功后自动消耗商品
private static final GoogleBillingUtil mGoogleBillingUtil = new GoogleBillingUtil() ;
private GoogleBillingUtil()
{
}
public static GoogleBillingUtil getInstance()
{
cleanListener();
return mGoogleBillingUtil;
}
public GoogleBillingUtil build(Context context)
{
if(mBillingClient==null)
{
synchronized (mGoogleBillingUtil)
{
if(mBillingClient==null)
{
if(isGooglePlayServicesAvailable(context))
{
builder = BillingClient.newBuilder(context);
mBillingClient = builder.setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener()).build();
}
else
{
if(IS_DEBUG)
{
log("警告:GooglePlay服务处于不可用状态,请检查");
}
if(mOnStartSetupFinishedListener!=null)
{
mOnStartSetupFinishedListener.onSetupError();
}
}
}
else
{
builder.setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener());
}
}
}
else
{
builder.setListener(mGoogleBillingUtil.new MyPurchasesUpdatedListener());
}
synchronized (mGoogleBillingUtil)
{
if(mGoogleBillingUtil.startConnection())
{
mGoogleBillingUtil.queryInventoryInApp();
mGoogleBillingUtil.queryInventorySubs();
mGoogleBillingUtil.queryPurchasesInApp();
}
}
return mGoogleBillingUtil;
}
public boolean startConnection()
{
if(mBillingClient==null)
{
log("初始化失败:mBillingClient==null");
return false;
}
if(!mBillingClient.isReady())
{
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode) {
if (billingResponseCode == BillingClient.BillingResponse.OK) {
queryInventoryInApp();
queryInventorySubs();
queryPurchasesInApp();
if(mOnStartSetupFinishedListener!=null)
{
mOnStartSetupFinishedListener.onSetupSuccess();
}
}
else
{
log("初始化失败:onSetupFail:code="+billingResponseCode);
if(mOnStartSetupFinishedListener!=null)
{
mOnStartSetupFinishedListener.onSetupFail(billingResponseCode);
}
}
}
@Override
public void onBillingServiceDisconnected() {
if(mOnStartSetupFinishedListener!=null)
{
mOnStartSetupFinishedListener.onSetupError();
}
log("初始化失败:onBillingServiceDisconnected");
}
});
return false;
}
else
{
return true;
}
}
/**
* Google购买商品回调接口(订阅和内购都走这个接口)
*/
private class MyPurchasesUpdatedListener implements PurchasesUpdatedListener
{
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List list) {
if(mOnPurchaseFinishedListener==null)
{
if(IS_DEBUG)
{
log("警告:接收到购买回调,但购买商品接口为Null,请设置购买接口。eg:setOnPurchaseFinishedListener()");
}
return ;
}
if(responseCode== BillingClient.BillingResponse.OK&&list!=null)
{
if(isAutoConsumeAsync)
{
//消耗商品
for(Purchase purchase:list)
{
String sku = purchase.getSku();
if(sku!=null)
{
String skuType = getSkuType(sku);
if(skuType!=null)
{
if(skuType.equals(BillingClient.SkuType.INAPP))
{
consumeAsync(purchase.getPurchaseToken());
}
}
}
}
}
mOnPurchaseFinishedListener.onPurchaseSuccess(list);
}
else
{
mOnPurchaseFinishedListener.onPurchaseFail(responseCode);
}
}
}
/**
* 查询内购商品信息
*/
public void queryInventoryInApp()
{
queryInventory(BillingClient.SkuType.INAPP);
}
/**
* 查询订阅商品信息
*/
public void queryInventorySubs()
{
queryInventory(BillingClient.SkuType.SUBS);
}
private void queryInventory(final String skuType) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (mBillingClient == null)
{
if(mOnQueryFinishedListener!=null)
{
mOnQueryFinishedListener.onQueryError();
}
return ;
}
ArrayList skuList = new ArrayList<>();
if(skuType.equals(BillingClient.SkuType.INAPP))
{
Collections.addAll(skuList, inAppSKUS);
}
else if(skuType.equals(BillingClient.SkuType.SUBS))
{
Collections.addAll(skuList, subsSKUS);
}
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(skuType);
mBillingClient.querySkuDetailsAsync(params.build(),new MySkuDetailsResponseListener(mOnQueryFinishedListener,skuType));
}
};
executeServiceRequest(runnable);
}
/**
* Google查询商品信息回调接口
*/
private class MySkuDetailsResponseListener implements SkuDetailsResponseListener
{
private OnQueryFinishedListener mOnQueryFinishedListener ;
private String skuType ;
public MySkuDetailsResponseListener(OnQueryFinishedListener onQueryFinishedListener,String skuType) {
mOnQueryFinishedListener = onQueryFinishedListener;
this.skuType = skuType;
}
@Override
public void onSkuDetailsResponse(int responseCode , List list) {
if(mOnQueryFinishedListener==null)
{
if(IS_DEBUG) {
log("警告:接收到查询商品回调,但查询商品接口为Null,请设置购买接口。eg:setOnQueryFinishedListener()");
}
return ;
}
if(responseCode== BillingClient.BillingResponse.OK&&list!=null)
{
mOnQueryFinishedListener.onQuerySuccess(skuType,list);
}
else
{
mOnQueryFinishedListener.onQueryFail(responseCode);
}
}
}
/**
* 发起内购
* @param skuId
* @return
*/
public void purchaseInApp(Activity activity,String skuId)
{
purchase(activity,skuId, BillingClient.SkuType.INAPP);
}
/**
* 发起订阅
* @param skuId
* @return
*/
public void purchaseSubs(Activity activity,String skuId)
{
purchase(activity,skuId, BillingClient.SkuType.SUBS);
}
private void purchase(Activity activity,final String skuId,final String skuType)
{
if(mBillingClient==null)
{
if(mOnPurchaseFinishedListener!=null)
{
mOnPurchaseFinishedListener.onPurchaseError();
}
return ;
}
if(startConnection())
{
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSku(skuId)
.setType(skuType)
.build();
mBillingClient.launchBillingFlow(activity,flowParams);
}
else
{
if(mOnPurchaseFinishedListener!=null)
{
mOnPurchaseFinishedListener.onPurchaseError();
}
}
}
/**
* 消耗商品
* @param purchaseToken
*/
public void consumeAsync(String purchaseToken)
{
if(mBillingClient==null)
{
return ;
}
mBillingClient.consumeAsync(purchaseToken, new MyConsumeResponseListener());
}
/**
* Googlg消耗商品回调
*/
private class MyConsumeResponseListener implements ConsumeResponseListener
{
@Override
public void onConsumeResponse(int responseCode, String s) {
if (responseCode == BillingClient.BillingResponse.OK) {
}
}
}
/**
* 获取已经内购的商品
* @return
*/
public List queryPurchasesInApp()
{
return queryPurchases(BillingClient.SkuType.INAPP);
}
/**
* 获取已经订阅的商品
* @return
*/
public List queryPurchasesSubs()
{
return queryPurchases(BillingClient.SkuType.SUBS);
}
private List queryPurchases(String skuType)
{
if(mBillingClient==null)
{
return null;
}
if(!mBillingClient.isReady())
{
startConnection();
}
else
{
Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(skuType);
if(purchasesResult!=null)
{
if(purchasesResult.getResponseCode()== BillingClient.BillingResponse.OK)
{
List purchaseList = purchasesResult.getPurchasesList();
if(isAutoConsumeAsync)
{
if(purchaseList!=null)
{
for(Purchase purchase:purchaseList)
{
if(skuType.equals(BillingClient.SkuType.INAPP))
{
consumeAsync(purchase.getPurchaseToken());
}
}
}
}
return purchaseList;
}
}
}
return null;
}
/**
* 获取有效订阅的数量
* @return -1查询失败,0没有有效订阅,>0具有有效的订阅
*/
public int getPurchasesSizeSubs()
{
List list = queryPurchasesSubs();
if(list!=null)
{
return list.size();
}
return -1;
}
/**
* 通过sku获取订阅商品序号
* @param sku
* @return
*/
public int getSubsPositionBySku(String sku)
{
return getPositionBySku(sku, BillingClient.SkuType.SUBS);
}
/**
* 通过sku获取内购商品序号
* @param sku
* @return 成功返回需要 失败返回-1
*/
public int getInAppPositionBySku(String sku)
{
return getPositionBySku(sku, BillingClient.SkuType.INAPP);
}
private int getPositionBySku(String sku,String skuType)
{
if(skuType.equals(BillingClient.SkuType.INAPP))
{
int i = 0;
for(String s:inAppSKUS)
{
if(s.equals(sku))
{
return i;
}
i++;
}
}
else if(skuType.equals(BillingClient.SkuType.SUBS))
{
int i = 0;
for(String s:subsSKUS)
{
if(s.equals(sku))
{
return i;
}
i++;
}
}
return -1;
}
private void executeServiceRequest(final Runnable runnable)
{
if(startConnection())
{
runnable.run();
}
}
/**
* 通过序号获取订阅sku
* @param position
* @return
*/
public String getSubsSkuByPosition(int position)
{
if(position>=0&&position=0&&position list);
public void onQueryFail(int responseCode);
public void onQueryError();
}
/**
* 本工具购买回调接口(内购与订阅都走这接口)
*/
public interface OnPurchaseFinishedListener{
public void onPurchaseSuccess(List list);
public void onPurchaseFail(int responseCode);
public void onPurchaseError();
}
/**
* google服务启动接口
*/
public interface OnStartSetupFinishedListener{
public void onSetupSuccess();
public void onSetupFail(int responseCode);
public void onSetupError();
}
public boolean isReady() {
return mBillingClient!=null&&mBillingClient.isReady();
}
public boolean isAutoConsumeAsync()
{
return isAutoConsumeAsync;
}
public void setIsAutoConsumeAsync(boolean isAutoConsumeAsync)
{
this.isAutoConsumeAsync= isAutoConsumeAsync;
}
/**
* 清除所有监听器,防止内存泄漏
* 如果有多个页面使用了支付,需要确保上个页面的cleanListener在下一个页面的GoogleBillingUtil.getInstance()前使用。
* 所以不建议放在onDestory里调用
*/
public static void cleanListener()
{
mOnPurchaseFinishedListener = null;
mOnQueryFinishedListener = null;
mOnStartSetupFinishedListener = null;
if(builder!=null)
{
builder.setListener(null);
}
}
/**
* 断开连接google服务
* 注意!!!一般情况不建议调用该方法,让google保留连接是最好的选择。
*/
public static void endConnection()
{
//注意!!!一般情况不建议调用该方法,让google保留连接是最好的选择。
if(mBillingClient!=null)
{
if(mBillingClient.isReady())
{
mBillingClient.endConnection();
mBillingClient = null;
}
}
}
private static void log(String msg)
{
if(IS_DEBUG)
{
Log.e(TAG,msg);
}
}
/**
* 是否支持内购
*/
public boolean isIabServiceAvailable(Context context)
{
final PackageManager packageManager = context.getPackageManager();
List list = packageManager.queryIntentServices(getBindServiceIntent(), 0);
return list != null && list.size() > 0;
}
private Intent getBindServiceIntent()
{
Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
intent.setPackage("com.android.vending");
return intent;
}
}
代码可以直接拿去用的,不需要填入key值。
④、代码示例:
private GoogleBillingUtil googleBillingUtil = null;
private MyOnPurchaseFinishedListener mOnPurchaseFinishedListener = new MyOnPurchaseFinishedListener();//购买回调接口
private MyOnQueryFinishedListener mOnQueryFinishedListener = new MyOnQueryFinishedListener();//查询回调接口
private MyOnStartSetupFinishedListener mOnStartSetupFinishedListener = new OnStartSetupFinishedListener();//启动结果回调接口
4.1:创建示例,需要在build之前设置回调接口,接口可以选择性设置
@Override
protected void onStart() {
super.onStart();
/*
建议放在onStart里面初始化,因为你可能在别的页面与谷歌服务断开了连接或者设置了接口,所以当一个页面打开的时候,
你应该重新检测连接是否正常(如果断开则重新连接)并重新设置接口
*/
googleBillingUtil = GoogleBillingUtil.getInstance()
.setOnPurchaseFinishedListener(mOnPurchaseFinishedListener)
.setOnQueryFinishedListener(mOnQueryFinishedListener)
.setOnStartSetupFinishedListener(mOnStartSetupFinishedListener)
.build();
}
4.2:发起内购或者订阅
public void queryInventoryInApp() 查询内购商品信息列表
public void queryInventorySubs() 查询订阅商品信息列表
public void purchaseInApp(Activity activity,String skuId) 发起内购
public void purchaseSubs(Activity activity,String skuId) 发起订阅
4.3:清除监听
//如果下个页面或者上个页面没有使用到googleBuillingUtil.getInstance(),那么就需要在finish或者startActivity之前调用cleanListener()方法,来清除接口。
//可以尝试这样
@Override
public void onBackPressed() {
GoogleBillingUtil.cleanListener();
super.onBackPressed();
}
//返回键点击
public void onBackClick()
{
GoogleBillingUtil.cleanListener();
this.finish();
}
//如果只使用一次googleBuillingUtil,可以选择使用endConnection()方法断开google服务的连接,下次使用重新连接。
4.4:接口说明
//查询商品信息回调接口
private class MyOnQueryFinishedListener implements GoogleBillingUtil.OnQueryFinishedListener
{
@Override
public void onQuerySuccess(String skuType,List list) {
//查询成功,返回商品列表,
//skuDetails.getPrice()获得价格(文本)
//skuDetails.getType()获得类型 sub或者inapp,因为sub和inapp的查询结果都走这里,所以需要判断。
//googleBillingUtil.getSubsPositionBySku(skuDetails.getSku())获得当前subs sku的序号
//googleBillingUtil.getInAppPositionBySku(skuDetails.getSku())获得当前inapp suk的序号
}
@Override
public void onQueryFail(int responseCode) {
//查询失败
}
@Override
public void onQueryError() {
//查询错误
}
}
//服务初始化结果回调接口
private class MyOnStartSetupFinishedListener implements GoogleBillingUtil.OnStartSetupFinishedListener
{
//...;
}
//购买商品回调接口
private class MyOnPurchaseFinishedListener implements GoogleBillingUtil.OnPurchaseFinishedListener
{
@Override
public void onPurchaseSuccess(List list) {
//内购或者订阅成功,可以通过purchase.getSku()获取suk进而来判断是哪个商品
}
@Override
public void onPurchaseFail(int responseCode) {
}
@Override
public void onPurchError() {
}
}
4.5:api说明:
//初始化
1、public static GoogleBillingUtil getInstance() 获取单实例
2、public GoogleBillingUtil build()
创建内购实例,连接谷歌支付服务(如果未创建、未连接),并查询商品信息列表。
如果默认自动消耗已内购但未被消耗的商品,可以通过设置isAutoConsumeAsync改变。
3、public void startConnection()
连接谷歌支付服务(一般情况下不需要手动调用,build的时候已经调用了)
//查询、购买与消耗
4、public void queryInventoryInApp() 查询内购商品信息列表
5、public void queryInventorySubs() 查询订阅商品信息列表
6、public void purchaseInApp(Activity activity,String skuId) 发起内购
7、public void purchaseSubs(Activity activity,String skuId) 发起订阅
8、public void consumeAsync(String purchaseToken) 消耗商品(一般情况下不需要手动调用,内购的时候自动调用了)
//购买历史、有效订阅数
9、public List queryPurchasesInApp() 获取已经内购的商品列表
10、public List queryPurchasesSubs() 获取已经订阅的商品列表
11、public int getPurchasesSizeSubs() 获取有效订阅的数量(-1查询失败,0没有有效订阅,>0具有有效的订阅)
//便捷工具
12、public int getSubsPositionBySku(String sku) 通过sku获取订阅商品序号
13、public int getInAppPositionBySku(String sku) 通过sku获取内购商品序号
14、public String getSubsSkuByPosition(int position) 通过序号获取订阅sku
15、public String getInAppSkuByPosition(int position) 通过序号获取内购sku
16、private String getSkuType(String sku) 通过sku获取商品类型(订阅获取内购)inapp内购,subs订阅
//接口设置
17、public GoogleBillingUtil setOnPurchaseFinishedListener(OnPurchaseFinishedListener onPurchaseFinishedListener) 购买回调接口
18、public GoogleBillingUtil setOnQueryFinishedListener(OnQueryFinishedListener onQueryFinishedListener) 商品信息查询接口
19、public GoogleBillingUtil setOnStartSetupFinishedListener(OnStartSetupFinishedListener onStartSetupFinishedListener) 服务初始化结果回调接口
//其他、内存
20、public void setIsAutoConsumeAsync(boolean isAutoConsumeAsync) 设置是否自动消耗商品
21、public static void cleanListener() 清除所有监听器,避免内存泄漏,回调错乱等问题。
22、public static void endConnection() 断开连接google服务(一般情况不建议调用该方法,让google保留连接是最好的选择。)
二、googleplay操作部分:
当你把代码添加完成后,可以在代码中添加一个skuid :(由字母和数字组成,例如weather_54).private String[] inAppSKUS = new String[]{"weather_54","",""},这个要与googlePlay应用内商品栏的添加商品一栏对应。然后打包apk.
①、上传应用,应用上传部分这里不做详细讲解。可以先上传一个内部测试版本。内部测试版本支付不会扣除金额。如下图所示:
我这里上传的是alpha测试版本,可以添加测试人员。当完成apk上传之后,会生成一个测试版本的googleplay下载地址。
②、应用内商品
当你的应用存在需要google登录或者其他使用到签名的位置的话,记住:千万不要选择应用签名!千万不要选择应用签名!千万不要选择应用签名!。它会覆盖你的签名。而且不可逆操作。
需要你填写一些信息,例如银行卡之类的。这里公司已经填好了。
③、添加商品:
还记得那个skuid么,对。就是这个Id名称。
④、开始填写定价内容:
红框里面的内容可以更改。
好了。保存更改后。就可以进行测试了。如果你的代码以及googleplay应用都ok的话。点击支付,就可以看到如下图所示:
文章就到这里了。有疑问随时可以@我~~~~thanks