Lernen Sie RxJava kennen: Die fehlende reaktive Programmierbibliothek für Android

Veröffentlicht: 2022-03-11

Wenn Sie ein Android-Entwickler sind, haben Sie wahrscheinlich schon von RxJava gehört. Es ist eine der am meisten diskutierten Bibliotheken zur Aktivierung der reaktiven Programmierung in der Android-Entwicklung. Es wird als das Go-to-Framework zur Vereinfachung von Nebenläufigkeits-/asynchronen Aufgaben angepriesen, die der mobilen Programmierung innewohnen.

Aber… was ist RxJava und wie „vereinfacht“ es die Dinge?

Funktionale reaktive Programmierung für Android: Eine Einführung in RxJava

Entwirren Sie Ihr Android von zu vielen Java-Threads mit RxJava.
Twittern

Obwohl bereits viele Ressourcen online verfügbar sind, die erklären, was RxJava ist, möchte ich Ihnen in diesem Artikel eine grundlegende Einführung in RxJava geben und insbesondere, wie es in die Android-Entwicklung passt. Ich werde auch einige konkrete Beispiele und Vorschläge geben, wie Sie es in ein neues oder bestehendes Projekt integrieren können.

Warum sollten Sie RxJava in Betracht ziehen?

Im Kern vereinfacht RxJava die Entwicklung, da es die Abstraktionsebene rund um das Threading erhöht. Das heißt, als Entwickler müssen Sie sich nicht zu viele Gedanken darüber machen, wie Operationen ausgeführt werden, die in verschiedenen Threads ausgeführt werden sollen. Dies ist besonders attraktiv, da es schwierig ist, Threading richtig hinzubekommen, und wenn es nicht korrekt implementiert wird, kann es einige der schwierigsten Fehler verursachen, die zu debuggen und zu beheben sind.

Zugegeben, das bedeutet nicht, dass RxJava kugelsicher ist, wenn es um Threading geht, und es ist immer noch wichtig zu verstehen, was hinter den Kulissen passiert; RxJava kann Ihr Leben jedoch definitiv einfacher machen.

Schauen wir uns ein Beispiel an.

Netzwerkaufruf – RxJava vs. AsyncTask

Angenommen, wir möchten Daten über das Netzwerk abrufen und als Ergebnis die Benutzeroberfläche aktualisieren. Eine Möglichkeit besteht darin, (1) eine innere AsyncTask Unterklasse in unserem Activity / Fragment zu erstellen, (2) den Netzwerkvorgang im Hintergrund auszuführen und (3) das Ergebnis dieses Vorgangs zu nehmen und die Benutzeroberfläche im Hauptthread zu aktualisieren .

 public class NetworkRequestTask extends AsyncTask<Void, Void, User> { private final int userId; public NetworkRequestTask(int userId) { this.userId = userId; } @Override protected User doInBackground(Void... params) { return networkService.getUser(userId); } @Override protected void onPostExecute(User user) { nameTextView.setText(user.getName()); // ...set other views } } private void onButtonClicked(Button button) { new NetworkRequestTask(123).execute() }

So harmlos dies auch erscheinen mag, dieser Ansatz hat einige Probleme und Einschränkungen. Speicher-/Kontextlecks werden nämlich leicht erzeugt, da NetworkRequestTask eine innere Klasse ist und somit einen impliziten Verweis auf die äußere Klasse enthält. Und was ist, wenn wir eine weitere lange Operation nach dem Netzwerkaufruf verketten wollen? Wir müssten zwei AsyncTask s verschachteln, was die Lesbarkeit erheblich beeinträchtigen kann.

Im Gegensatz dazu könnte ein RxJava-Ansatz zum Durchführen eines Netzwerkaufrufs etwa so aussehen:

 private Subscription subscription; private void onButtonClicked(Button button) { subscription = networkService.getObservableUser(123) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<User>() { @Override public void call(User user) { nameTextView.setText(user.getName()); // ... set other views } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); }

Mit diesem Ansatz lösen wir das Problem (möglicher Speicherverluste, die durch einen laufenden Thread verursacht werden, der einen Verweis auf den äußeren Kontext enthält), indem wir einen Verweis auf das zurückgegebene Subscription Objekt beibehalten. Dieses Subscription -Objekt wird dann an die #onDestroy() Methode des Activity / Fragment gebunden, um sicherzustellen, dass die Action1#call Operation nicht ausgeführt wird, wenn die Activity /das Fragment zerstört werden muss.

Beachten Sie auch, dass der Rückgabetyp von #getObservableUser(...) (dh ein Observable<User> ) mit weiteren Aufrufen verkettet ist. Durch diese fließende API sind wir in der Lage, das zweite Problem bei der Verwendung einer AsyncTask zu lösen, nämlich dass sie weitere Netzwerkaufrufe/lange Verkettungen von Vorgängen ermöglicht. Ziemlich ordentlich, oder?

Lassen Sie uns tiefer in einige RxJava-Konzepte eintauchen.

Observable, Observer und Operator - Die 3 O's von RxJava Core

In der RxJava-Welt kann alles als Stream modelliert werden. Ein Stream gibt im Laufe der Zeit ein oder mehrere Elemente aus, und jede Emission kann konsumiert/beobachtet werden.

Wenn Sie darüber nachdenken, ist ein Stream kein neues Konzept: Klickereignisse können ein Stream sein, Standortaktualisierungen können ein Stream sein, Push-Benachrichtigungen können ein Stream sein und so weiter.

In der RxJava-Welt kann alles als Stream modelliert werden.

Die Stream-Abstraktion wird durch 3 Kernkonstrukte implementiert, die ich gerne „die 3 O's“ nenne; nämlich: das Observable, O bserver und der O perator . Das Observable gibt Items aus (der Stream); und der Beobachter verbraucht diese Gegenstände. Emissionen von beobachtbaren Objekten können weiter modifiziert, transformiert und manipuliert werden, indem Operator -Aufrufe verkettet werden.

Beobachtbar

Ein Observable ist die Stream-Abstraktion in RxJava. Es ähnelt einem Iterator darin, dass es bei einer gegebenen Sequenz iteriert und diese Elemente in geordneter Weise erzeugt. Ein Verbraucher kann diese Elemente dann unabhängig von der zugrunde liegenden Sequenz über dieselbe Schnittstelle konsumieren.

Angenommen, wir wollten die Zahlen 1, 2, 3 in dieser Reihenfolge ausgeben. Dazu können wir die Methode Observable<T>#create(OnSubscribe<T>) verwenden.

 Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { subscriber.onNext(1); subscriber.onNext(2); subscriber.onNext(3); subscriber.onCompleted(); } });

Durch Aufrufen von subscriber.onNext(Integer) wird ein Element im Stream ausgegeben, und wenn der Stream die Ausgabe beendet hat, wird subscriber.onCompleted() aufgerufen.

Dieser Ansatz zum Erstellen eines Observable ist ziemlich ausführlich. Aus diesem Grund gibt es bequeme Methoden zum Erstellen von Observable-Instanzen, die in fast allen Fällen bevorzugt werden sollten.

Der einfachste Weg, ein Observable zu erstellen, ist die Verwendung von Observable#just(...) . Wie der Methodenname andeutet, gibt es nur die Elemente aus, die Sie als Methodenargumente übergeben.

 Observable.just(1, 2, 3); // 1, 2, 3 will be emitted, respectively

Beobachter

Die nächste Komponente des Observable-Streams ist der Observer (oder die Observer), die ihn abonniert haben. Beobachter werden benachrichtigt, wenn etwas „Interessantes“ im Stream passiert. Beobachter werden über die folgenden Ereignisse benachrichtigt:

  • Observer#onNext(T) – wird aufgerufen, wenn ein Element aus dem Stream ausgegeben wird
  • Observable#onError(Throwable) – wird aufgerufen, wenn innerhalb des Streams ein Fehler aufgetreten ist
  • Observable#onCompleted() – wird aufgerufen, wenn der Stream die Ausgabe von Elementen beendet hat.

Um einen Stream zu abonnieren, rufen Sie einfach Observable<T>#subscribe(...) auf und übergeben Sie eine Observer-Instanz.

 Observable<Integer> observable = Observable.just(1, 2, 3); observable.subscribe(new Observer<Integer>() { @Override public void onCompleted() { Log.d("Test", "In onCompleted()"); } @Override public void onError(Throwable e) { Log.d("Test", "In onError()"); } @Override public void onNext(Integer integer) { Log.d("Test", "In onNext():" + integer); } });

Der obige Code gibt in Logcat Folgendes aus:

 In onNext(): 1 In onNext(): 2 In onNext(): 3 In onNext(): 4 In onCompleted()

Es kann auch Fälle geben, in denen wir nicht mehr an den Emissionen eines Observable interessiert sind. Dies ist besonders relevant in Android, wenn beispielsweise eine Activity / ein Fragment im Speicher zurückgefordert werden muss.

Um die Beobachtung von Elementen zu beenden, müssen wir einfach Subscription#unsubscribe() für das zurückgegebene Subscription-Objekt aufrufen.

 Subscription subscription = someInfiniteObservable.subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } }); // Call unsubscribe when appropriate subscription.unsubscribe();

Wie im obigen Code-Snippet zu sehen ist, halten wir beim Abonnieren eines Observables den Verweis auf das zurückgegebene Subscription-Objekt und rufen später bei Bedarf subscription#unsubscribe() auf. In Android wird dies am besten innerhalb von Activity#onDestroy() oder Fragment#onDestroy() .

Operator

Von einem Observable ausgegebene Elemente können durch Operatoren transformiert, modifiziert und gefiltert werden, bevor die abonnierten Observer-Objekte benachrichtigt werden. Einige der häufigsten Operationen der funktionalen Programmierung (z. B. Map, Filter, Reduce usw.) können auch auf einen Observable-Stream angewendet werden. Schauen wir uns die Karte als Beispiel an:

 Observable.just(1, 2, 3, 4, 5).map(new Func1<Integer, Integer>() { @Override public Integer call(Integer integer) { return integer * 3; } }).subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });

Das obige Code-Snippet würde jede Emission vom Observable nehmen und mit 3 multiplizieren, wodurch der Stream 3, 6, 9, 12, 15 entsteht. Das Anwenden eines Operators gibt normalerweise ein anderes Observable als Ergebnis zurück, was praktisch ist, da wir so mehrere Operationen verketten können, um ein gewünschtes Ergebnis zu erhalten.

Angenommen, wir wollten angesichts des obigen Streams nur gerade Zahlen erhalten. Dies kann durch Verketten einer Filteroperation erreicht werden.

 Observable.just(1, 2, 3, 4, 5).map(new Func1<Integer, Integer>() { @Override public Integer call(Integer integer) { return integer * 3; } }).filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }).subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });

Es gibt viele in das RxJava-Toolset integrierte Operatoren, die den Observable-Stream modifizieren; Wenn Sie sich vorstellen können, wie Sie den Stream ändern können, gibt es wahrscheinlich einen Operator dafür. Im Gegensatz zu den meisten technischen Dokumentationen ist das Lesen der RxJava/ReactiveX-Dokumentation ziemlich einfach und auf den Punkt gebracht. Jeder Operator in der Dokumentation enthält eine Visualisierung, wie sich der Operator auf den Stream auswirkt. Diese Visualisierungen werden „Marmordiagramme“ genannt.

So könnte ein hypothetischer Operator namens Flip durch ein Marmordiagramm modelliert werden:

Beispiel dafür, wie ein hypothetischer Operator namens Flip durch ein Marmordiagramm modelliert werden könnte.

Multithreading mit RxJava

Die Steuerung des Threads, in dem Operationen in der Observable-Kette auftreten, erfolgt durch Angabe des Schedulers, in dem ein Operator auftreten soll. Im Wesentlichen können Sie sich einen Scheduler als einen Thread-Pool vorstellen, den ein Operator verwendet und auf dem er ausgeführt wird, wenn er angegeben wird. Wenn kein solcher Scheduler bereitgestellt wird, arbeitet die Observable-Kette standardmäßig auf demselben Thread, in dem Observable#subscribe(...) aufgerufen wird. Andernfalls kann ein Scheduler über Observable#subscribeOn(Scheduler) und/oder Observable#observeOn(Scheduler) angegeben werden, wobei die geplante Operation auf einem vom Scheduler ausgewählten Thread stattfindet.

Der Hauptunterschied zwischen den beiden Methoden besteht darin, dass Observable#subscribeOn(Scheduler) das Quell-Observable anweist, auf welchem ​​Scheduler es ausgeführt werden soll. Die Kette wird weiterhin auf dem Thread von dem in Observable#subscribeOn(Scheduler) angegebenen Scheduler ausgeführt, bis ein Aufruf von Observable#observeOn(Scheduler) mit einem anderen Scheduler erfolgt. Wenn ein solcher Aufruf getätigt wird, erhalten alle Beobachter von da an (dh nachfolgende Operationen in der Kette) Benachrichtigungen in einem Thread, der von dem observeOn Scheduler genommen wird.

Hier ist ein Marmordiagramm, das zeigt, wie sich diese Methoden darauf auswirken, wo Operationen ausgeführt werden:

Ein Marmordiagramm, das zeigt, wie sich diese Methoden darauf auswirken, wo Operationen ausgeführt werden.

Wenn im Kontext von Android eine UI-Operation als Ergebnis einer langen Operation stattfinden muss, möchten wir, dass diese Operation im UI-Thread stattfindet. Zu diesem Zweck können wir AndroidScheduler#mainThread() verwenden, einen der in der RxAndroid-Bibliothek bereitgestellten Scheduler.

RxJava auf Android

Jetzt, da wir einige der Grundlagen kennengelernt haben, fragen Sie sich vielleicht – wie lässt sich RxJava am besten in eine Android-Anwendung integrieren? Wie Sie sich vorstellen können, gibt es viele Anwendungsfälle für RxJava, aber in diesem Beispiel werfen wir einen Blick auf einen bestimmten Fall: die Verwendung von beobachtbaren Objekten als Teil des Netzwerkstapels.

In diesem Beispiel sehen wir uns Retrofit an, einen Open-Source-HTTP-Client von Square, der über integrierte Bindungen mit RxJava verfügt, um mit der API von GitHub zu interagieren. Insbesondere erstellen wir eine einfache App, die alle markierten Repositories für einen Benutzer mit einem GitHub-Benutzernamen anzeigt. Wenn Sie weiter springen möchten, ist der Quellcode hier verfügbar.

Erstellen Sie ein neues Android-Projekt

  • Erstellen Sie zunächst ein neues Android-Projekt und nennen Sie es GitHubRxJava .

Screenshot: Erstellen Sie ein neues Android-Projekt

  • Lassen Sie im Bildschirm Ziel-Android-Geräte Telefon und Tablet ausgewählt und legen Sie die minimale SDK-Stufe von 17 fest. Sie können es gerne auf eine niedrigere/höhere API-Stufe einstellen, aber für dieses Beispiel ist API-Stufe 17 ausreichend.

Screenshot: Bildschirm Ziel-Android-Geräte

  • Wählen Sie in der nächsten Eingabeaufforderung Leere Aktivität aus.

Screenshot: Bildschirm „Aktivität zum Mobilgerät hinzufügen“.

  • Behalten Sie im letzten Schritt den Aktivitätsnamen als MainActivity bei und generieren Sie eine Layoutdatei activity_main .

Screenshot: Passen Sie den Aktivitätsbildschirm an

Projektaufbau

Fügen Sie RxJava, RxAndroid und die Retrofit-Bibliothek in app/build.gradle . Beachten Sie, dass das Einbeziehen von RxAndroid implizit auch RxJava einschließt. Es empfiehlt sich jedoch, diese beiden Bibliotheken immer explizit einzuschließen, da RxAndroid nicht immer die aktuellste Version von RxJava enthält. Die ausdrückliche Einbeziehung der neuesten Version von RxJava garantiert die Verwendung der aktuellsten Version.

 dependencies { compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'io.reactivex:rxandroid:1.2.0' compile 'io.reactivex:rxjava:1.1.8' // ...other dependencies }

Datenobjekt erstellen

Erstellen Sie die GitHubRepo -Datenobjektklasse. Diese Klasse kapselt ein Repository in GitHub (die Netzwerkantwort enthält mehr Daten, aber wir sind nur an einer Teilmenge davon interessiert).

 public class GitHubRepo { public final int id; public final String name; public final String htmlUrl; public final String description; public final String language; public final int stargazersCount; public GitHubRepo(int id, String name, String htmlUrl, String description, String language, int stargazersCount) { this.id = id; this.name = name; this.htmlUrl = htmlUrl; this.description = description; this.language = language; this.stargazersCount = stargazersCount; } }

Einrichtung Nachrüstung

  • Erstellen Sie die GitHubService Schnittstelle. Wir übergeben diese Schnittstelle an Retrofit und Retrofit erstellt eine Implementierung von GitHubService .
 public interface GitHubService { @GET("users/{user}/starred") Observable<List<GitHubRepo>> getStarredRepositories(@Path("user") String userName); }
  • Erstellen Sie die GitHubClient -Klasse. Dies ist das Objekt, mit dem wir interagieren, um Netzwerkaufrufe von der UI-Ebene aus zu tätigen.

    • Beim Erstellen einer Implementierung von GitHubService durch Retrofit müssen wir eine RxJavaCallAdapterFactory als Aufrufadapter übergeben, damit Netzwerkaufrufe beobachtbare Objekte zurückgeben können (das Übergeben eines Aufrufadapters ist für jeden Netzwerkaufruf erforderlich, der ein anderes Ergebnis als Call zurückgibt).

    • Wir müssen auch eine GsonConverterFactory , damit wir Gson als Möglichkeit verwenden können, JSON-Objekte in Java-Objekte zu marshallen.

 public class GitHubClient { private static final String GITHUB_BASE_URL = "https://api.github.com/"; private static GitHubClient instance; private GitHubService gitHubService; private GitHubClient() { final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); final Retrofit retrofit = new Retrofit.Builder().baseUrl(GITHUB_BASE_URL) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); gitHubService = retrofit.create(GitHubService.class); } public static GitHubClient getInstance() { if (instance == null) { instance = new GitHubClient(); } return instance; } public Observable<List<GitHubRepo>> getStarredRepos(@NonNull String userName) { return gitHubService.getStarredRepositories(userName); } }

Layouts einrichten

Erstellen Sie als Nächstes eine einfache Benutzeroberfläche, die die abgerufenen Repositorys mit einem eingegebenen GitHub-Benutzernamen anzeigt. Erstellen Sie activity_home.xml – das Layout für unsere Aktivität – mit etwas wie dem Folgenden:

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android: android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android: android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="@string/username"/> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/search"/> </LinearLayout> </LinearLayout>

Erstellen Sie item_github_repo.xml – das ListView -Elementlayout für das GitHub-Repository-Objekt – mit etwas wie dem Folgenden:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="6dp"> <TextView android: android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="24sp" android:text tools:text="Cropper"/> <TextView android: android:layout_width="match_parent" android:layout_height="wrap_content" android:lines="2" android:ellipsize="end" android:textSize="16sp" android:layout_below="@+id/text_repo_name" tools:text="Android widget for cropping and rotating an image."/> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/text_repo_description" android:layout_alignParentLeft="true" android:textColor="?attr/colorPrimary" android:textSize="14sp" android:text tools:text="Language: Java"/> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/text_repo_description" android:layout_alignParentRight="true" android:textColor="?attr/colorAccent" android:textSize="14sp" android:text tools:text="Stars: 1953"/> </RelativeLayout>

Alles zusammenkleben

Erstellen Sie einen ListAdapter , der für die Bindung von GitHubRepo Objekten an ListView Elemente zuständig ist. Der Prozess beinhaltet im Wesentlichen das Aufblasen item_github_repo.xml in eine View , wenn keine recycelte View bereitgestellt wird; andernfalls wird eine recycelte View wiederverwendet, um zu verhindern, dass zu viele View -Objekte zu stark aufgebläht werden.

 public class GitHubRepoAdapter extends BaseAdapter { private List<GitHubRepo> gitHubRepos = new ArrayList<>(); @Override public int getCount() { return gitHubRepos.size(); } @Override public GitHubRepo getItem(int position) { if (position < 0 || position >= gitHubRepos.size()) { return null; } else { return gitHubRepos.get(position); } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = (convertView != null ? convertView : createView(parent)); final GitHubRepoViewHolder viewHolder = (GitHubRepoViewHolder) view.getTag(); viewHolder.setGitHubRepo(getItem(position)); return view; } public void setGitHubRepos(@Nullable List<GitHubRepo> repos) { if (repos == null) { return; } gitHubRepos.clear(); gitHubRepos.addAll(repos); notifyDataSetChanged(); } private View createView(ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View view = inflater.inflate(R.layout.item_github_repo, parent, false); final GitHubRepoViewHolder viewHolder = new GitHubRepoViewHolder(view); view.setTag(viewHolder); return view; } private static class GitHubRepoViewHolder { private TextView textRepoName; private TextView textRepoDescription; private TextView textLanguage; private TextView textStars; public GitHubRepoViewHolder(View view) { textRepoName = (TextView) view.findViewById(R.id.text_repo_name); textRepoDescription = (TextView) view.findViewById(R.id.text_repo_description); textLanguage = (TextView) view.findViewById(R.id.text_language); textStars = (TextView) view.findViewById(R.id.text_stars); } public void setGitHubRepo(GitHubRepo gitHubRepo) { textRepoName.setText(gitHubRepo.name); textRepoDescription.setText(gitHubRepo.description); textLanguage.setText("Language: " + gitHubRepo.language); textStars.setText("Stars: " + gitHubRepo.stargazersCount); } } }

Kleben Sie alles in MainActivity zusammen. Dies ist im Wesentlichen die Activity , die angezeigt wird, wenn wir die App zum ersten Mal starten. Hier bitten wir den Benutzer, seinen GitHub-Benutzernamen einzugeben und schließlich alle markierten Repositories mit diesem Benutzernamen anzuzeigen.

 public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private GitHubRepoAdapter adapter = new GitHubRepoAdapter(); private Subscription subscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ListView listView = (ListView) findViewById(R.id.list_view_repos); listView.setAdapter(adapter); final EditText editTextUsername = (EditText) findViewById(R.id.edit_text_username); final Button buttonSearch = (Button) findViewById(R.id.button_search); buttonSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String username = editTextUsername.getText().toString(); if (!TextUtils.isEmpty(username)) { getStarredRepos(username); } } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); } private void getStarredRepos(String username) { subscription = GitHubClient.getInstance() .getStarredRepos(username) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<GitHubRepo>>() { @Override public void onCompleted() { Log.d(TAG, "In onCompleted()"); } @Override public void onError(Throwable e) { e.printStackTrace(); Log.d(TAG, "In onError()"); } @Override public void onNext(List<GitHubRepo> gitHubRepos) { Log.d(TAG, "In onNext()"); adapter.setGitHubRepos(gitHubRepos); } }); } }

Führen Sie die App aus

Beim Ausführen der App sollte ein Bildschirm mit einem Eingabefeld zur Eingabe eines GitHub-Benutzernamens angezeigt werden. Die Suche sollte dann die Liste aller markierten Repos anzeigen.

Screenshot der App mit einer Liste aller markierten Repos.

Fazit

Ich hoffe, dies dient als nützliche Einführung in RxJava und als Überblick über seine grundlegenden Fähigkeiten. Es gibt eine Menge mächtiger Konzepte in RxJava und ich fordere Sie auf, sie zu erforschen, indem Sie tiefer in das gut dokumentierte RxJava-Wiki eintauchen.

Fühlen Sie sich frei, Fragen oder Kommentare im Kommentarfeld unten zu hinterlassen. Sie können mir auch auf Twitter unter @arriolachris folgen, wo ich viel über RxJava und alles rund um Android twittere.

Wenn Sie eine umfassende Lernressource zu RxJava wünschen, können Sie sich das E-Book ansehen, das ich mit Angus Huang auf Leanpub geschrieben habe.

Verwandte: Zehn Kotlin-Funktionen zur Förderung der Android-Entwicklung