Pro Yearly is on sale from $80 to $50! »

Reactive In-App Billing on Android

730df0227780e818df8ce1e19c9a6c48?s=47 Edward Dale
November 27, 2015

Reactive In-App Billing on Android

Presented at DevFest Greece 2015

730df0227780e818df8ce1e19c9a6c48?s=128

Edward Dale

November 27, 2015
Tweet

Transcript

  1. None
  2. Reactive In-App Billing An In-App Billing API with RxJava Edward

    Dale Freeletics GmbH
  3. Motivation • Our app uses RxJava for async operations •

    Except In-app billing • Crashes in IAB code
  4. Agenda • In-App Billing • IabHelper • RxJava • Going

    Reactive
  5. In-App Billing • Google Play service that lets you sell

    digital content from inside your applications • Google Play handles all checkout details • Any application that you publish through Google Play can implement In-app Billing • Other stores (Amazon, etc) have similar APIs http://developer.android.com/google/play/billing/index.html
  6. In-App Billing Your App Google Play App Google Play Server

    IPC Binder Magic
  7. package com.android.vending.billing; interface IInAppBillingService { int isBillingSupported(int apiVersion, String packageName,

    String type); . . AIDL
  8. package com.android.vending.billing; interface IInAppBillingService { . . Bundle getSkuDetails(int apiVersion,

    String packageName, String type, in Bundle skusBundle); . . AIDL
  9. package com.android.vending.billing; interface IInAppBillingService { . . Bundle getBuyIntent(int apiVersion,

    String packageName, String sku, String type, String developerPayload); . AIDL
  10. package com.android.vending.billing; interface IInAppBillingService { . . Bundle getPurchases(int apiVersion,

    String packageName, String type, String continuationToken); . . AIDL
  11. package com.android.vending.billing; interface IInAppBillingService { . . int consumePurchase(int apiVersion,

    String packageName, String purchaseToken); } AIDL
  12. Interprocess Communication • Supports primitives, Parcelables, and some Collection classes

    • Parcelables must be available to both processes • Synchronous • Multithreading must be dealt with • Exceptions aren’t propogated http://developer.android.com/guide/components/aidl.html
  13. But who really wants to write IPC code?

  14. How some people do it Your App Google Play App

    Google Play Server IPC Magic IabHelper Java
  15. IabHelper • “Sample” code from Google • Not versioned or

    part of a library • ~1,000 lines of code • Big API • 20 public methods • 5 interfaces • Buggy
  16. public Inventory queryInventory(boolean querySkuDetails, List<String> moreSkus) throws IabException public Inventory

    queryInventory(boolean querySkuDetails, List<String> moreItemSkus, List<String> moreSubsSkus) throws IabException public void queryInventoryAsync(boolean querySkuDetails, List<String> moreSkus, 
 QueryInventoryFinishedListener listener) public void queryInventoryAsync(QueryInventoryFinishedListener listener) public void queryInventoryAsync(boolean querySkuDetails,
 QueryInventoryFinishedListener listener) Query
  17. public Inventory queryInventory(
 boolean querySkuDetails, 
 List<String> moreItemSkus, 
 List<String>

    moreSubsSkus
 ) throws IabException Query
  18. public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener)

    public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, 
 OnIabPurchaseFinishedListener listener) public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, 
 OnIabPurchaseFinishedListener listener, String extraData) public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) Purchase
  19. public void launchPurchaseFlow( Activity act, 
 String sku, 
 String

    itemType, 
 int requestCode,
 OnIabPurchaseFinishedListener listener,
 String extraData ) Purchase
  20. void consume(Purchase itemInfo) throws IabException public void consumeAsync(Purchase purchase, OnConsumeFinishedListener

    listener) public void consumeAsync(List<Purchase> purchases, OnConsumeMultiFinishedListener listener) Consume
  21. public boolean handleActivityResult(int requestCode, 
 int resultCode, Intent data) public

    void enableDebugLogging(boolean enable, String tag) public void enableDebugLogging(boolean enable) public void startSetup(OnIabSetupFinishedListener listener) public void dispose() Misc
  22. The Ideal Billing API • query(…) • purchase(…) • consume(…)

    • Not too much else
  23. RxJava • A library for composing asynchronous and event-based programs

    by using observable sequences • Parameterized concurrency • Uniform, composable interface https://github.com/ReactiveX/RxJava
  24. “observable sequences” http://rxmarbles.com/#map

  25. “composable” http://rxmarbles.com/#filter

  26. “parameterized concurrency” http://reactivex.io/documentation/operators/observeon.html

  27. Let’s create a Billing API with Rx

  28. Going Reactive • Remember “The ideal Billing API”? • query(…)

    • purchase(…) • consume(…) • (Not much else)
  29. Observable<Inventory> queryInventory(
 boolean querySkuDetails, 
 List<String> moreItemSkus, 
 List<String> moreSubsSkus)

    Observable<Purchase> consume(List<Purchase> toConsume) Observable<Purchase> launchPurchaseFlow(
 Activity activity,
 SkuDetails sku,
 int requestCode,
 String extraData) Going Reactive
  30. Observable<Purchase> consume(List<Purchase> toConsume) • sync/async versions of the method •

    Listener to be notified of the result • Checked exceptions • Single/multiple product consumption What’s Missing?
  31. final Observable<Purchase> consumeObs = billingService.consume(purchases); consumeObs.subscribe(new Action1<Purchase>() { @Override public

    void call(Purchase purchase) { // Successful consume } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { // Show error message } }); Using the API
  32. final Observable<Purchase> consumeObs = billingService.consume(purchases) .subscribeOn(io()) .observeOn(mainThread()); consumeObs.subscribe(new Action1<Purchase>() {

    @Override public void call(Purchase purchase) { // Successful consume } }); Using the API
  33. final Observable<List<Purchase>> consumeObs = billingService.consume(purchases).toList() consumeObs.subscribe(new Action1<List<Purchase>>() { @Override public

    void call(List<Purchase> purchases) { // Successful consume } }); Using the API
  34. billingService.queryInventory(true) .flatMap(inventory -> just(getPurchases()) .flatMap(purchases -> billingService.consume(purchases)) .subscribe() Using the

    API (Advanced)
  35. Additional Benefits Your App Google Play App Google Play Server

    IPC Magic Play Billing Service RxJava Amazon Billing Service Amazon Server Magic Billing Service Polymorphism
  36. Additional Benefits Your App Mock Billing Service RxJava Testability Billing

    Service
  37. Observable<Inventory> queryInventory(
 boolean querySkuDetails, 
 List<String> moreItemSkus, 
 List<String> moreSubsSkus)

    Observable<Purchase> consume(List<Purchase> toConsume) Observable<Purchase> launchPurchaseFlow(
 Activity activity,
 SkuDetails sku,
 int requestCode,
 String extraData) Reactive In-App Billing Coming soon
 as Open Source library
  38. Questions? Edward Dale
 edward@freeletics.com @scompt Now hiring Android, iOS, Ruby

    backend, DevOps in Germany
  39. None