Google內購--封裝版
最近老大提出了一個需求,在應用裡面加一個內購。由於之前沒做過這塊,所以百度一番。網上都是講的使用一大堆的utils、還要加一個aidl檔案。感覺挺麻煩的。最終讓我找到了:com.android.billingclient:billing:1.0 。使用該依賴,解決了一大堆的utils。
好了,開始介紹下使用步驟:
一、程式碼部分
①、在AndroidManifest.xml中新增內購許可權。
<uses-permission android:name="com.android.vending.BILLING" />
這個是必須的。
②、加入依賴。
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<Purchase> 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<String> 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<SkuDetails> 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<Purchase> queryPurchasesInApp() { return queryPurchases(BillingClient.SkuType.INAPP); } /** * 獲取已經訂閱的商品 * @return */ public List<Purchase> queryPurchasesSubs() { return queryPurchases(BillingClient.SkuType.SUBS); } private List<Purchase> 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<Purchase> 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<Purchase> 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<subsSKUS.length) { return subsSKUS[position]; } else { return null; } } /** * 通過序號獲取內購sku * @param position * @return */ public String getInAppSkuByPosition(int position) { if(position>=0&&position<inAppSKUS.length) { return inAppSKUS[position]; } else { return null; } } /** * 通過sku獲取商品型別(訂閱獲取內購) * @param sku * @return inapp內購,subs訂閱 */ private String getSkuType(String sku) { if(Arrays.asList(inAppSKUS).contains(sku)) { return BillingClient.SkuType.INAPP; } else if(Arrays.asList(subsSKUS).contains(sku)) { return BillingClient.SkuType.SUBS; } return null; } /** * 檢測GooglePlay服務是否可用(需要匯入包api "com.google.android.gms:play-services-location:11.8.0",也可以不檢查,跳過這個程式碼) * @param context * @return */ public static boolean isGooglePlayServicesAvailable(Context context) { GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); if(googleApiAvailability!=null) { int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context); return resultCode == ConnectionResult.SUCCESS; } return false; //return true;//不檢查直接跳過 } public GoogleBillingUtil setOnQueryFinishedListener(OnQueryFinishedListener onQueryFinishedListener) { mOnQueryFinishedListener = onQueryFinishedListener; return mGoogleBillingUtil; } public GoogleBillingUtil setOnPurchaseFinishedListener(OnPurchaseFinishedListener onPurchaseFinishedListener) { mOnPurchaseFinishedListener = onPurchaseFinishedListener; return mGoogleBillingUtil; } public OnStartSetupFinishedListener getOnStartSetupFinishedListener() { return mOnStartSetupFinishedListener; } public GoogleBillingUtil setOnStartSetupFinishedListener(OnStartSetupFinishedListener onStartSetupFinishedListener) { mOnStartSetupFinishedListener = onStartSetupFinishedListener; return mGoogleBillingUtil; } /** * 本工具查詢回撥介面 */ public interface OnQueryFinishedListener{ //Inapp和sub都走這個介面查詢的時候一定要判斷skuType public void onQuerySuccess(String skuType, List<SkuDetails> list); public void onQueryFail(int responseCode); public void onQueryError(); } /** * 本工具購買回調介面(內購與訂閱都走這介面) */ public interface OnPurchaseFinishedListener{ public void onPurchaseSuccess(List<Purchase> 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<ResolveInfo> 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<SkuDetails> 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<Purchase> 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<Purchase> queryPurchasesInApp() 獲取已經內購的商品列表
10、public List<Purchase> 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