Вступление
Разработка сложного приложения для Android, которое имеет множество сетевых подключений, взаимодействий с пользователем и анимации, часто означает написание кода, заполненного вложенными обратными вызовами. Такой код, иногда называемый адом обратного вызова , не только длинный и трудный для понимания, но и подвержен ошибкам. ReactiveX предлагает альтернативный подход, который является одновременно ясным и лаконичным для управления асинхронными задачами и событиями.
RxJava — это JVM-реализация ReactiveX, разработанная NetFlix и очень популярная среди разработчиков Java. Из этого руководства вы узнаете, как использовать привязки RxJava для Android или, если коротко, RxAndroid , в своих проектах Android.
1. Настройка RxAndroid
Чтобы использовать RxAndroid в проекте Android Studio, добавьте его в качестве зависимости compile
в build.gradle модуля приложения .
java compile 'io.reactivex:rxandroid:0.25.0'
2. Основы наблюдателей и наблюдаемых
При работе с ReactiveX вы будете широко использовать наблюдаемые и наблюдателей. Вы можете рассматривать наблюдаемое как объект, который испускает данные, а наблюдателя — как объект, который потребляет эти данные. В RxJava и RxAndroid наблюдатели являются экземплярами интерфейса Observer
, а наблюдаемые — экземплярами класса Observable
.
Класс Observable
имеет много статических методов, называемых операторами , для создания объектов Observable
. В следующем коде показано, как использовать оператор just
для создания очень простого Observable
которое генерирует одну String
.
java Observable<String> myObservable = Observable.just("Hello"); // Emits "Hello"
Наблюдение, которое мы только что создали, будет излучать свои данные только тогда, когда у него есть хотя бы один наблюдатель. Чтобы создать наблюдателя, вы создаете класс, который реализует интерфейс Observer
. Интерфейс Observer
имеет интуитивно названные методы для обработки различных типов уведомлений, которые он может получать от наблюдаемой. Вот наблюдатель, который может напечатать String
излучаемую наблюдаемой нами ранее:
« `Наблюдатель Java
@Override public void onError (Throwable e) { // Вызывается, когда наблюдаемое встречает ошибку } @Override public void onNext (String s) { // Вызывается каждый раз, когда наблюдаемое излучает данные Log.d («Мой наблюдатель», с); }}; `` `
Чтобы назначить наблюдателя наблюдаемой, вы должны использовать метод subscribe
, который возвращает объект Subscription
. Следующий код заставляет myObserver
наблюдать myObservable
:
java Subscription mySubscription = myObservable.subscribe(myObserver);
Как только наблюдатель добавляется к наблюдаемой, он отправляет свои данные. Поэтому, если вы выполните код сейчас, вы увидите Hello, напечатанный в окне logcat Android Studio.
Вы могли заметить, что мы не использовали методы onCompleted
и onError
в myObserver
. Поскольку эти методы часто остаются неиспользованными, у вас также есть возможность использовать интерфейс Action1
, который содержит единственный метод с именем call
.
java Action1<String> myAction = new Action1<String>() { @Override public void call(String s) { Log.d("My Action", s); } };
Когда вы передаете экземпляр Action1
методу subscribe
метод call
вызывается всякий раз, когда наблюдаемое излучает данные.
java Subscription mySubscription = myObservable.subscribe(myAction1);
Чтобы отсоединить наблюдателя от его наблюдаемой, когда наблюдаемая все еще излучает данные, вы можете вызвать метод unsubscribe
от Subscription
объекта Subscription
.
java mySubscription.unsubscribe();
3. Использование операторов
Теперь, когда вы знаете, как создавать наблюдатели и наблюдаемые, позвольте мне показать вам, как использовать операторы ReactiveX, которые могут создавать, преобразовывать и выполнять другие операции с наблюдаемыми. Давайте начнем с создания чуть более продвинутого Observable
, который генерирует элементы из массива объектов Integer
. Для этого вы должны использовать оператор from
, который может генерировать Observable
из массивов и списков.
« `Ява Наблюдаемая
myArrayObservable.subscribe (новый Action1
Когда вы запустите этот код, вы увидите каждое из чисел массива, напечатанных одно за другим.
Если вы знакомы с JavaScript, Ruby или Kotlin, вы можете быть знакомы с функциями более высокого порядка, такими как map
и filter
, которые можно использовать при работе с массивами. В ReactiveX есть операторы, которые могут выполнять аналогичные операции с наблюдаемыми. Однако, поскольку в Java 7 нет лямбд и функций более высокого порядка, нам придется делать это с классами, имитирующими лямбды. Чтобы моделировать лямбду, которая принимает один аргумент, вам нужно создать класс, который реализует интерфейс Func1
.
Вот как вы можете использовать оператор map
для myArrayObservable
в квадрат каждого элемента myArrayObservable
:
java myArrayObservable = myArrayObservable.map(new Func1<Integer, Integer>() { // Input and Output are both Integer @Override public Integer call(Integer integer) { return integer * integer; // Square the number } });
Обратите внимание, что вызов оператора map
возвращает новый объект Observable
, он не меняет исходный Observable
. Если вы подпишитесь на myArrayObservable
сейчас, вы получите квадраты чисел.
Операторы могут быть связаны. Например, следующий блок кода использует оператор skip
чтобы пропустить первые два числа, а затем оператор filter
чтобы игнорировать нечетные числа:
« `java myArrayObservable .skip (2) // Пропускаем первые два элемента .filter (new Func1 <Integer, Boolean> () {@Override public boolean call (Integer integer) {// Игнорирует любой элемент, который возвращает ложное возвращаемое целое число % 2 == 0;}});
// испускает 4 и 6 « `
4. Обработка асинхронных заданий
Наблюдатели и наблюдаемые, которые мы создали в предыдущих разделах, работали над одним потоком — потоком пользовательского интерфейса Android. В этом разделе я покажу вам, как использовать ReactiveX для управления несколькими потоками и как ReactiveX решает проблему ада обратного вызова.
Предположим, у вас есть метод с именем fetchData
который можно использовать для извлечения данных из API. Допустим, он принимает URL-адрес в качестве своего параметра и возвращает содержимое ответа в виде String
. Следующий фрагмент кода показывает, как его можно использовать.
java String content = fetchData("http://www.google.com"); // fetches the contents of google.com as a String
Этот метод должен выполняться в собственном потоке, поскольку Android не разрешает сетевые операции в потоке пользовательского интерфейса. Это означает, что вы либо создадите AsyncTask
либо создадите AsyncTask
, использующий Handler
.
Однако, с ReactiveX у вас есть третий вариант, который немного более лаконичен. Используя операторы observeOn
и observeOn
, вы можете явно указать, какой поток должен выполнять фоновое задание, а какой должен обрабатывать обновления пользовательского интерфейса.
Следующий код создает пользовательскую Observable
с помощью оператора create
. Когда вы создаете Observable
таким образом, вы должны реализовать интерфейс Observable.OnSubscribe
и управлять тем, что он излучает, вызывая onNext
, onError
и onCompleted
самостоятельно.
java Observable<String> fetchFromGoogle = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { try { String data = fetchData("http://www.google.com"); subscriber.onNext(data); // Emit the contents of the URL subscriber.onCompleted(); // Nothing more to emit }catch(Exception e){ subscriber.onError(e); // In case there are network errors } } });
Когда Observable
готов, вы можете использовать observeOn
и observeOn
чтобы указать потоки, которые он должен использовать, и подписаться на него.
java fetchFromGoogle .subscribeOn(Schedulers.newThread()) // Create a new Thread .observeOn(AndroidSchedulers.mainThread()) // Use the UI thread .subscribe(new Action1<String>() { @Override public void call(String s) { view.setText(view.getText() + "\n" + s); // Change a View } });
Вы все еще можете подумать, что реактивный подход не намного лучше, чем использование классов AsyncTask
или Handler
. Вы правы, вам не нужен ReactiveX, если вам нужно управлять только одной фоновой работой.
Теперь рассмотрим сценарий, который привел бы к сложной кодовой базе, если бы вы использовали обычный подход. Допустим, вам нужно получать данные с двух (или более) веб-сайтов параллельно и обновлять представление только после завершения всех запросов. Если вы следуете традиционному подходу, вам придется написать много ненужного кода, чтобы убедиться, что запросы выполнены без ошибок.
Рассмотрим другой сценарий, в котором вы должны запускать фоновое задание только после завершения другого фонового задания. Используя традиционный подход, это привело бы к вложенным обратным вызовам.
С операторами ReactiveX оба сценария могут быть обработаны с очень небольшим количеством кода. Например, если вам нужно использовать fetchData
для извлечения содержимого двух веб-сайтов, например, Google и Yahoo, вы должны создать два объекта Observable
и использовать метод subscribeOn
чтобы они запускались в разных потоках.
java fetchFromGoogle = fetchFromGoogle.subscribeOn(Schedulers.newThread()); fetchFromYahoo = fetchFromYahoo.subscribeOn(Schedulers.newThread());
Для обработки первого сценария, в котором оба запроса должны выполняться параллельно, вы можете использовать оператор zip
и подписаться на Observable
он возвращает.
java // Fetch from both simultaneously Observable<String> zipped = Observable.zip(fetchFromGoogle, fetchFromYahoo, new Func2<String, String, String>() { @Override public String call(String google, String yahoo) { // Do something with the results of both threads return google + "\n" + yahoo; } });
Аналогично, для обработки второго сценария вы можете использовать оператор concat
для запуска потоков один за другим.
java Observable<String> concatenated = Observable.concat(fetchFromGoogle, fetchFromYahoo); // Emit the results one after another
5. Обработка событий
RxAndroid имеет класс ViewObservable
который облегчает обработку событий, связанных с объектами View
. В следующем фрагменте кода показано, как создать ViewObservable
который можно использовать для обработки событий нажатия Button
.
« `java Button myButton = (Button) findViewById (R.id.my_button); // Создать кнопку из макета
наблюдаемый
Теперь вы можете подписаться на clicksObservable
и использовать любой из операторов, о которых вы узнали в предыдущих разделах. Например, если вы хотите, чтобы ваше приложение пропускало первые четыре нажатия кнопки и начинало отвечать с пятого щелчка и далее, вы можете использовать следующую реализацию:
java clicksObservable .skip(4) // Skip the first 4 clicks .subscribe(new Action1<OnClickEvent>() { @Override public void call(OnClickEvent onClickEvent) { Log.d("Click Action", "Clicked!"); // Printed from the fifth click onwards } });
Вывод
Из этого руководства вы узнали, как использовать наблюдатели, наблюдаемые и операторы ReactiveX для обработки нескольких асинхронных операций и событий. Поскольку работа с ReactiveX включает в себя функциональное, реактивное программирование, парадигма программирования, к которой большинство разработчиков Android не привыкли, не будьте слишком строги к себе, если вы не поняли это правильно с первого раза. Вы также должны знать, что код ReactiveX будет намного более читабельным, если вы используете современный язык программирования, такой как Kotlin, который поддерживает функции более высокого порядка.
Чтобы узнать больше о реактивных расширениях, я советую вам просмотреть ресурсы, доступные на ReactiveX .