112

【移动开发】Checkout开源库源码解析

 6 years ago
source link: http://mp.weixin.qq.com/s/otcgxbimR-s_zXs4w5U-Pw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【移动开发】Checkout开源库源码解析

Original 三七互娱技术中心 三七互娱技术中心 2018-01-05 04:00 Posted on

1.功能介绍

1.1Checkout是什么

Checkout是Android In-App Billing API(v3 +)的一个封装库。

In-App Billing 是一项 Google Play提供的内购服务,可让我们在自己的应用内出售数字内容。我们可以使用该服务来出售众多内容,包括可下载内容(例如媒体文件或照片)和虚拟内容(例如游戏关卡或魔药、高级服务和功能,等等)。

Checkout的主要目标是尽可能简单直接地集成应用内产品:开发人员不应该花太多时间来实现乏味的应用内结算API,而应该关注更重要的事情:他们的应用。

Checkout的github地址是:https://github.com/serso/android-checkout

1.2Checkout解决的问题

  • Activity被销毁时如何取消所有的billing请求?

  • 如何在后台查询购买信息?

  • 如何验证购买?

  • 如何使用continuationToken来加载用户已购买的商品项以及商品项的信息[接口限制每次请求只会返回20个商品项]

  • 如何使用最少示例代码增加储值功能?

1.3结算流程

Image

2.总体设计

2.1总体设计图

Image

2.2核心类的概念

Billing: Checkout的核心类,实现了Android's Billing API。主要负责:

  • Billing Service的连接建立和断开

  • 执行结算请求

  • 缓存请求结果

  • 创建Checkout对象

Request: 表示Billing结算请求的实体类,具体实现类有BillingSupportedRequest,GetPurchaseHistoryRequest,GetPurchasesRequest,ChangePurchaseRequest,ConsumePurchaseRequest,GetSkuDetailsRequest,PurchaseRequest,分别代表具体的请求操作。

OnConnectedServiceRunnable: Request的包装类,实现了RequestRunnable接口,核心方法是run()

Checkout: Billing类的帮助类,维护了Billing实例,用于主线程中,生命周期需与activity/fragment/service绑定,对应的子类有FragmentCheckout,ActivityCheckout和CustomUiCheckout等。

PendingRequests:该类表示待处理的请求,维护了一个RequestRunnable的集合,所有的请求顺序执行。

Configuration: 表示Billing结算的配置接口,需要实现Configuration接口自定义配置。

Cache:表示缓存的接口,具体实现类为MapCache。

ServiceConnector: 连接服务的接口,默认实现类为DefaultServiceConnector,负责Google play app的绑定和解绑。

Purchase: 表示购买信息的类,成员变量与getBuyIntent()返回的INAPPDATASIGNATURE数据的 JSON 字段对应,也就是说Purchase都是根据这个JSON字段的内容定义的。

Purchases: 表示购买信息列表的类。维护了一个Purchase集合。

PurchaseFlow: 表示从用户请求购买之时起直到购买完成为止的一个购买流程的类

PurchaseVerifier: 验证购买接口,实现类为BasePurchaseVerifier,该类为抽象类,可继承它实现自己的验证类。验证过程通常在后台服务器进行。

Inventory: 用于加载产品,SKU和购买相关信息的类,其生命周期与Checkout的相关。子类有FallingBackInventory,CheckoutInventory和RobotmediaInventory。

3.request流程图

Image

4.详细设计

4.1UML类关系图

Image

4.2 核心类解析

4.2.1 Checkout.java

Checkout是一个工具类,主要是对Billing结算流程的一个封装和对Inventory的处理。根据Context环境的不同,构建一个Checkout类的非抽象子类(FragmentCheckoutActivityCheckoutCustomUiCheckout)对象,启动结算流程。
注意:Checkout要与activity/fragment/service等生命周期相绑定,在onDestroy()中调用mCheckout.stop(),取消待执行的请求,解绑service.

1.主要成员变量

  • Billing mBilling主类实例

  • Billing.Requests mRequests 代表各种结算方法的对象。

2.构造对象

根据以下几个静态方法构造出子类实例,对应ui/activity/fragment/service,并将Billing作为参数传进来。



public static UiCheckout forUi(@Nonnull IntentStarter intentStarter, @Nonnull Object tag, @Nonnull Billing billing);
public static UiCheckout forFragment(@Nonnull Fragment fragment, @Nonnull Billing billing);
public static ActivityCheckout forActivity(@Nonnull Activity activity, @Nonnull Billing billing);
public static Checkout forService(@Nonnull Service service, @Nonnull Billing billing);

2. 主要方法

作为Checkout库的调用入口,创建出 Checkout 以后,调用 start 方法



public void start() {
   start(null);
}
public void start(@Nullable final Listener listener) {
   Check.isMainThread();
   synchronized (mLock) {
       Check.isFalse(mState == State.STARTED, "Already started");
       Check.isNull(mRequests, "Already started");
       mState = State.STARTED;
       mBilling.onCheckoutStarted();
       mRequests = mBilling.getRequests(mTag);
   }
   whenReady(listener == null ? new EmptyListener() {} : listener);
}

start有两重载方法,无参方法调用带有listener的方法,由第二个方法可见,主要是通过mBilling获取mRequests,然后调用whenReady()方法。



public void whenReady(@Nonnull final Listener listener) {
   Check.isMainThread();
   synchronized (mLock) {
       Check.isNotNull(mRequests);
       final Billing.Requests requests = mRequests;
       @Nonnull
       final Set<String> loadingProducts = new HashSet<>(ProductTypes.ALL);
       for (final String product : ProductTypes.ALL) {
           requests.isBillingSupported(product, new RequestListener<Object>() {
               private void onBillingSupported(boolean supported) {
                   listener.onReady(requests, product, supported);
                   loadingProducts.remove(product);
                   if (loadingProducts.isEmpty()) {
                       listener.onReady(requests);
                   }
               }
               @Override
               public void onSuccess(@Nonnull Object result) {
                   onBillingSupported(true);
               }
               @Override
               public void onError(int response, @Nonnull Exception e) {
                   onBillingSupported(false);
               }
           });
       }
   }
}

当调用stop()时,将Request队列中的请求取消,而mBilling.onCheckoutStopped();主要做的事是断开与Google Play服务的连接。

3.使用流程

Image

在分析Billing类之前,我们先分析Billing中几个成员变量对应的类。

4.2.2 Request.java

表示Billing请求的实体类,该类为抽象类,具体实现类有BillingSupportedRequestGetSkuDetailsRequest
ConsumePurchaseRequest等,子类需要实现抽象方法



abstract void start(@Nonnull IInAppBillingService service, @Nonnull String packageName)
       throws RemoteException, RequestException;
abstract String getCacheKey();

子类的start()调用service相关的Billing API方法。

主要成员变量

  • int mApiVersion In-app Billing的api版本

  • int mId 作为请求独一无二的id

  • RequestType mType 请求的类型

  • Object mTag 标签

  • RequestListener<R> mListener 请求的回调接口

4.2.3 OnConnectedServiceRunnable

该类实现了RequestRunnable接口,主要是对Request的行为进行包装,增加缓存检查和异常处理

1.成员变量

  • Request mRequest 被包装的请求

2.核心方法



   @Override
   public boolean run() {
       final Request localRequest = getRequest();
       if (localRequest == null) {
           // request was cancelled => finish here
           return true;
       }
       if (checkCache(localRequest)) return true;
       // request is alive, let's check the service state
       final State localState;
       final IInAppBillingService localService;
       synchronized (mLock) {
           localState = mState;
           localService = mService;
       }
       if (localState == State.CONNECTED) {
           Check.isNotNull(localService);
           // service is connected, let's start request
           try {
               localRequest.start(localService, mContext.getPackageName());
           } catch (RemoteException | RuntimeException | RequestException e) {
               localRequest.onError(e);
           }
       } else {
           // service is not connected, let's check why
           if (localState != State.FAILED) {
               // service was disconnected
               connect();
               return false;
           } else {
               // service was not connected in the first place => can't do anything, aborting the request
               localRequest.onError(ResponseCodes.SERVICE_NOT_CONNECTED);
           }
       }
       return true;
   }

该方法的逻辑也很清楚,先检查是否有缓存,如果有缓存,直接返回(注意:checkCache()会将缓存返回给request),否则检查状态,如果处于已连接状态,执行request的start(),否则尝试建立起连接。

4.2.4 PendingRequests

该类表示待处理的请求,并实现了Runnable接口,其维护了一个RequestRunnable列表mList,所有请求需添加至mList才能被处理。核心方法为run(),通过循环取出RequestRunnable,并执行RequestRunnablerun()方法。

4.2.5 Requests

该类实现了BillingRequests接口,Requests作为Billing的内部类,持有Billing实例的引用,并调用了其实例方法。BillingRequests定义一系列关于Billing api相关的方法

4.2.6 Configuration

Billing API的配置接口,定义了如下方法

String getPublicKey();获取公钥,用于购买过程中的签名。

Cache getCache();获取缓存对象

PurchaseVerifier getPurchaseVerifier();返回PurchaseVerifier

Inventory getFallbackInventory(@Nonnull Checkout checkout, @Nonnull Executor onLoadExecutor);返回后备库存,用于恢复购买

boolean isAutoConnect();是否自动连接



4.2.7 StaticConfiguration

该类可对其他Configuration进行包装,得到其mPublicKeymPurchaseVerifier的引用。StaticConfiguration实现了Configuration的方法。一般情况下,我们需要实现自己的Configuration

1.成员变量

  • Configuration mOriginal 原始的Configuration

  • String mPublicKey;公钥字符串

  • PurchaseVerifier mPurchaseVerifier 验证购买类对象

4.2.8 DefaultConfiguration

实现Configuration部分方法的类,该类通过newCache()获取缓存对象,通过newPurchaseVerifier()获取购买验证对象,isAutoConnect()直接返回true。而getFallbackInventory()则返回null,其子类需要实现getPublicKey()

4.2.9 Cache

缓存接口,代表了一个可以获取请求结果,存储请求结果的缓存。

1.主要方法

Entry get(Key key); 通过 key 获取请求的缓存实体

void put(Key key, Entry entry); 存入一个请求的缓存实体

void init();初始化

void remove(Key key); 移除指定的缓存实体

void removeAll(int type); 清除某一类型的缓存实体

void clear(); 清空缓存

2.代表键实体的内部类Key

成员变量

  • int type类型

  • String key键值字符串

2.代表缓存实体的内部类Entry

成员变量

  • Object data 缓存的对象

  • long expiresAt缓存到期时间

4.2.10 MapCache

Cache接口的实现类,通过维护一个Map<Key, Entry> mMap对象,实现了Cache的缓存功能。

4.2.11 ConcurrentCache

Cache接口的实现类,该类对其他Cache实现类进行包装,通过synchronized同步锁达到线程安全的效果

4.2.12 SafeCache

该类对Cache接口的实现类,该类对其他Cache实现类进行包装,捕获异常。

4.2.13 DefaultServiceConnector

该类实现了ServiceConnector接口,实现了connect()disconnect(),用于处理服务建立与断开。DefaultServiceConnector持有Billing对象的引用。

1.成员变量

ServiceConnection mConnection ServiceConnection实例,当建立连接后,会调用Billing的setService()



   private final ServiceConnection mConnection = new ServiceConnection() {
       @Override
       public void onServiceDisconnected(ComponentName name) {
           setService(null, false);
       }
       @Override
       public void onServiceConnected(ComponentName name,
                                      IBinder service) {
           setService(IInAppBillingService.Stub.asInterface(service), true);
       }
   };

2.实现方法



   @Override
   public boolean connect() {
       try {
           final Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
           intent.setPackage("com.android.vending");
           return mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
       } catch (IllegalArgumentException e) {
           // some devices throw IllegalArgumentException (Service Intent must be explicit)
           // even though we set package name explicitly. Let's not crash the app and catch
           // such exceptions here, the billing on such devices will not work.
           return false;
       } catch (NullPointerException e) {
           // Meizu M3s phones might throw an NPE in Context#bindService (Attempt to read from field 'int com.android.server.am.ProcessRecord.uid' on a null object reference).
           // As in-app purchases don't work if connection to the billing service can't be
           // established let's not crash and allow users to continue using the app
           return false;
       }
   }
   @Override
   public void disconnect() {
       mContext.unbindService(mConnection);
   }



4.2.14 Billing.java

接下来重点分析Billing类。作为Checkout的核心类,Billing封装了结算流程的主要逻辑。

1.构造对象

为避免与Google Play app重复连接,所以只能有一个Billing对象,所以我们采取在application中构建单例的形式。



@Nonnull
private final Billing mBilling = new Billing(this, new Conguration());

2.主要成员变量

  • StaticConfiguration mConfiguration 配置类,主要是对publicKey,Cache等配置

  • ConcurrentCache mCache 缓存类,代表了一个可以获取请求结果,存储请求结果的缓存

  • PendingRequests mPendingRequests 表示待执行的请求队列。

  • BillingRequests mRequests 定义了所有的billing结算方法的接口

  • IInAppBillingService mService billing服务实例对象

  • State mState 表示结算过程中的状态

  • CancellableExecutor mMainThread表示主线程,用于处理服务连接建立和取消的过程。

  • Executor mBackground 表示子线程,用于处理结算流程。

  • ServiceConnector mConnector 服务连接类。

3.state状态切换流程

state表示连接过程中的状态的枚举类,具有INITIAL,CONNECTING,
CONNECTED,DISCONNECTING,DISCONNECTED,FAILED6个状态

state的转换方式需要按照下图:

Image

通过setState()方法改变State状态,如果传入的值为CONNECTED,则开始执行Request队列



void setState(@Nonnull State newState) {
   synchronized (mLock) {
       if (mState == newState) {
           return;
       }
       Check.isTrue(sPreviousStates.get(newState).contains(mState), "State " + newState + " can't come right after " + mState + " state");
       mState = newState;
       switch (mState) {
           case DISCONNECTING:
               // as we can jump directly from DISCONNECTING to CONNECTED state let's remove
               // the listener here instead of in DISCONNECTED state. That also will protect
               // us from getting in the following trap: CONNECTED->DISCONNECTING->CONNECTING->FAILED
               mPlayStoreBroadcastReceiver.removeListener(mPlayStoreListener);
               break;
           case CONNECTED:
               // CONNECTED is the only state when we know for sure that Play Store is available.
               // Registering the listener here also means that it should be never registered
               // in the FAILED state
               mPlayStoreBroadcastReceiver.addListener(mPlayStoreListener);
               executePendingRequests();
               break;
           case FAILED:
               // the play store listener should not be registered in the receiver in case of
               // failure as FAILED state can't occur after CONNECTED
               Check.isTrue(!mPlayStoreBroadcastReceiver.contains(mPlayStoreListener), "Leaking the listener");
               mMainThread.execute(new Runnable() {
                   @Override
                   public void run() {
                       mPendingRequests.onConnectionFailed();
                   }
               });
               break;
       }
   }
}

4.建立连接



public void connect() {
   synchronized (mLock) {
       if (mState == State.CONNECTED) {
           executePendingRequests();
           return;
       }
       if (mState == State.CONNECTING) {
           return;
       }
       if (mConfiguration.isAutoConnect() && mCheckoutCount <= 0) {
           warning("Auto connection feature is turned on. There is no need in calling Billing.connect() manually. See Billing.Configuration.isAutoConnect");
       }
       setState(State.CONNECTING);
       mMainThread.execute(new Runnable() {
           @Override
           public void run() {
               connectOnMainThread();
           }
       });
   }
}

通过上面看出,connect()方法主要是设置stateCONNECTING,并通过mMainThread调用了connectOnMainThread()方法,该方法又调用了mConnectorconnect()方法,并返回mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)的结果。

需要注意的是,每次执行请求流程时,connect()都会被调用,确保服务是连接上的。

5.执行request

当建立起连接后,state被置为CONNECTED,并调用executePendingRequests()方法,该方法通过一个单线程的线程池,执行mPendingRequestsrun()方法,循环的取出request(实际上是RequestRunnable)并执行。



private void executePendingRequests() {
   mBackground.execute(mPendingRequests);
}

当开启某一类型的请求时,Billing类中的runWhenConnected()会被调用,这个方法会调用到connect(),并最终执行executePendingRequests()方法。
接着我们来重点看一下这个方法,这是个重载方法。



private int runWhenConnected(@Nonnull Request request, @Nullable Object tag) {
   return runWhenConnected(request, null, tag);
}
<R> int runWhenConnected(@Nonnull Request<R> request, @Nullable RequestListener<R> listener, @Nullable Object tag) {
   if (listener != null) {
       if (mCache.hasCache()) {
           listener = new CachingRequestListener<>(request, listener);
       }
       request.setListener(listener);
   }
   if (tag != null) {
       request.setTag(tag);
   }
   mPendingRequests.add(onConnectedService(request));
   connect();
   return request.getId();
}

可以看出runWhenConnected()做的事情就是传进一个request对象,并将其加到mPendingRequests中,然后在connect()中执行request任务。

6.request执行过程中的调用栈

我们来了解一下一个请求执行的过程,以获取购买的商品为例

Image

7.断开连接



public void disconnect() {
   synchronized (mLock) {
       if (mState == State.DISCONNECTED || mState == State.DISCONNECTING || mState == State.INITIAL) {
           return;
       }
       if (mState == State.FAILED) {
           // it would be strange to change the state from FAILED to DISCONNECTING/DISCONNECTED,
           // thus, just cancelling all pending the requested here and returning without updating
           // the state
           mPendingRequests.cancelAll();
           return;
       }
       if (mState == State.CONNECTED) {
           setState(State.DISCONNECTING);
           mMainThread.execute(new Runnable() {
               @Override
               public void run() {
                   disconnectOnMainThread();
               }
           });
       } else {
           // if we're still CONNECTING - skip DISCONNECTING state
           setState(State.DISCONNECTED);
       }
       // requests should be cancelled only when Billing#disconnect() is called explicitly as
       // it's only then we know for sure that no more work should be done
       mPendingRequests.cancelAll();
   }
}

针对不同状态做不同处理。当mState为CONNECTED时,通过mMainThread调用disconnectOnMainThread()。来看下这个方法。



private void disconnectOnMainThread() {
   Check.isMainThread();
   mConnector.disconnect();
}

逻辑很简单,通过mConnector断开service连接。

4.2.15 Purchase

表示购买信息的类

成员变量

  • String sku 表示商品项名称

  • String orderId表示订单标识符

  • String packageName 应用包名

  • long time 购买的时间

  • String payload 一个开发人员指定的字符串,该字段在储值的时候填入,在Google Play储值完成后返回

  • String token

  • State state 购买的状态,有PURCHASED,CANCELLED,REFUNDED,EXPIRED四个状态

  • boolean autoRenewing是否自动更新订阅。

  • String data 购买的原始数据

  • String signature 数据签名

4.2.16 Purchases

表示购买信息列表的类。维护了一个Purchase集合。

成员变量

  • String product 产品类型

  • List<Purchase> list购买过的商品列表

  • String continuationToken用于查询更多产品的token

4.2.17 PurchaseFlow

表示从用户请求购买之时起直到购买完成为止的一个购买流程的类,该类实现了CancellableRequestListener接口,重写了onSuccess()回调方法。

1.核心方法



@Override
public void onSuccess(@Nonnull PendingIntent purchaseIntent) {
   if (mListener == null) {
       // request was cancelled => stop here
       return;
   }
   try {
       mIntentStarter.startForResult(purchaseIntent.getIntentSender(), mRequestCode, new Intent());
   } catch (RuntimeException | IntentSender.SendIntentException e) {
       handleError(e);
   }

当PurchaseRequest获取到BuyIntent后,调用了RequestListener的onSuccess()并把purchaseIntent传进来,启动购买页面。然后在activity的onActivityResult()执行购买结果流程,PurchaseFlow把这个流程封装在本类中的onActivityResult()方法中



void onActivityResult(int requestCode, int resultCode, Intent intent) {
   try {
       Check.equals(mRequestCode, requestCode);
       if (intent == null) {
           // sometimes intent is null (it's not obvious when it happens but it happens from time to time)
           handleError(NULL_INTENT);
           return;
       }
       final int responseCode = intent.getIntExtra(EXTRA_RESPONSE, OK);
       if (resultCode != RESULT_OK || responseCode != OK) {
           handleError(responseCode);
           return;
       }
       final String data = intent.getStringExtra(EXTRA_PURCHASE_DATA);
       final String signature = intent.getStringExtra(EXTRA_PURCHASE_SIGNATURE);
       Check.isNotNull(data);
       Check.isNotNull(signature);
       final Purchase purchase = Purchase.fromJson(data, signature);
       mVerifier.verify(singletonList(purchase), new VerificationListener());
   } catch (RuntimeException | JSONException e) {
       handleError(e);
   }
}

2.PurchaseFlow的流程

Image

4.2.18 Inventory

表示加载关于products,SKUs和purchases相关信息的接口。

1.构造对象

这个类不能直接被实例化,需要通过调用Checkout的loadInventory()makeInventory()



@Nonnull
public Inventory loadInventory(@Nonnull Inventory.Request request, @Nonnull Inventory.Callback callback) {
   final Inventory inventory = makeInventory();
   inventory.load(request, callback);
   return inventory;
}
@Nonnull
public Inventory makeInventory() {
   Check.isMainThread();
   synchronized (mLock) {
       checkIsNotStopped();
   }
   final Inventory inventory;
   final Inventory fallbackInventory = mBilling.getConfiguration().getFallbackInventory(this, mOnLoadExecutor);
   if (fallbackInventory == null) {
       inventory = new CheckoutInventory(this);
   } else {
       inventory = new FallingBackInventory(this, fallbackInventory);
   }
   return inventory;
}

可以看出loadInventory()又调用了makeInventory()Inventory的实例化是在makeInventory()中进行的。先获取FallingBackInventory对象,如果不存在,则实例化CheckoutInventory对象。

2.主要方法



int load(@Nonnull Request request, @Nonnull Callback callback);//加载Products并且异步传递到Callback中,这是核心方法。
void cancel();//取消所有加载任务
void cancel(int id);//根据id取消指定的任务。
boolean isLoading();//判断是否至少有一个任务在加载中

4.2.19 BaseInventory

1.主要成员变量

  • List<Task> mTasks 维护了一个Task列表,用于对任务的管理

  • Checkout mCheckout 持有Checkout引用。

2.核心方法



@Override
public int load(@Nonnull Request request, @Nonnull Callback callback) {
   synchronized (mLock) {
       final Task task = new Task(request, callback);
       mTasks.add(task);
       task.run();
       return task.mId;
   }
}

可以看出load()根据request和callback实例化task对象,并添加到mTasks中,再执行task的run()

4.2.20 CheckoutInventory

BaseInventory的子类,用于加载购买流程的相关信息,实现了BaseInventory的抽象方法



protected Runnable createWorker(@Nonnull Task task) {
   return new Worker(task);
}

可见createWorker()方法返回了Worker对象,Worker是CheckoutInventory的内部类。

1.内部类Worker

Worker实现了Runnable接口和Checkout.Listener接口,作为CheckoutInventory的内部类,持有外部类引用,所以也就持有Checkout引用。run()方法调用了checkout的whenReady()方法.我们来看一下whenReady()中又调用了 Checkout.Listener回调方法。我们看一下回调方法的实现。



   @Override
   public void onReady(@Nonnull BillingRequests requests) {
   }
   @Override
   public void onReady(@Nonnull BillingRequests requests, @Nonnull String productId,
           boolean billingSupported) {
       final Product product = new Product(productId, billingSupported);
       synchronized (mLock) {
           countDown();
           mProducts.add(product);
           if (!mTask.isCancelled() && product.supported && mTask.getRequest().shouldLoadPurchases(productId)) {
               loadPurchases(requests, product);
           } else {
               countDown(1);
           }
           if (!mTask.isCancelled() && product.supported && mTask.getRequest().shouldLoadSkus(productId)) {
               loadSkus(requests, product);
           } else {
               countDown(1);
           }
       }
   }

可以看出onReady()回调方法判断是否加载购买信息或者加载SKU,分别调用了loadPurchases()loadSkus(),而两个方法右分别调用了requests.getAllPurchases()requests.getSkus(),从而实现了获取信息的流程。

2.查询信息流程

我们通过时序图来理清整个流程,这里以获取购买信息为例

Image

4.2.21 FallingBackInventory

同样的集成了BaseInventory,该类持有CheckoutInventory引用。表示如果其中一个产品不被支持,则库存回退到后备库存。

4.2.22 ProductTypes

Billing API中支持的Product类型,目前有IN_APP和SUBSCRIPTION两种

4.2.23 Product

表示在Inventory中的一种Product,包含了purchase列表和SKUS列表(如果有的话),Product可根据ProductTypes分为IN_APP和SUBSCRIPTION。

1.成员变量

  • String id Product ID

  • boolean supportedproduct是否被支持

  • List<Purchase> mPurchases purchase列表

  • List<Sku> mSkus SKU列表

4.2.24 Products

表示Product的集合,维护了一个存储Product的map。

4.2.25 MainThread

工具类,作用是确保Runnable在主线程执行

主要方法



@Override
public void execute(@Nonnull Runnable runnable) {
   if (MainThread.isMainThread()) {
       runnable.run();
   } else {
       mHandler.post(runnable);
   }
}

  • Checkout为整个应用内结算算流程的逻辑进行封装,提供了简便的方法给开发者调用。集成的时候只需要在构建Billing实例时做简单的配置,在生命周期内调用方法即可。

  • 做了缓存处理,避免重复的跨进程请求。

  • 通过一个队列维护请求顺序,便于管理Request

  • Billing库中做了很多并发处理,在方法中添加同步锁,这一定程度上影响了程序性能。

Image

喜欢这篇文章吗?感谢看官们打赏关注走一波。

Image

Image

Image


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK