Kotlin Reactive Programming met RxJava en RxKotlin

Sinds Kotlin een officieel ondersteunde taal voor Android-ontwikkeling is, is het snel in populariteit gegroeid onder Android-ontwikkelaars, met Google rapporteert een 6x toename in de applicaties die met Kotlin zijn gemaakt.

Als je eerder RxJava of RxAndroid hebt gebruikt en de overstap naar Kotlin wilt maken, of als je reactief programmeren met Kotlin wilt beginnen, is deze zelfstudie iets voor jou. We bespreken de essentie van het maken van RxJava 2.0 waarnemers, observabelen en datastreams in Kotlin, voordat je kijkt naar hoe je een ton boilerplate code uit je projecten kunt trimmen, door RxJava te combineren met Kotlin-uitbreidingsfuncties.

Het gebruik van RxJava met Kotlin kan u helpen zeer reactieve apps te maken in minder code, maar geen enkele programmeertaal is perfect, dus ik zal ook een oplossing delen voor het SAM-conversieprobleem dat veel ontwikkelaars tegenkomen wanneer zij voor het eerst RxJava 2.0 gaan gebruiken met Kotlin.

Om de zaken af ​​te ronden, maken we een applicatie die laat zien hoe u RxJava kunt gebruiken om enkele van de problemen op te lossen die u tegenkomt in real-life Android-projecten.

Als dit je eerste smaak is van RxJava, zal ik onderweg ook alle achtergrondinformatie geven die je nodig hebt om de belangrijkste RxJava-concepten te begrijpen. Zelfs als je nog nooit eerder met RxJava hebt geëxperimenteerd, zul je aan het eind van dit artikel een goed begrip hebben van hoe je deze bibliotheek in je projecten kunt gebruiken, en je hebt verschillende werkende apps gemaakt met RxJava, RxKotlin, RxAndroid en RxBinding.

Wat is RxJava hoe dan ook?

RxJava is een open-source implementatie van de ReactiveX-bibliotheek die u helpt bij het maken van toepassingen in de reactieve programmeerstijl. Hoewel RxJava is ontworpen voor het verwerken van synchrone en asynchrone gegevensstromen, is dit niet beperkt tot "traditionele" gegevenstypen. RxJava's definitie van "data" is behoorlijk breed en omvat zaken als caches, variabelen, eigenschappen en zelfs gebruikersinvoergebeurtenissen zoals klikken en swipes. Alleen omdat uw toepassing geen enorme aantallen verwerkt of complexe gegevenstransformaties uitvoert, betekent dit niet dat deze niet kan profiteren van RxJava!

Voor een kleine achtergrond over het gebruik van RxJava voor Android-apps, kun je sommige van mijn andere berichten hier op Envato Tuts bekijken+.

Dus hoe werkt RxJava?

RxJava breidt het ontwerppatroon van de Observer-software uit, gebaseerd op het concept van waarnemers en waarneembare objecten. Als u een standaard RxJava-gegevenspijplijn wilt maken, moet u:

  • Maak een waarneembaar.
  • Geef waarneembaar wat gegevens die moeten worden uitgezonden.  
  • Maak een waarnemer.
  • Abonneer de waarnemer op het waarneembare.

Zodra het Waarneembare ten minste één waarnemer heeft, zal het beginnen met het uitzenden van gegevens. Telkens wanneer Observable een stuk data uitzendt, zal het de toegewezen Observer hiervan op de hoogte brengen door het onNext () methode, en de waarnemer zal dan typisch wat actie ondernemen als reactie op deze gegevensemissie. Zodra Observable klaar is met het uitzenden van gegevens, zal het de Observer hiervan op de hoogte brengen door te bellen onComplete (). Waarneembaar wordt dan beëindigd en de gegevensstroom eindigt.

Als er een uitzondering optreedt, dan OnError () wordt opgeroepen en het Waarneembare wordt onmiddellijk beëindigd zonder meer gegevens of oproepen te verzenden onComplete ().

Maar RxJava is dat niet net over het doorgeven van gegevens van een waarneembaar naar een waarnemer! RxJava heeft een enorme verzameling operatoren die u kunt gebruiken om deze gegevens te filteren, samen te voegen en te transformeren. Stel je bijvoorbeeld voor dat je app een heeft Nu betalen knop die detecteert bij klikken evenementen en u bent bang dat een ongeduldige gebruiker meerdere keren op de knop tikt, waardoor uw app verschillende betalingen verwerkt.  

RxJava laat je deze transformeren bij klikken gebeurtenissen in een gegevensstroom, die u vervolgens kunt manipuleren met behulp van de verschillende operators van RxJava. In dit specifieke voorbeeld zou u de ontdendering () operator om gegevensemissies te filteren die snel achter elkaar plaatsvinden, dus zelfs als de gebruiker wegloopt bij de Nu betalen knop, zal uw app slechts één enkele betaling registreren.

Wat zijn de voordelen van het gebruik van RxJava?

We hebben gezien hoe RxJava u kan helpen een specifiek probleem op te lossen in een specifieke toepassing, maar wat heeft het te bieden aan Android-projecten, in het algemeen?

RxJava kan uw code vereenvoudigen door u een manier te geven om te schrijven wat u wilt bereiken, in plaats van een lijst met instructies te schrijven die uw toepassing moet doorlopen. Als u bijvoorbeeld alle gegevensemissies wilt negeren die zich binnen dezelfde periode van 500 milliseconden voordoen, schrijft u het volgende:

.debounce (500, TimeUnit.MILLISECONDS)

Bovendien, aangezien RxJava behandelt bijna alles als gegevens biedt het een sjabloon die u kunt toepassen op een breed scala aan gebeurtenissen: maak een waarneembaar, maak een waarnemer, onderschrijf de waarnemer voor waarneembaar, spoel en herhaal. Deze formulebenadering resulteert in veel meer rechtlijnige, voor de mens leesbare code.

Het andere grote voordeel voor Android-ontwikkelaars is dat RxJava veel van de pijn kan wegnemen van multithreading op Android. De mobiele gebruikers van vandaag verwachten dat hun toepassingen kunnen multitasken, zelfs als het iets eenvoudigs is als het downloaden van gegevens op de achtergrond terwijl ze nog steeds reageren op gebruikersinvoer.

Android heeft verschillende ingebouwde oplossingen voor het maken en beheren van meerdere threads, maar geen van deze zijn bijzonder eenvoudig te implementeren en ze kunnen snel resulteren in complexe, uitgebreide code die moeilijk te lezen is en gevoelig is voor fouten.

In RxJava maakt en beheert u extra discussies met een combinatie van operators en planners. U kunt eenvoudig de thread wijzigen waar het werk wordt uitgevoerd, met behulp van de subscribeOn operator plus een planner. Hier plannen we bijvoorbeeld werk dat moet worden uitgevoerd op een nieuwe thread:

.subscribeOn (Schedulers.newThread ())

U kunt opgeven waar de resultaten van dit werk moeten worden gepost, met behulp van de observeOn operator. Hier plaatsen we de resultaten op de belangrijkste UI-thread van Android, met behulp van de AndroidSchedulers.mainThread scheduler, die beschikbaar is als onderdeel van de RxAndroid-bibliotheek:

.observeOn (AndroidSchedulers.mainThread ())

Vergeleken met de ingebouwde multithreading-oplossingen van Android is de aanpak van RxJava veel meer beknopt en gemakkelijker te begrijpen.

Nogmaals, u kunt meer te weten komen over hoe RxJava werkt en wat de voordelen zijn van het toevoegen van deze bibliotheek aan uw project, in mijn artikel Aan de slag met RxJava 2 voor Android.

Moet ik RxJava of RxKotlin gebruiken?

Aangezien Kotlin 100% interoperabel is met Java, kunt u de meeste Java-bibliotheken in uw Kotlin-projecten zonder problemen gebruiken - en de RxJava-bibliotheek is geen uitzondering.

Er is een speciale RxKotlin-bibliotheek, een Kotlin-wikkel rond de reguliere RxJava-bibliotheek. Deze wrapper biedt uitbreidingen die RxJava voor de Kotlin-omgeving optimaliseren en kan de hoeveelheid boilerplate-code die je moet schrijven verder verminderen.

Omdat je RxJava in Kotlin kunt gebruiken zonder ooit RxKotlin nodig te hebben, zullen we RxJava in dit artikel gebruiken, tenzij anders aangegeven.

Eenvoudige waarnemers en waarneembare voorwerpen creëren in Kotlin

Observers en Observables zijn de bouwstenen van RxJava, dus laten we beginnen met het maken van:

  • Een eenvoudige waarneembare die een korte gegevensstroom afgeeft als reactie op een gebeurtenis met een knopklik.
  • An Observable die op deze gegevens reageert door verschillende berichten naar Android Studio's af te drukken logcat.

Maak een nieuw project met de instellingen van uw keuze, maar zorg ervoor dat u de Inclusief Kotlin-ondersteuning checkbox wanneer gevraagd. Open vervolgens je project build.gradle bestand en voeg de RxJava-bibliotheek toe als een projectafhankelijkheid:

afhankelijkheden implementatie fileTree (dir: 'libs', include: ['* .jar']) implementatie "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementatie 'androidx.appcompat: appcompat: 1.0.0- alpha1 'implementatie' androidx.constraintlayout: constraintlayout: 1.1.0 'implementatie' io.reactivex.rxjava2: rxjava: 2.1.9 '

Open vervolgens je project activity_main.xml bestand en voeg de knop toe waarmee de gegevensstroom wordt gestart:

  

Er zijn verschillende manieren om Observable te maken, maar een van de gemakkelijkste is om de net() operator om een ​​object of lijst met objecten om te zetten in een waarneembaar.

In de volgende code maken we een waarneembaar (myObservable) en geef deze de items 1, 2, 3, 4 en 5 om uit te zenden. We creëren ook een waarnemer (myObserver), abonneren op myObservable, en vertel het vervolgens om een ​​bericht af te drukken logcat elke keer dat het een nieuwe emissie ontvangt.

import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import io.reactivex.Observable import io.reactivex.Observer import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.activity_main . * klasse MainActivity: AppCompatActivity () private var TAG = "MainActivity" onderdrukt plezier onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Start de stream wanneer op de knop wordt geklikt // button.setOnClickListener startRStream () private fun startRStream () // Maak een waarneembaar // val myObservable = getObservable () // Maak een waarnemer // val myObserver = getObserver () // Abonneer myObserver op myObservable / / myObservable .subscribe (myObserver) private fun getObserver (): Observer object terugzenden: waarnemer override fun onSubscribe (d: Disposable)  // print elke keer dat onNext wordt aangeroepen de waarde naar Android Studio's Logcat // override fun onNext (s: String) Log.d (TAG, "onNext: $ s")  // Wordt aangeroepen als een uitzondering wordt gegenereerd // overschrijven leuk bij fout (e: Throwable) Log.e (TAG, "onError:" + e.message) // Wanneer onComplete wordt aangeroepen, drukt u het volgende af naar Logcat // override fun onComplete () Log.d (TAG, "onComplete") // Geef mijnBevatbare data om uit te zenden // private fun getObservable (): Observable return Observable.just ("1", "2", "3", "4", "5")

U kunt nu deze applicatie testen:

  • Installeer uw project op een fysieke Android-smartphone of -tablet of een Android Virtual Device (AVD).
  • Geef de Start RxJava stream druk op een klik.
  • Open de Logcat-monitor van Android Studio door de. Te selecteren Android-monitor tab (waar de cursor zich bevindt in de volgende schermafbeelding) en selecteer vervolgens de logcat tab.

Op dit punt begint het waarneembare gegevens uit te zenden en de waarnemer zal zijn berichten afdrukken naar Logcat. Uw Logcat-uitvoer zou er ongeveer zo uit moeten zien:

Je kunt dit project downloaden van GitHub als je het zelf wilt proberen.

Kotlin-uitbreidingen voor RxJava

Nu we hebben gezien hoe we een eenvoudige RxJava-pijplijn in Kotlin kunnen opzetten, laten we eens kijken hoe je dit kunt bereiken in minder code, met behulp van de uitbreidingsfuncties van RxKotlin.

Om de RxKotlin-bibliotheek te gebruiken, moet u deze als een projectafhankelijkheid toevoegen:

afhankelijkheden implementatie fileTree (dir: 'libs', include: ['* .jar']) implementatie "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementatie 'androidx.appcompat: appcompat: 1.0.0- alpha1 'implementatie' androidx.constraintlayout: constraintlayout: 1.1.0 'implementatie' io.reactivex.rxjava2: rxjava: 2.1.9 '// Voeg de volgende // implementatie toe' io.reactivex.rxjava2: rxkotlin: 2.2.0 '

In het volgende voorbeeld gebruiken we RxKotlin's toObservable () uitbreidingsfunctie om a te transformeren Lijst in een waarneembaar. We gebruiken ook de subscribeBy () uitbreidingsfunctie, omdat het ons toestaat een waarnemer te construeren met benoemde argumenten, wat resulteert in duidelijkere code.

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.toObservable import kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Start de stream wanneer op de knop wordt geklikt // button.setOnClickListener startRStream () private fun startRStream ()  val list = listOf ("1", "2", "3", "4", "5") // Pas de toObservable () uitbreidingsfunctie toe // list.toObservable () // Construeer uw waarnemer met behulp van de subscribeBy ( ) uitbreidingsfunctie // .subscribeBy (onNext = println (it), onError = it.printStackTrace (), onComplete = println ("onComplete!"))

Dit is de uitvoer die u zou moeten zien:

De kwestie van SAM-dubbelzinnigheid van RxJava oplossen

RxKotlin biedt ook een belangrijke oplossing voor het SAM-conversieprobleem dat kan optreden wanneer er meerdere SAM-parameteroverbelasting is op een bepaalde Java-methode. Deze SAM-ambiguïteit verwart de Kotlin-compiler, omdat het niet kan uitrekenen welke interface het moet converteren, en je project zal niet compileren als resultaat.

Deze SAM-ambiguïteit vormt een bijzonder probleem bij het gebruik van RxJava 2.0 met Kotlin, omdat veel RxJava-operators meerdere SAM-compatibele typen gebruiken.

Laten we het SAM-conversieprobleem in actie bekijken. In de volgende code gebruiken we de zip () operator om de output van twee Observables te combineren:  

import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import io.reactivex.Observable import kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super .onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Start de stream wanneer op de knop wordt geklikt // button.setOnClickListener startRStream () private fun startRStream () val numbers = Observable.range (1, 6 ) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observable.zip (strings, numbers) s, n -> " $ s $ n " zipped.subscribe (:: println)

Dit zorgt ervoor dat de Kotlin-compiler een fout voor het afleiden van het type gooit. RxKotlin biedt echter hulpmethoden en uitbreidingsfuncties voor de betrokken operators, inclusief Observables.zip (), die we gebruiken in de volgende code:

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.Observable import io.reactivex.rxkotlin.Observables import kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: bundel?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Start de stream wanneer op de knop wordt geklikt // button.setOnClickListener startRStream () private fun startRStream () val numbers = Observable.range (1, 6) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observables.zip (tekenreeksen, getallen ) s, n -> "$ s $ n" zipped.subscribe (:: println)

Dit is de uitvoer van deze code:

Conclusie

In deze zelfstudie heb ik je laten zien hoe je de RxJava-bibliotheek in je Kotlin-projecten kunt gebruiken, inclusief het gebruik van een aantal aanvullende ondersteunende bibliotheken, zoals RxKotlin en RxBinding. We hebben gekeken hoe je eenvoudige waarnemers en waarnemingen kunt creëren in Kotlin, tot en met het optimaliseren van RxJava voor het Kotlin-platform, met behulp van uitbreidingsfuncties.

Tot nu toe hebben we RxJava gebruikt om eenvoudige Observables te maken die gegevens uitzenden, en waarnemers die deze gegevens afdrukken naar Logcat van Android Studio, maar dit is niet hoe je RxJava in de echte wereld zult gebruiken!

In het volgende bericht gaan we bekijken hoe RxJava kan helpen bij het oplossen van praktische problemen die u tegenkomt bij het ontwikkelen van Android-apps. We zullen RxJava met Kotlin gebruiken om een ​​klassieker te maken Inschrijven scherm.