Статьи

Начало работы с ReactiveX на Android

Разработка сложного приложения для Android, которое имеет множество сетевых подключений, взаимодействий с пользователем и анимации, часто означает написание кода, заполненного вложенными обратными вызовами. Такой код, иногда называемый адом обратного вызова , не только длинный и трудный для понимания, но и подвержен ошибкам. ReactiveX предлагает альтернативный подход, который является одновременно ясным и лаконичным для управления асинхронными задачами и событиями.

RxJava — это JVM-реализация ReactiveX, разработанная NetFlix и очень популярная среди разработчиков Java. Из этого руководства вы узнаете, как использовать привязки RxJava для Android или, если коротко, RxAndroid , в своих проектах Android.

Чтобы использовать RxAndroid в проекте Android Studio, добавьте его в качестве зависимости compile в build.gradle модуля приложения .

java compile 'io.reactivex:rxandroid:0.25.0'

При работе с ReactiveX вы будете широко использовать наблюдаемые и наблюдателей. Вы можете рассматривать наблюдаемое как объект, который испускает данные, а наблюдателя — как объект, который потребляет эти данные. В RxJava и RxAndroid наблюдатели являются экземплярами интерфейса Observer , а наблюдаемые — экземплярами класса Observable .

Класс Observable имеет много статических методов, называемых операторами , для создания объектов Observable . В следующем коде показано, как использовать оператор just для создания очень простого Observable которое генерирует одну String .

java Observable<String> myObservable = Observable.just("Hello"); // Emits "Hello"

Наблюдение, которое мы только что создали, будет излучать свои данные только тогда, когда у него есть хотя бы один наблюдатель. Чтобы создать наблюдателя, вы создаете класс, который реализует интерфейс Observer . Интерфейс Observer имеет интуитивно названные методы для обработки различных типов уведомлений, которые он может получать от наблюдаемой. Вот наблюдатель, который может напечатать String излучаемую наблюдаемой нами ранее:

« `Наблюдатель Java myObserver = новый наблюдатель () {@Override public void onCompleted () {// Вызывается, когда у наблюдаемой больше нет данных для отправки}

Чтобы назначить наблюдателя наблюдаемой, вы должны использовать метод 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();

Теперь, когда вы знаете, как создавать наблюдатели и наблюдаемые, позвольте мне показать вам, как использовать операторы ReactiveX, которые могут создавать, преобразовывать и выполнять другие операции с наблюдаемыми. Давайте начнем с создания чуть более продвинутого Observable , который генерирует элементы из массива объектов Integer . Для этого вы должны использовать оператор from , который может генерировать Observable из массивов и списков.

« `Ява Наблюдаемая myArrayObservable = Observable.from (new Integer [] {1, 2, 3, 4, 5, 6}); // Выдает каждый элемент массива, по одному

myArrayObservable.subscribe (новый Action1 () {@Override public void call (Integer i) {Log.d («Мое действие», String.valueOf (i)); // печатает полученный номер}}); « `

Когда вы запустите этот код, вы увидите каждое из чисел массива, напечатанных одно за другим.

Если вы знакомы с 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 « `

Наблюдатели и наблюдаемые, которые мы создали в предыдущих разделах, работали над одним потоком — потоком пользовательского интерфейса 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

RxAndroid имеет класс ViewObservable который облегчает обработку событий, связанных с объектами View . В следующем фрагменте кода показано, как создать ViewObservable который можно использовать для обработки событий нажатия Button .

« `java Button myButton = (Button) findViewById (R.id.my_button); // Создать кнопку из макета

наблюдаемый clicksObservable = ViewObservable.clicks (myButton); // Создаем ViewObservable для кнопки « `

Теперь вы можете подписаться на 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 .