Reaktív programozás Androidon

Erdődy-Nagy Zsombor

Miről lesz szó?

  • RxJava tapasztalatok Androidon, éles környezetben
  • Előnyök, hátrányok, buktatók
  • Alkalmazott Rx minták

Miről nem lesz szó?

  • Rx alapokról kimerítően :(

Reaktív programozás

  • Problémák megoldása komponálható reaktív streamekkel
  • RxJava: de-facto Java-land Rx implementacio
  • Hasznos linkek gyűjteménye kezdéshez: http://bit.ly/21nTbGj
Observable.range(0, Integer.MAX_VALUE)
    .take(6)
    .filter(it -> it % 2 == 0)
    .map(it -> String.valueOf(it) + ' ')
    .reduce(new StringBuilder(), (sb, it) -> sb.append(it))
    .subscribeOn(Schedulers.newThread())
    .observeOn(Schedulers.io())
    .subscribe(System.out::println);

// Output: 0 2 4 

Élő példa

Skyscanner Android App

Skyscanner Android App

  • Letöltések száma > 22 millió
  • ~200k sor kód
  • ~30 fejlesztő
  • 2 hetes release train
  • Continous Integration - Condor
  • Rendszeres A/B tesztelés - Dr. Jekyll
  • > 1 éve használunk reaktív mintákat

Autosuggest

autosuggestInput
    .throttleLast(250, TimeUnit.MILLISECONDS)
    .distinctUntilChanged()
    .switchMap(input -> {
        return autosuggestManager.autosuggest(input);
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread)
    .subscribe(results -> ...);

Korábbi keresések

Observable.zip(
    // Get recent searches
    recentSearchStore.getRecentSearches(),
    // Get Price Alerts
    priceAlertStore.getPriceAlerts(),
    // Get latest saved search configuration
    searchConfigStore.latestSearchConfig(),
    (searches, alerts, lastConfig) -> {
        // Batch the data based on common City
        ...
    })
    .flatMap(batched -> {
        // Get images for the cities
        return Observable.zip(
            Observable.just(batched),
            imageStore.getCityImage(batched.cityId()),
            (ars, s) -> ars.setImageUrl(s));
    });

User-vektor

userVector
    // Avoid uploading too often
    .throttleLast(3, TimeUnit.SECONDS)
    // Upload
    .flatMap(it -> userVectorStore.upload(it))
    // Re-subscribe on failure
    .retry()
    // Upload on background thread
    .subscribeOn(Schedulers.newThread())
    // Don't care about the results
    .subscribe();

+

  • Boilerplate kód--
  • Kódminőség++
  • Igen aktív RxJava közösség
  • Közös, de-facto minta:
    • Folyamatok komponáláshoz
    • Folyamatok leállításához
    • Folyamatok hibáinak kezeléséhez
    • Többszálúság kezeléséhez

-

  • Tanulási görbe
  • Debug nehézségek++
  • Cargo-cult coding hatványozottan veszélyes

Buktatók

Observable.create()

  • Minden ránk van bízva -> óvatosan vele
  • Nincs backpressure-támogatás
  • Nincs automatikus unsubscription-kezelés
  • Ha lehet, inkább .just() / .from() / .defer()
Observable.defer(() -> Observable.just(expensiveCalculation()))
                .subscribe(System.out::println);

Leiratkozás

  • Csak best-effort módon történik!
  • Megtörténte után még szembejöhet 1-1 event
  • Elsősorban összetettebb, többszálú streameknél
  • Többnyire ritkán okoz problémát

Error blokk

  • Feliratkozáskor elhagyható az Error blokk
Observable.range(0, 10)
    .doOnNext(integer -> {
        if (integer.equals(9)) throw new RuntimeException("Surprise!");
    })
    .subscribe(System.out::println); // -> felrobbanunk 9-nel :(
  • Könnyen lemaradhat ott is, ahol kellene
  • Tipp: Teljes Subscriber használata Action-ök helyett
  • Tipp: Lint check a hiányzó Error blokkokra!

Végtelen streamek

  • 0...n onNext() -> onComplete() | onError()
  • Megvan a helyük a végtelen streameknek is
    • Pl. touch input, szenzor input
  • Bizonyos operátorok viszont meglephetnek minket!
    • Pl. concatMap()

Unsafe használat

  • Egyes optimalizált adatszerkezetek az RxJava-n belül Unsafe alapúak
  • Samsung - Android 5.0, 5.0.1, 5.0.2 -> crash :(
  • Viszont kikapcsolható: If Samsung -> No Unsafe
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP && 
    "samsung".equalsIgnoreCase(Build.MANUFACTURER)) {
    System.setProperty("rx.unsafe-disable", "True");
}

Operátorok

  • Az RxJava kifejezőereje nagy, a tömörség azonban nincs ingyen
  • Ne csak feltételezzünk működéseket, teszteljünk!
  • Unit test, unit test, unit test
  • Komplexebb streameknél hatványozottan igaz

Lambda hell

  • Figyeljünk az olvashatóságra
  • Komplexebb esetekben sokat segít, ha nem inline írunk mindent egy stream felépítésekor
  • Főleg lambdák híján! (< Java 1.8)
  • Ami segíthet:
    • >= Java 1.8
    • Retrolambda
    • Kotlin

Köszönöm a figyelmet

  • Kérdések?

  • We're hiring!

Reaktív programozás Androidon I.

By rzsombor

Reaktív programozás Androidon I.

  • 922