Kotlin Reactive Programming With RxJava och RxKotlin

Sedan det blev ett officiellt stödt språk för Android-utveckling, har Kotlin snabbt vuxit i popularitet bland Android-utvecklare, med Google rapporterar en 6x ökning av de program som skapas med Kotlin.

Om du tidigare har använt RxJava eller RxAndroid och vill byta till Kotlin, eller vill starta en reaktiv programmering med Kotlin, är denna handledning för dig. Vi täcker väsentliga för att skapa RxJava 2.0 observatörer, observabler och dataströmmar i Kotlin innan du tittar på hur du kan trimma ett ton kedjekodskod från dina projekt genom att kombinera RxJava med Kotlins förlängningsfunktioner.

Att använda RxJava med Kotlin kan hjälpa dig att skapa mycket reaktiva appar i mindre kod, men inget programmeringsspråk är perfekt, så jag delar också en lösning för SAM-konverteringsproblemet som många utvecklare stöter på när de först börjar använda RxJava 2.0 med Kotlin.

För att paketera upp saker skapar vi en applikation som visar hur du kan använda RxJava för att lösa några av de problem du stöter på i verkliga Android-projekt.

Om det här är din första smak av RxJava, så längs vägen kommer jag också att ge all bakgrundsinformation du behöver för att förstå de centrala RxJava-koncepten. Även om du aldrig har experimenterat med RxJava innan, i slutet av den här artikeln får du en gedigen förståelse för hur du använder det här biblioteket i dina projekt, och du har skapat flera fungerande appar, med hjälp av RxJava, RxKotlin, RxAndroid och RxBinding.

Vad är RxJava, hur som helst?

RxJava är en öppen källkodsimplementering av ReactiveX-biblioteket som hjälper dig att skapa applikationer i den reaktiva programmeringsstilen. Fastän RxJava är utformad för att bearbeta synkrona och asynkrona dataströmmar, är den inte begränsad till "traditionella" datatyper. RxJavas definition av "data" är ganska bred och innehåller saker som cachar, variabler, egenskaper och till och med användarinmatningshändelser som klick och swipes. Bara för att din ansökan inte hanterar enorma tal eller utför komplexa dataomvandlingar betyder det inte att det inte kan dra nytta av RxJava!

För lite bakgrund på att använda RxJava för Android-appar kan du kolla in några av mina andra inlägg här på Envato Tuts+.

Så hur fungerar RxJava?

RxJava utökar Observer-mjukvarans designmönster, som bygger på begreppet Observers and Observables. För att skapa en grundläggande RxJava-datapipeline behöver du:

  • Skapa en observerbar.
  • Ge Observable några data att avge.  
  • Skapa en observatör.
  • Prenumerera Observeren till Observable.

Så snart som Observable har minst en Observer, kommer den att börja sända data. Varje gång Observable avger en bit data, kommer den att meddela sin tilldelade Observer genom att ringa onNext () metod, och Observeren utför då vanligtvis någon åtgärd som svar på denna datautsläpp. När Observable har slutat att skicka data, kommer det att meddela observatören genom att ringa onComplete (). Den Observable kommer då att avslutas, och dataströmmen kommer att sluta.

Om ett undantag uppstår, då onerror () kommer att ringas, och Observable kommer att avslutas omedelbart utan att ge mer data eller samtal onComplete ().

Men RxJava är det inte bara om att överföra data från en observerbar till en observatör! RxJava har en enorm samling operatörer som du kan använda för att filtrera, slå samman och omvandla denna data. Tänk dig att din app har en Betala nu knapp som upptäcker onClick händelser, och du är orolig för att en otålig användare kan trycka på knappen flera gånger, vilket gör att din app hanterar flera betalningar.  

RxJava kan du omvandla dessa onClick händelser i en dataström, som du då kan manipulera med RxJavas olika operatörer. I det här exemplet kan du använda debounce () operatör för att filtrera datautsläpp som händer i snabb följd, så även om användaren bashes bort vid Betala nu knappen, kommer din app bara att registrera en enda betalning.

Vad är fördelarna med att använda RxJava?

Vi har sett hur RxJava kan hjälpa dig att lösa ett specifikt problem i en specifik applikation, men vad har den att erbjuda Android-projekt i allmänhet?

RxJava kan hjälpa till att förenkla din kod genom att ge dig ett sätt att skriva vad du vill uppnå, snarare än att skriva en lista med instruktioner som din ansökan ska fungera genom. Om du till exempel vill ignorera alla datautsläpp som händer inom samma 500-millisekundperiod skriver du:

.debounce (500, TimeUnit.MILLISECONDS)

Dessutom, eftersom RxJava behandlar nästan allting Som data tillhandahåller det en mall som du kan ansöka om ett brett spektrum av händelser: skapa en observerbar, skapa en observatör, prenumerera observatören till observable, skölj och repetera. Denna formeliska tillvägagångssätt resulterar i mycket enklare, läsbar kod.

Den andra stora fördelen för Android-utvecklare är att RxJava kan ta mycket av smärtan av multithreading på Android. Dagens mobilanvändare förväntar sig att deras applikationer kan multitaska, även om det är något så enkelt som att ladda ner data i bakgrunden medan de fortfarande svarar på användarinmatning.

Android har flera inbyggda lösningar för att skapa och hantera flera trådar, men ingen av dessa är särskilt lätta att implementera, och de kan snabbt resultera i komplex, verbos kod som är svåra att läsa och är utsatta för fel.

I RxJava skapar och hanterar du ytterligare trådar med en kombination av operatörer och schemaläggare. Du kan enkelt ändra tråden där arbetet utförs med hjälp av subscribeOn operatör plus en schemaläggare. Till exempel, här planerar vi arbete som ska utföras på en ny tråd:

.subscribeOn (Schedulers.newThread ())

Du kan ange var resultaten av det här arbetet ska läggas ut, med hjälp av observeOn operatör. Här lägger vi resultaten till Android: s viktigaste huvudbruksanvisning, med hjälp av AndroidSchedulers.mainThread schemaläggare, som är tillgänglig som en del av RxAndroid biblioteket:

.observeOn (AndroidSchedulers.mainThread ())

Jämfört med Androids inbyggda multithreading-lösningar är RxJavas tillvägagångssätt mycket mer koncis och lättare att förstå.

Återigen kan du lära dig mer om hur RxJava fungerar, och fördelarna med att lägga till det här biblioteket i ditt projekt, i min Komma igång med RxJava 2 för Android-artikeln.

Ska jag använda RxJava eller RxKotlin?

Eftersom Kotlin är 100% kompatibelt med Java kan du använda de flesta Java-bibliotek i dina Kotlin-projekt utan några svårigheter - och RxJava-biblioteket är inget undantag.

där är ett dedikerat RxKotlin-bibliotek, vilket är ett Kotlin-omslag runt det vanliga RxJava-biblioteket. Denna omslag ger förlängningar som optimerar RxJava för Kotlin-miljön och kan ytterligare minska mängden kedjekortskod du behöver skriva.

Eftersom du kan använda RxJava i Kotlin utan att någonsin behöva RxKotlin, använder vi RxJava genom hela denna artikel, om inte annat anges.

Skapa enkla observatörer och observables i Kotlin

Observatörer och Observables är byggstenarna i RxJava, så låt oss börja med att skapa:

  • En enkel Observable som avger en kort ström av data som svar på en knapphändelse.
  • En Observable som reagerar på dessa data genom att skriva ut olika meddelanden till Android Studio logcat.

Skapa ett nytt projekt med de inställningar du vill ha, men se till att du väljer Inkludera Kotlin support kryssrutan när du blir ombedd. Öppna sedan ditt projekt build.gradle fil och lägg till RxJava-biblioteket som ett projektberoende:

beroenden implementation fileTree (dir: 'libs') inkluderar: ['* .jar']) implementation "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementation "androidx.appcompat: appcompat: 1.0.0- alpha1 "implementation" androidx.constraintlayout: begränsningslayout: 1.1.0 "implementation" io.reactivex.rxjava2: rxjava: 2.1.9 '

Öppna sedan ditt projekt activity_main.xml fil och lägg till den knapp som startar dataströmmen:

  

Det finns flera olika sätt att skapa en observerbar, men en av de enklaste är att använda bara() operatör för att konvertera ett objekt eller en lista över objekt till en observerbar.

I följande kod skapar vi en observerbar (myObservable) och ger det föremålen 1, 2, 3, 4 och 5 för att avge. Vi skapar också en Observer (myObserver), prenumerera på det myObservable, och sedan berätta för att skriva ut ett meddelande till logcat varje gång det tar emot en ny utsläpp.

importera androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import io.reactivex.Observable import io.reactivex.Observer importera io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.activity_main . * Klass MainActivity: AppCompatActivity () privat var TAG = "MainActivity" överruller roligt onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Starta strömmen när knappen klickas // button.setOnClickListener startRStream () privata roliga startRStream () // Skapa en observerbar // val myObservable = getObservable () // Skapa en observatör // val myObserver = getObserver () // Prenumerera myObserver på myObservable / / myObservable .subscribe (myObserver) privata roliga getObserver (): Observer Returobjekt: Observer stryka roligt onSubscribe (d: Disposable)  // Varje gång onNext heter, skriv ut värdet till Android Studio: s Logcat // override fun påNext (s: String) Log.d (TAG, "onNext: $ s")  // Kallas om ett undantag kastas // överrätta kul påError (e: Throwable) Log.e (TAG, "onError:" + e.message) // När onComplete kallas, skriv ut följande till Logcat // åsidosätta roligt onComplete () Log.d (TAG, "onComplete") // Ge myObservable några data att avge // privata roliga getObservable (): Observable return Observable.just ("1", "2", "3", "4", "5")

Du kan nu sätta denna ansökan på provet:

  • Installera ditt projekt på en fysisk Android-smarttelefon eller -tablet eller en Android Virtual Device (AVD).
  • Ge Starta RxJava-strömmen tryck ett klick.
  • Öppna Android Studio's Logcat Monitor genom att välja Android Monitor fliken (där markören är placerad i följande skärmdump) och sedan välja logcat flik.

Vid denna punkt börjar Observable starta sin data, och Observer kommer att skriva ut sina meddelanden till Logcat. Din Logcat-utgåva ska se ut så här:

Du kan ladda ner det här projektet från GitHub om du vill prova det själv.

Kotlin Extensions för RxJava

Nu när vi har sett hur man skapar en enkel RxJava-pipeline i Kotlin, låt oss se hur du kan uppnå detta i mindre kod, med hjälp av RxKotlins förlängningsfunktioner.

För att använda RxKotlin-biblioteket måste du lägga till det som ett projektberoende:

beroenden implementation fileTree (dir: 'libs') inkluderar: ['* .jar']) implementation "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementation "androidx.appcompat: appcompat: 1.0.0- alfa1 'implementation' androidx.constraintlayout: begränsningslayout: 1.1.0 'implementation' io.reactivex.rxjava2: rxjava: 2.1.9 '// Lägg till följande // implementation' io.reactivex.rxjava2: rxkotlin: 2.2.0 '

I följande exempel använder vi RxKotlin toObservable () förlängningsfunktion för att omvandla a Lista in i en observerbar. Vi använder också subscribeBy () förlängningsfunktion, eftersom det tillåter oss att konstruera en Observer med namngivna argument, vilket resulterar i tydligare kod.

importera android.os.Bundle import androidx.appcompat.app.AppCompatActivity importera io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.toObservable import kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () override roligt onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Starta strömmen när knappen klickar // button.setOnClickListener startRStream () privata roliga startRStream ()  val lista = listOf ("1", "2", "3", "4", "5") // Använd funktionen toObservable () extension // list.toObservable () // Konstruera din Observer med subscribeBy ) förlängningsfunktion // .subscribeBy (onNext = println (it)), onError = it.printStackTrace (), onComplete = println ("onComplete!"))

Här är produktionen som du bör se:

Lösning av RxJavas SAM-tvetydighetsproblem

RxKotlin ger också en viktig lösning för SAM-omvandlingsproblemet som kan uppstå när det finns flera SAM-parameteröverbelastningar på en given Java-metod. Denna SAM-tvetydighet förvirrar Kotlin-kompilatorn, eftersom det inte kan utgöra vilket gränssnitt det ska konvertera, och ditt projekt kommer inte att kompilera som ett resultat.

Denna SAM-tvetydighet är ett särskilt problem vid användning av RxJava 2.0 med Kotlin, eftersom många av RxJava-operatörerna använder flera SAM-kompatibla typer.

Låt oss titta på SAM-konverteringsproblemet i aktion. I följande kod använder vi blixtlås() operatör för att kombinera utgången av två observables:  

importera androidx.appcompat.app.AppCompatActivity importera android.os.Bundle import io.reactivex.Observable import kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () överstyra roligt onCreate (savedInstanceState: Bundle?) super .onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Starta strömmen när knappen klickar // button.setOnClickListener startRStream () privata roliga startRStream () val numbers = Observable.range (1, 6 ) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observable.zip (strängar, siffror) s, n -> " $ s $ n " zipped.subscribe (:: println)

Detta kommer att leda till att Kotlin-kompilatorn slänger ett typfel. RxKotlin tillhandahåller dock hjälpar metoder och förlängningsfunktioner för de drabbade operatörerna, inklusive Observables.zip (), som vi använder i följande kod:

importera android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.Observable import io.reactivex.rxkotlin.Observables importera kotlinx.android.synthetic.main.activity_main. * class MainActivity: AppCompatActivity () överträffa kul onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Starta strömmen när knappen klickar // button.setOnClickListener startRStream () privata roliga startRStream () val nummer = Observable.range (1, 6) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observables.zip ) s, n -> "$ s $ n" zipped.subscribe (:: println)

Här är resultatet av den här koden:

Slutsats

I den här handledningen visade jag dig hur du börjar använda RxJava-biblioteket i dina Kotlin-projekt, inklusive att använda ett antal ytterligare stödjande bibliotek, till exempel RxKotlin och RxBinding. Vi tittade på hur du kan skapa enkla observatörer och observatörer i Kotlin, genom att optimera RxJava för Kotlin-plattformen, med hjälp av förlängningsfunktioner.

Hittills har vi använt RxJava för att skapa enkla observables som avger data och observatörer som skriver ut dessa data till Android Studio's Logcat-men det här är inte hur du ska använda RxJava i den verkliga världen!

I nästa inlägg ska vi titta på hur RxJava kan hjälpa till att lösa verkliga problem som du kommer att stöta på när du utvecklar Android-applikationer. Vi använder RxJava med Kotlin för att skapa en klassiker Bli Medlem skärm.