Kotlin Reactive Programming con RxJava e RxKotlin

Da quando è diventato un linguaggio ufficialmente supportato per lo sviluppo di Android, Kotlin è cresciuto rapidamente in popolarità tra gli sviluppatori Android, con Google che registra un aumento di 6 volte delle applicazioni create utilizzando Kotlin.

Se in precedenza hai usato RxJava o RxAndroid e vuoi passare a Kotlin, o vuoi iniziare la programmazione reattiva con Kotlin, questo tutorial fa per te. Tratteremo gli elementi essenziali per la creazione di RxJava 2.0 Gli osservatori, osservabili e flussi di dati in Kotlin, prima di esaminare come è possibile tagliare una tonnellata di codice boilerplate dai progetti, combinando RxJava con le funzioni di estensione di Kotlin.

L'utilizzo di RxJava con Kotlin può aiutarti a creare app altamente reattive con meno codice, ma nessun linguaggio di programmazione è perfetto, quindi condividerò anche una soluzione per il problema di conversione SAM che molti sviluppatori incontrano quando iniziano a utilizzare RxJava 2.0 con Kotlin.

Per concludere, creeremo un'applicazione che dimostra come utilizzare RxJava per risolvere alcuni dei problemi riscontrati nei progetti Android reali..

Se questo è il tuo primo assaggio di RxJava, poi lungo il percorso fornirò anche tutte le informazioni di base necessarie per comprendere i concetti fondamentali di RxJava. Anche se non hai mai provato prima RxJava, alla fine di questo articolo avrai una solida conoscenza di come usare questa libreria nei tuoi progetti, e avrai creato diverse app funzionanti, usando RxJava, RxKotlin, RxAndroid e RxBinding.

Che cos'è RxJava, comunque?

RxJava è un'implementazione open-source della libreria ReactiveX che ti aiuta a creare applicazioni nello stile di programmazione reattivo. Sebbene RxJava sia progettato per elaborare flussi di dati sincroni e asincroni, non è limitato ai tipi di dati "tradizionali". La definizione di "dati" di RxJava è piuttosto ampia e include cose come cache, variabili, proprietà e persino eventi di input dell'utente come clic e scorrimenti. Solo perché l'applicazione non gestisce numeri enormi o esegue complesse trasformazioni di dati, ciò non significa che non possa beneficiare di RxJava!

Per un po 'di esperienza sull'uso di RxJava per le app Android, puoi controllare alcuni dei miei altri post qui su Envato Tuts+.

Quindi, come funziona RxJava?

RxJava estende il modello di progettazione del software Observer, che si basa sul concetto di osservatori e osservabili. Per creare una pipeline di dati RxJava di base, è necessario:

  • Crea un osservabile.
  • Dare all'osservabile alcuni dati da emettere.  
  • Crea un osservatore.
  • Sottoscrivi l'Observer to the Observable.

Non appena l'Observable ha almeno un Observer, inizierà a emettere dati. Ogni volta che l'Observable emette un dato, notificherà l'Observer assegnato chiamando il onNext () metodo, e l'Observer eseguirà in genere alcune azioni in risposta a questa emissione di dati. Una volta che l'osservabile ha finito di emettere i dati, notificherà l'osservatore chiamando onComplete (). L'Observable terminerà e il flusso di dati terminerà.

Se si verifica un'eccezione, quindi onError () verrà chiamato e l'Observable terminerà immediatamente senza emettere altri dati o chiamate onComplete ().

Ma RxJava no appena sul passaggio di dati da un osservabile a un osservatore! RxJava ha una vasta collezione di operatori che puoi usare per filtrare, unire e trasformare questi dati. Ad esempio, immagina che la tua app abbia un Paga ora pulsante che rileva al clic eventi e temono che un utente impaziente possa toccare il pulsante più volte, facendo sì che l'app elabori diversi pagamenti.  

RxJava ti consente di trasformarli al clic eventi in un flusso di dati, che è possibile quindi manipolare utilizzando i vari operatori di RxJava. In questo particolare esempio, potresti usare il antirimbalzo () operatore per filtrare le emissioni di dati che si verificano in rapida successione, quindi anche se l'utente si lava via Paga ora pulsante, la tua app registrerà sempre un unico pagamento.

Quali sono i vantaggi nell'utilizzo di RxJava?

Abbiamo visto come RxJava può aiutarti a risolvere un problema specifico, in un'applicazione specifica, ma cosa ha da offrire per i progetti Android, in generale?

RxJava può aiutare a semplificare il codice dandoti modo di scrivere ciò che vuoi ottenere, piuttosto che scrivere un elenco di istruzioni che l'applicazione deve elaborare. Ad esempio, se si desidera ignorare tutte le emissioni di dati che si verificano nello stesso periodo di 500 millisecondi, si dovrebbe scrivere:

.rimbalzo (500, TimeUnit.MILLISECONDS)

Inoltre, dal momento che RxJava tratta quasi tutto come dati, fornisce un modello che è possibile applicare a una vasta gamma di eventi: creare un osservabile, creare un osservatore, sottoscrivere l'osservatore all'osservabile, risciacquare e ripetere. Questo approccio formulaico si traduce in un codice molto più diretto e leggibile dall'uomo.

L'altro grande vantaggio per gli sviluppatori Android è che RxJava può assorbire gran parte del dolore del multithreading su Android. Gli utenti mobili di oggi si aspettano che le loro applicazioni siano in grado di multitasking, anche se è qualcosa di semplice come scaricare dati in background pur rimanendo reattivi all'input dell'utente.

Android ha diverse soluzioni integrate per la creazione e la gestione di più thread, ma nessuno di questi è particolarmente facile da implementare e possono rapidamente produrre un codice complesso e dettagliato, difficile da leggere e soggetto a errori.

In RxJava, si creano e gestiscono thread aggiuntivi utilizzando una combinazione di operatori e programmatori. È possibile modificare facilmente il thread in cui viene eseguito il lavoro, utilizzando il subscribeOn operatore più un programmatore. Ad esempio, qui stiamo pianificando il lavoro da eseguire su un nuovo thread:

.subscribeOn (Schedulers.newThread ())

È possibile specificare dove devono essere pubblicati i risultati di questo lavoro, utilizzando il observeOn operatore. Qui, pubblichiamo i risultati sull'intero thread principale dell'interfaccia utente di Android, utilizzando il comando AndroidSchedulers.mainThread scheduler, che è disponibile come parte della libreria RxAndroid:

.observeOn (AndroidSchedulers.mainThread ())

Rispetto alle soluzioni di multithreading integrate di Android, l'approccio di RxJava è tanto più conciso e più facile da capire.

Di nuovo, puoi imparare di più su come funziona RxJava e i vantaggi di aggiungere questa libreria al tuo progetto, nel mio articolo Get Started With RxJava 2 per Android.

Dovrei usare RxJava o RxKotlin?

Poiché Kotlin è al 100% interoperabile con Java, puoi utilizzare la maggior parte delle librerie Java nei tuoi progetti Kotlin senza difficoltà e la libreria RxJava non fa eccezione.

è una libreria RxKotlin dedicata, che è un wrapper Kotlin attorno alla normale libreria RxJava. Questo wrapper fornisce estensioni che ottimizzano RxJava per l'ambiente Kotlin e possono ridurre ulteriormente la quantità di codice boilerplate che è necessario scrivere.

Dato che puoi usare RxJava in Kotlin senza aver mai bisogno di RxKotlin, useremo RxJava in questo articolo, a meno che non sia specificato diversamente.

Creazione di semplici osservatori e osservabili in Kotlin

Gli osservatori e gli osservabili sono gli elementi costitutivi di RxJava, quindi iniziamo creando:

  • Un osservabile semplice che emette un breve flusso di dati in risposta a un evento click del pulsante.
  • Un osservabile che reagisce a questi dati stampando diversi messaggi su Android Studio logcat.

Crea un nuovo progetto con le impostazioni di tua scelta, ma assicurati di selezionare il Include il supporto Kotlin casella di controllo quando richiesto. Quindi, apri il tuo progetto build.gradle file e aggiungi la libreria RxJava come dipendenza del progetto:

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

Quindi apri il tuo progetto activity_main.xml file e aggiungi il pulsante che avvierà il flusso di dati:

  

Esistono diversi modi per creare un Osservabile, ma uno dei più facili è usare il appena() operatore per convertire un oggetto o un elenco di oggetti in un Osservabile.

Nel codice seguente, stiamo creando un Observable (myObservable) e dandogli gli elementi 1, 2, 3, 4 e 5 da emettere. Stiamo anche creando un osservatore (myObserver), abbonandolo a myObservable, e poi dirgli di stampare un messaggio a logcat ogni volta riceve una nuova emissione.

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 . * class MainActivity: AppCompatActivity () private var TAG = "MainActivity" sovrascrive fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Avvia lo stream quando si fa clic sul pulsante // button.setOnClickListener startRStream () fun private startRStream () // Crea un osservabile // val myObservable = getObservable () // Crea un osservatore // val myObserver = getObserver () // Sottoscrivi myObserver su myObservable / / myObservable .subscribe (myObserver) fun private getObserver (): Observer return object: Observer override fun onSubscribe (d: Disposable)  // Ogni volta che onNext viene chiamato, stampa il valore su Android Studio's Logcat // override fun onNext (s: String) Log.d (TAG, "onNext: $ s")  // Chiamato se viene lanciata un'eccezione // override fun onError (e: Throwable) Log.e (TAG, "onError:" + e.message) // Quando viene chiamato onComplete, stampare quanto segue in Logcat // override fun onComplete () Log.d (TAG, "onComplete") // Dai a myObservable alcuni dati da emettere // private fun getObservable (): Observable return Observable.just ("1", "2", "3", "4", "5")

Ora puoi mettere questa applicazione alla prova:

  • Installa il tuo progetto su uno smartphone o tablet Android fisico o su un dispositivo virtuale Android (AVD).
  • Dai il Avvia stream RxJava pulsante un clic.
  • Apri il monitor Logcat di Android Studio selezionando Monitor Android scheda (dove il cursore è posizionato nello screenshot seguente) e quindi selezionare il logcat linguetta.

A questo punto, l'Observable inizierà a emettere i suoi dati e l'Observer stamperà i suoi messaggi su Logcat. L'output di Logcat dovrebbe essere simile a questo:

Puoi scaricare questo progetto da GitHub se vuoi provarlo tu stesso.

Estensioni di Kotlin per RxJava

Ora che abbiamo visto come impostare una semplice pipeline RxJava in Kotlin, diamo un'occhiata a come è possibile ottenere questo risultato Di meno codice, usando le funzioni di estensione di RxKotlin.

Per utilizzare la libreria RxKotlin, è necessario aggiungerla come dipendenza del progetto:

dependencies implementation fileTree (dir: 'libs', include: ['* .jar']) implementazione "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementazione 'androidx.appcompat: appcompat: 1.0.0- alpha1 'implementation' androidx.constraintlayout: constraintlayout: 1.1.0 'implementazione' io.reactivex.rxjava2: rxjava: 2.1.9 '// Aggiungi la seguente // implementazione' io.reactivex.rxjava2: rxkotlin: 2.2.0 '

Nel seguente esempio, stiamo usando RxKotlin toObservable () funzione di estensione per trasformare a Elenco in un osservabile. Stiamo anche utilizzando il subscribeBy () funzione di estensione, in quanto ci consente di costruire un Observer usando argomenti con nome, il che risulta in un codice più chiaro.

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import io.reactivex.rxkotlin.subscribeImporta 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) // Avvia lo stream quando si fa clic sul pulsante // button.setOnClickListener startRStream () fun private startRStream ()  val list = listOf ("1", "2", "3", "4", "5") // Applica la funzione di estensione toObservable () // list.toObservable () // Costruisci il tuo Observer usando subscribeBy ( ) funzione di estensione // .subscribeBy (onNext = println (it), onError = it.printStackTrace (), onComplete = println ("onComplete!"))

Ecco l'output che dovresti vedere:

Risoluzione del problema SAM Ambiguity di RxJava

RxKotlin fornisce anche un'importante soluzione alternativa al problema di conversione SAM che può verificarsi quando sono presenti più overload di parametri SAM su un determinato metodo Java. Questa ambiguità di SAM confonde il compilatore di Kotlin, in quanto non riesce a capire quale interfaccia deve convertire e il tuo progetto non riuscirà a compilare di conseguenza.

Questa ambiguità SAM è un problema particolare quando si utilizza RxJava 2.0 con Kotlin, poiché molti degli operatori RxJava accettano più tipi compatibili con SAM.

Diamo un'occhiata al problema di conversione SAM in azione. Nel seguente codice, stiamo usando il cerniera lampo() operatore per combinare l'output di due oggetti osservabili:  

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) // Avvia lo stream quando si fa clic sul pulsante // button.setOnClickListener startRStream () fun private startRStream () val numbers = Observable.range (1, 6 ) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observable.zip (stringhe, numeri) s, n -> " $ s $ n " zipped.subscribe (:: println)

Questo farà sì che il compilatore di Kotlin lanci un errore di inferenza di tipo. Tuttavia, RxKotlin fornisce metodi di supporto e funzioni di estensione per gli operatori interessati, inclusi Observables.zip (), che stiamo usando nel seguente codice:

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: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Avvia lo stream quando si fa clic sul pulsante // button.setOnClickListener startRStream () fun private startRStream () numeri val = Observable.range (1, 6) val strings = Observable.just ("One", "Two", "Three", "Four", "Five", "Six") val zipped = Observables.zip (stringhe, numeri ) s, n -> "$ s $ n" zipped.subscribe (:: println)

Ecco l'output di questo codice:

Conclusione

In questo tutorial, ti ho mostrato come iniziare a utilizzare la libreria RxJava nei tuoi progetti Kotlin, incluso l'utilizzo di un numero di librerie di supporto aggiuntive, come RxKotlin e RxBinding. Abbiamo esaminato come è possibile creare osservatori e osservabili semplici in Kotlin, fino all'ottimizzazione di RxJava per la piattaforma Kotlin, utilizzando le funzioni di estensione.

Finora, abbiamo usato RxJava per creare semplici osservabili che emettono dati e osservatori che stampano questi dati su Logcat di Android Studio, ma non è così che utilizzerai RxJava nel mondo reale!

Nel prossimo post, vedremo come RxJava può aiutare a risolvere problemi reali che incontrerai quando sviluppi applicazioni Android. Useremo RxJava con Kotlin per creare un classico Iscriviti schermo.