Checkout开源库的源码解析

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结算流程

2.总体设计

2.1总体设计图

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()返回的INAPP_DATA_SIGNATURE数据的 JSON 字段对应,也就是说Purchase都是根据这个JSON字段的内容定义的。

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

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

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

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

3.request流程图

4.详细设计

4.1UML类关系图

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;@Nonnullfinal 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);}}@Overridepublic void onSuccess(@Nonnull Object result) {onBillingSupported(true);}@Overridepublic void onError(int response, @Nonnull Exception e) {onBillingSupported(false);}});}}
}
复制代码

whenReady()方法的目的是检查是否支持Billing API,也就是最终会调用service.isBillingSupported()方法,然后返回回调处理结果。

当离开页面时,需要调用stop()方法释放资源

public void stop() {Check.isMainThread();synchronized (mLock) {if (mState != State.INITIAL) {mState = State.STOPPED;}if (mRequests != null) {mRequests.cancelAll();mRequests = null;}if (mState == State.STOPPED) {mBilling.onCheckoutStopped();}}
}
复制代码

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

3.使用流程

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

4.2.2 Request.java

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

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.核心方法

    @Overridepublic boolean run() {final Request localRequest = getRequest();if (localRequest == null) {// request was cancelled => finish herereturn true;}if (checkCache(localRequest)) return true;// request is alive, let's check the service statefinal State localState;final IInAppBillingService localService;synchronized (mLock) {localState = mState;localService = mService;}if (localState == State.CONNECTED) {Check.isNotNull(localService);// service is connected, let's start requesttry {localRequest.start(localService, mContext.getPackageName());} catch (RemoteException | RuntimeException | RequestException e) {localRequest.onError(e);}} else {// service is not connected, let's check whyif (localState != State.FAILED) {// service was disconnectedconnect();return false;} else {// service was not connected in the first place => can't do anything, aborting the requestlocalRequest.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() {@Overridepublic void onServiceDisconnected(ComponentName name) {setService(null, false);}@Overridepublic void onServiceConnected(ComponentName name,IBinder service) {setService(IInAppBillingService.Stub.asInterface(service), true);}};
    复制代码

2.实现方法

    @Overridepublic 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 appreturn false;}}@Overridepublic void disconnect() {mContext.unbindService(mConnection);}
复制代码

connect()负责绑定服务,disconnect()解绑服务。

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的转换方式需要按照下图:

通过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->FAILEDmPlayStoreBroadcastReceiver.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 statemPlayStoreBroadcastReceiver.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 CONNECTEDCheck.isTrue(!mPlayStoreBroadcastReceiver.contains(mPlayStoreListener), "Leaking the listener");mMainThread.execute(new Runnable() {@Overridepublic 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() {@Overridepublic 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执行过程中的调用栈

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

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 statemPendingRequests.cancelAll();return;}if (mState == State.CONNECTED) {setState(State.DISCONNECTING);mMainThread.execute(new Runnable() {@Overridepublic void run() {disconnectOnMainThread();}});} else {// if we're still CONNECTING - skip DISCONNECTING statesetState(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 donemPendingRequests.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 herereturn;}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的流程

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

BaseInventory实现了Inventory接口,作为基类。子类需要实现protected abstract Runnable createWorker(@Nonnull Task task);抽象方法。

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回调方法。我们看一下回调方法的实现。

    @Overridepublic void onReady(@Nonnull BillingRequests requests) {}@Overridepublic 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.查询信息流程

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

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);}
}
复制代码

5.总结

优点

  • Checkout为整个应用内结算算流程的逻辑进行封装,提供了简便的方法给开发者调用。集成的时候只需要在构建Billing实例时做简单的配置,在生命周期内调用方法即可。
  • 做了缓存处理,避免重复的跨进程请求。
  • 通过一个队列维护请求顺序,便于管理Request

缺点

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

本文首发在公众号:三七互娱技术中心。欢迎关注~~

转载于:https://juejin.im/post/5a4f1be76fb9a01ca9153eb3

【移动开发】Checkout开源库源码解析相关推荐

  1. linux epoll 开发指南-【ffrpc源码解析】

    linux epoll 开发指南-[ffrpc源码解析] 摘要 关于epoll的问题很早就像写文章讲讲自己的看法,但是由于ffrpc一直没有完工,所以也就拖下来了.Epoll主要在服务器编程中使用,本 ...

  2. SEAL开源库源码02

    SEAL开源库源码02 本篇的最终目的是要分析 seal/modulus.h 文章目录 SEAL开源库源码02 seal/version.h seal/util/hestdparms.h 128-bi ...

  3. SEAL开源库源码10

    SEAL开源库源码10 文章目录 SEAL开源库源码10 seal/evaluator.h Evaluator 类 构造函数,利用 context 来构造 negate_inplace 和 negat ...

  4. SEAL开源库源码12

    SEAL开源库源码12 文章目录 SEAL开源库源码12 5_ckks_basics.cpp example_ckks_basics 函数 6_rotation.cpp example_rotatio ...

  5. SEAL开源库源码01

    SEAL开源库源码01 文章目录 SEAL开源库源码01 seal/util/defines.h 通用的判断函数 将x输出为字符串 字符串的拼接 检查double类型是否是64比特 检查int是否为3 ...

  6. PyCrypto密码学库源码解析(二)RSA参数生成

    Python Crypto库源码解析(二) RSA参数生成 * 版权声明 * 引用请注明出处,转载请联系: h0.1c@foxmail.com 本文主要讲解pycrypto库中RSA参数生成的实现方法 ...

  7. HEAAN开源库源码(一)

    HEAAN开源库源码 文章目录 HEAAN开源库源码 HEAAN.h HEAAN.cpp TimeUtils.h TimeUtils.cpp TestScheme.h TestScheme.cpp S ...

  8. hox 状态管理库源码解析

    文章目录 hox是什么 hox实现状态共享的方式 基本使用 全局状态共享 局部状态共享 源码解析 index.js 入口文件 coantainer.tsx 管理每个hook 全局状态共享的实现 Hox ...

  9. Android 常用开源框架源码解析 系列 (四)Glide

    一.定义  Glide 一个被google所推荐的图片加载库,作者是bumptech.对Android SDk 最低要求是 API 10  与之功能类似的是Square公司的picasso  二.基本 ...

最新文章

  1. python使用matplotlib可视化线图(line plot)、并自定义线条的粗细(线条的宽度、 line width in Matplotlib)
  2. 复习es6-let和const
  3. OpenStack镜像制作笔记 --以windows8.1-amd64为例
  4. JSF Struts Spring Hibernate 整合
  5. ubuntu14.04的键盘失灵解决方案
  6. 打印更无缝:微软改善Win11中通用打印体验
  7. Android平台RTMP多实例推送的几种情况探讨
  8. 无处不在的xss攻击
  9. css索引_CSS中的Z索引:它是什么以及它做什么
  10. Python项目实战:带领你爬取sexy的福利图片
  11. python内置函数__init__及__str__的区别
  12. gfdgdfgdfg
  13. cocos2d-x打飞机实例总结
  14. android源码定制之初探--定制android关机界面
  15. C1认证学习二十六(基础选择器)
  16. 音频服务器未能正常启动,Windows10系统音频服务未启动问题彻底解决方法
  17. JPEG 标准推荐的亮度、色度DC、AC Huffman 编码表
  18. 小学生也能看懂的海伦公式推导
  19. 不正方形(四个点构成一个凸四边形)
  20. 智能优化算法——哈里鹰算法(Matlab实现)

热门文章

  1. 图扑软件数字孪生海上风电 | 向海图强,奋楫争先
  2. 达芬奇发明计算机读后感200,三年级读后感:读《达芬奇》有感200字
  3. 我们学校的计算机房英语作文,精选我的学校英语作文6篇
  4. OSChina 周六乱弹 ——所以会魔法的人都是孤独的
  5. 发那科机器人请关闭电源_FANUC机器人常见错误恢复步骤,你真的都会吗?
  6. error怎么开机 fan_电脑开机提示cpu fan error怎么解决
  7. 异常被 ”吃“ 掉导致事务无法回滚
  8. 不运动也能增肌???打一针冬眠黑熊的血清就行
  9. php 5.3.29 nts,为什么PHP 5.3不在支持ISAPI,为什么还要分NTS和TS版本?
  10. 去除数组中指定元素的方法