Недавно в проекте использовался фреймворк Rxjava, и он показался мне очень мощным.Поэтому я просмотрел много связанных статей в Интернете и нашел очень хорошую статью.Сегодня я поделюсь этой статьей с вами, спасибо оригинальному автору за его преданность, так что давайте все проверим эту статью:
предисловие
Я использую RxJava с прошлого года, и уже больше года. Присоединившись к Flipboard в этом году, я увидел, что Android-проект Flipboard также использует RxJava, и он используется во все большем количестве сценариев. В последние месяцы я также обнаружил, что все больше и больше людей в Китае начали упоминать RxJava. Некоторые люди говорят, что «RxJava действительно проста в использовании», некоторые люди говорят, что «RxJava действительно сложна в использовании», а другие люди говорят: «У меня действительно есть Baidu и Google, но я все еще хочу спросить: что такое RxJava?»
Ввиду текущего горячего и загадочного статуса RxJava, а также наличия у меня некоторого понимания RxJava в процессе его использования в течение года, я решил написать эту статью, чтобы сделать относительно подробное введение в RxJava для Android-разработчиков.
У этой статьи две цели: 1. Дать людям, интересующимся RxJava, некоторые рекомендации для начала работы. 2. Дать более глубокий анализ тем, кто использует RxJava, но все еще сомневается.
содержание:
В конце перед началом текста поставитьGitHub
связывание и импорт зависимостейgradle
Код: Гитхаб:
https://github.com/ReactiveX/RxJava
https://github.com/ReactiveX/RxAndroid
Импорт зависимостей:compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
(Номер версии — это последняя стабильная версия на момент публикации статьи)
Кроме того, благодаря основным членам RxJavaТечет огонь кленовый лестехническая поддержка и внутренние бета-ридерыкодеры,Бао Юнчжан,drakeet,Марлен,иногда снисходительный,Программы не обезьяны,призрак с большой головой,XZoomEye,Си Дэю,TCahead,Tiiime,Ailurus,Жилой старший,злодей,большой большой министр,NicodeLeeпомощь, иЧжоу Ботонг Вербовкаспонсорство.
Что такое RxJava
одно слово:асинхронный.
Собственное представление RxJava на домашней странице GitHub — это «библиотека для составления асинхронных и событийных программ с использованием наблюдаемых последовательностей для Java VM» (библиотека для составления асинхронных событийных программ с использованием наблюдаемых последовательностей для Java VM). Это RxJava, в очень точном выражении.
Однако для новичков это слишком сложно для чтения. Потому что это «резюме», а новичкам нужно «введение».
На самом деле суть RxJava можно выразить словом асинхронный. Говоря о руте, это библиотека, которая реализует асинхронные операции, и на этом основаны другие атрибуты.
Что хорошего в RxJava
Другими словами, «Это тоже асинхронно, почему люди используют его вместо готового AsyncTask/Handler/XXX/… ? 』
одно слово:лаконичный.
Ключевым моментом асинхронной операции является простота программы, потому что когда процесс планирования усложняется, асинхронный код часто трудно писать и читать. Создано AndroidAsyncTask
иHandler
На самом деле, чтобы сделать асинхронный код более лаконичным. Преимущество Rxjava также простота, но его простота отличается от этого,По мере того, как логика программы становится все более и более сложной, она может оставаться лаконичной.
Допустим есть такое требование: есть кастомный вид на интерфейсimageCollectorView
, его роль заключается в отображении нескольких изображений и может использоватьaddImage(Bitmap)
способ произвольного увеличения отображаемого изображения. Теперь программа требуется для преобразования заданного массива каталоговFile[] folders
Изображения png в каждом каталоге загружаются и отображаются вimageCollectorView
середина. Следует отметить, что поскольку процесс чтения изображений занимает много времени, его необходимо выполнять в фоновом режиме, а отображение изображений должно выполняться в UI-потоке. Существует много часто используемых методов реализации, и я опубликую один из них здесь:
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
А если использовать RxJava, то реализация такая:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
Один сказал: «Ваш код явно изменился! Будь проще! 』Брат, успокойся, я говорю о простоте логики, а не просто небольшом количестве кода (простота логики — это нирвана для повышения скорости чтения и написания кода, верно?). Если вы понаблюдаете за этим, вы обнаружите, что эта реализация RxJava представляет собой цепной вызов сверху вниз без какой-либо вложенности, что имеет преимущества с точки зрения простоты логики. Когда требования станут более сложными, это преимущество будет более очевидным (Представьте, если требуется выбрать только первые 10 изображений, что нам делать традиционным способом? А если таких требований больше? Подумайте опять же, в этом большом количестве требований После двух месяцев внедрения нужно изменить функцию.Когда вы вернетесь сюда и увидите загадочный отступ, который вы написали в начале, вы можете гарантировать, что вы быстро его поймете, а не пере -думая код?).
Кроме того, если вашей IDE является Android Studio, фактически каждый раз, когда вы открываете файл Java, вы будете видеть предварительный просмотр, который автоматически Lambdaized, что позволит вам более четко увидеть логику программы:
Observable.from(folders)
.flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) })
.filter((Func1) (file) -> { file.getName().endsWith(".png") })
.map((Func1) (file) -> { getBitmapFromFile(file) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });
Если вы привыкли использовать Retrolambda, вы также можете написать код непосредственно в приведенной выше краткой форме. И если вы видите здесь и не знаете, что такое Retrolambda, я не рекомендую изучать это сейчас. Причин две: 1. Lambda — это палка о двух концах, она делает ваш код лаконичным и в то же время снижает его читабельность, поэтому одновременное изучение RxJava и Retrolambda может заставить вас игнорировать некоторые технические детали RxJava. 2. Retrolambda — это неофициальное решение для лямбда-выражений, совместимое с Java 6/7. Его обратная совместимость и стабильность не могут быть гарантированы, поэтому для корпоративных проектов использование Retrolambda рискованно. Так что, в отличие от многих промоутеров RxJava, я не рекомендую изучать Retrolambda вместе с RxJava. На самом деле, я лично ценю Retrolambda, но никогда ею не пользуюсь.
В Android-коде Flipboard присутствует очень сложная логика, включающая множественные операции с памятью, операции с локальными файлами и сетевые операции, объекты делятся и объединяются, потоки взаимодействуют друг с другом и ждут друг друга. Если вы используете обычный метод для реализации, он должен быть написан как фея, но в случае использования RxJava это все еще просто цепочка вызовов для завершения. Это долго, но ясно.
Итак, что же такого хорошего в RxJava? Хорошо быть кратким, и хорошо сводить любую сложную логику к простоте.
Введение в API и принципиальный анализ
Я не могу объяснить это одним словом... потому что основное содержание этого раздела состоит в том, чтобы шаг за шагом объяснить, как RxJava достигает асинхронности и как достичь простоты.
1. Концепция: шаблон расширенного наблюдателя
Асинхронная реализация RxJava достигается за счет расширенного шаблона наблюдателя.
Шаблон наблюдателя
Сначала кратко опишем паттерн Observer, те, кто с ним уже знаком, могут пропустить этот абзац.
Требования паттерна наблюдатель таковы: объект А (наблюдатель) очень чувствителен к некоторым изменениям в объекте Б (наблюдаемый), и должен реагировать в момент изменения В. Например, полиции, которая любит видеть в новостях ловлю вора, полиции нужно арестовать вора, когда он протянет руку, чтобы совершить преступление. В этом примере полицейский выступает в роли наблюдателя, а вор — в роли наблюдаемого.Полицейский должен следить за каждым движением вора, чтобы не упустить ни одного момента. Режим наблюдателя программы несколько отличается от настоящего «наблюдения»: наблюдателю не нужно все время смотреть на наблюдаемое (например, А не нужно проверять состояние В каждые 2 мс), а используетсярегистр(Register)или позвонилподписка(Subscribe)способ сказать наблюдаемому: мне нужно ваше такое-то состояние, и вы должны уведомить меня, когда оно изменится. Типичным примером в разработке для Android является прослушиватель кликов.OnClickListener
. настройки парыOnClickListener
Сказать,View
наблюдается,OnClickListener
является наблюдателем, как черезsetOnClickListener()
способ достижения отношения подписки. В тот момент, когда пользователь нажимает кнопку после подписки, Android Framework отправит событие click зарегистрированному пользователю.OnClickListener
. Принятие такого пассивного метода наблюдения не только экономит потребление ресурсов при повторном извлечении состояния, но также позволяет получить самую высокую скорость обратной связи. Конечно, это также выигрывает от того, что мы можем настроить наблюдателей и наблюдаемых лиц в наших собственных программах по своему желанию, и полицейский дядя, очевидно, не может попросить вора «вы должны уведомить меня, когда вы совершите преступление».
Шаблон OnClickListener примерно следующий:
Как показано на рисунке, черезsetOnClickListener()
метод,Button
держатьOnClickListener
ссылка (этот процесс не нарисован на схеме); когда пользователь нажимает,Button
автоматический вызовOnClickListener
изonClick()
метод. Кроме того, если понятие на этой картинке абстрагировано (Button
-> Наблюдается,OnClickListener
-> Наблюдатель,setOnClickListener()
-> подписаться,onClick()
-> события), из режима выделенного наблюдателя (например, только для мониторинга кликов по элементам управления) в режим обычного наблюдателя. Как показано ниже:
RxJava, как библиотека инструментов, использует общую форму шаблона наблюдателя.
Шаблон наблюдателя в RxJava
RxJava имеет четыре основных понятия:Observable
(наблюдаемый, то есть наблюдаемый),Observer
(наблюдатель),subscribe
(Подписка), События.Observable
иObserver
пройти черезsubscribe()
метод реализует отношения подписки, таким образомObservable
События могут быть отправлены, чтобы уведомить, когда это необходимоObserver
.
В отличие от традиционного режима наблюдателя, метод обратного вызова события RxJava является дополнением к обычным событиям.onNext()
(эквивалентноonClick()
/ onEvent()
), определены два особых события:onCompleted()
иonError()
.
-
onCompleted()
: очередь событий завершена. RxJava не только обрабатывает каждое событие отдельно, но и рассматривает их как очередь. RxJava указывает, что когда не будет новыхonNext()
Когда выпущено, нужно вызватьonCompleted()
метод в качестве флага. -
onError()
: Исключение очереди событий. Когда во время обработки события возникает исключение,onError()
будет запущен, и очередь будет автоматически завершена, и больше никаких событий не разрешено. - В правильно функционирующей последовательности событий
onCompleted()
иonError()
Есть один и только один, и он последний в последовательности событий. должны знать о том,onCompleted()
иonError()
Эти два также являются взаимоисключающими, то есть, когда один вызывается в очереди, другой не должен вызываться.
Шаблон наблюдателя RxJava примерно следующий:
2. Базовая реализация
Основываясь на вышеизложенных концепциях, базовая реализация RxJava имеет три основных момента:
1) Создать наблюдателя
Наблюдатель — это наблюдатель, он решает, какое поведение он будет иметь при запуске события. в RxJavaObserver
Реализация интерфейса:
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
КромеObserver
В дополнение к интерфейсу RxJava также имеет встроенную реализациюObserver
Абстрактный класс:Subscriber
.Subscriber
правильноObserver
Интерфейсы были немного расширены, но их основное использование точно такое же:
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
Не только основное использование одинаково, по сути, в процессе подписки RxJava,Observer
всегда сначала преобразуется вSubscriber
повторное использование. Поэтому, если вы просто хотите использовать базовые функции, выберитеObserver
иSubscriber
точно такой же. Между ними есть два основных отличия для пользователей:
-
onStart()
: ЭтоSubscriber
метод увеличения. Он будет вызываться в начале подписки и перед отправкой события и может использоваться для выполнения некоторой подготовительной работы, такой как очистка или сброс данных. Это необязательный метод, и его реализация по умолчанию пуста. Следует отметить, что если потоку требуется подготовить работу (например, всплывающее диалоговое окно, показывающее ход выполнения, это должно быть выполнено в основном потоке),onStart()
Не применяется, потому что он всегда вызывается в потоке, в котором происходит подписка, вы не можете указать поток. Чтобы выполнить подготовительную работу на указанном потоке, вы можете использоватьdoOnSubscribe()
Подробно метод можно увидеть в следующем тексте. -
unsubscribe()
: ЭтоSubscriber
Реализован еще один интерфейсSubscription
способ отписки. После вызова этого методаSubscriber
События больше не будут приниматься. Как правило, перед вызовом этого метода вы можете использоватьisUnsubscribed()
Сначала оцените статус.unsubscribe()
Этот метод важен тем, что вsubscribe()
после,Observable
будет держатьSubscriber
Если эта ссылка не может быть освобождена вовремя, возникает риск утечки памяти. Так что лучше придерживаться эмпирического правила: быть в нужном месте, как только вы его не используете (например,onPause()
onStop()
и т.д. метод) вызовunsubscribe()
чтобы разыменовать отношение, чтобы избежать утечек памяти.
2) Создать наблюдаемый
Observable — это наблюдаемое, оно решает, когда запускать событие и какое событие запускать. Использование RxJavacreate()
метод для создания Observable и определения для него правил запуска событий:
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hello");
subscriber.onNext("Hi");
subscriber.onNext("Aloha");
subscriber.onCompleted();
}
});
Как видите, аOnSubscribe
объект в качестве параметра.OnSubscribe
будет храниться в возвращаемомObservable
объект, его роль эквивалентна расписанию, когдаObservable
Подписавшись,OnSubscribe
изcall()
Метод будет вызываться автоматически, и последовательность событий будет запускаться по порядку в соответствии с настройками (для приведенного выше кода это наблюдательSubscriber
будет вызываться три разаonNext()
и однаждыonCompleted()
). Таким образом, callback-метод наблюдателя вызывается наблюдателем, и реализуется доставка события от наблюдателя к наблюдателю, то есть режим наблюдателя.
Этот пример очень прост: содержимое события — это строка, а не какой-то сложный объект; содержимое события уже определено, а не определяется как некоторые шаблоны наблюдателя (например, результат сетевого запроса возвращается в запрос, ранее неизвестный); все события отправляются сразу, а не через какой-то детерминированный или неопределенный интервал времени или запускаются каким-то триггером. В общем, этот пример кажется бесполезным. Но это для иллюстрации, по сути можно сколько угодно писать правила отправки событий. Что касается того, как это сделать, мы поговорим об этом позже, но не сейчас. Только когда сначала объясняются основные принципы, приложение верхнего уровня может быть более легко объяснено.
create()
Методы — это самый простой способ создания последовательностей событий в RxJava. На основе этого метода RxJava также предоставляет несколько методов для быстрого создания очередей событий, таких как:
-
just(T...)
: Отправлять входящие параметры последовательно.
Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
-
from(T[])
/from(Iterable<? extends T>)
: переданный массив илиIterable
После разделения на определенные объекты они отправляются последовательно.
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
вышеjust(T...)
пример иfrom(T[])
пример, оба такие же, как и предыдущийcreate(OnSubscribe)
Примеры эквивалентны.
3) Подписаться
созданныйObservable
иObserver
После этого используйтеsubscribe()
методы соединяют их, и работает вся цепочка. Форма кода проста:
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);
Кто-то может заметить,
subscribe()
Этот метод немного странный: он выглядит как "observalbe
подписалсяobserver
/subscriber
' вместо 'observer
/subscriber
подписалсяobservalbe
', который выглядит как "журнал подписывается на читателей", чтобы изменить объектное отношение. Это немного неудобно читать, но если API предназначен дляobserver.subscribe(observable)
/subscriber.subscribe(observable)
, хотя это больше соответствует логике мышления, но оказывает влияние на дизайн потокового API, который явно не стоит потерь в сравнении.
Observable.subscribe(Subscriber)
Внутренняя реализация такова (только основной код):
// 注意:这不是 subscribe() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
можно увидеть,subscriber()
сделал 3 вещи:
- перечислить
Subscriber.onStart()
. Этот метод был представлен ранее и является необязательным методом подготовки. - перечислить
Observable
серединаOnSubscribe.call(Subscriber)
. Здесь начинает работать логика отправки событий. Из этого также видно, что в RxJava,Observable
Событие отправляется не сразу после создания, а при подписке, т.е.subscribe()
когда метод выполняется. - будет передан в
Subscriber
в видеSubscription
возвращение. Это для удобстваunsubscribe()
.
Отношения между объектами во всем процессе следующие:
Или вы можете посмотреть анимацию:
Кромеsubscribe(Observer)
иsubscribe(Subscriber)
,subscribe()
Также поддерживает не полностью определенные обратные вызовы, RxJava автоматически создаст их в соответствии с определением.Subscriber
. Форма выглядит следующим образом:
Action1<String> onNextAction = new Action1<String>() {
// onNext()
@Override
public void call(String s) {
Log.d(tag, s);
}
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
// onError()
@Override
public void call(Throwable throwable) {
// Error handling
}
};
Action0 onCompletedAction = new Action0() {
// onCompleted()
@Override
public void call() {
Log.d(tag, "completed");
}
};
// 自动创建 Subscriber ,并使用 onNextAction 来定义 onNext()
observable.subscribe(onNextAction);
// 自动创建 Subscriber ,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
observable.subscribe(onNextAction, onErrorAction);
// 自动创建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);
Кратко объясните, что появляется в этом кодеAction1
иAction0
.Action0
это интерфейс RxJava, он имеет только один методcall()
, этот метод не имеет параметров и возвращаемого значения, посколькуonCompleted()
Метод также не имеет параметров и возвращаемого значения, поэтомуAction0
можно рассматривать как объект-оболочку, которыйonCompleted()
Содержимое пакета упаковывается и передается в качестве параметраsubscribe()
для реализации не полностью определенного обратного вызова. Собственно, это тоже можно рассматривать какonCompleted()
Метод передается как параметрsubscribe()
, что эквивалентно «замыканию» в некоторых других языках.Action1
тоже интерфейс, у него тоже только один методcall(T param)
, этот метод также не имеет возвращаемого значения, но имеет один параметр; иAction0
Точно так же, потому чтоonNext(T obj)
иonError(Throwable error)
Это также один параметр и не возвращаемое значение, поэтомуAction1
можетonNext(obj)
иonError(error)
Соберись и пройдиsubscribe()
для реализации не полностью определенного обратного вызова. На самом деле, хотяAction0
иAction1
Наиболее широко используется в API, но RxJava предоставляет несколькоActionX
интерфейс формы (например,Action2
, Action3
), их можно использовать для обертывания различных невозвратных методов.
Примечание. Как упоминалось ранее,
Observer
иSubscriber
одинаковые роли иObserver
существуетsubscribe()
в конечном итоге превратится вSubscriber
объект, поэтому с этого момента я буду использоватьSubscriber
заменитьObserver
, что является более строгим.
4) Пример сценария
Вот два примера:
Для того, чтобы понятнее выразить принцип, примеры в этой статье подобраны как можно более простыми, чтобы некоторые коды примеров создавали ощущение "лишнего" и "очевидно проще решить задачу без RxJava" . Когда вы видите это, думайте, что это не потому, что RxJava слишком многословна, а потому, что она не способствует анализу принципа, слишком рано приводя примеры реальных сценариев, поэтому я намеренно выбрал простые сценарии.
а) напечатать массив строк
массив строкnames
Все строки выводятся последовательно:
String[] names = ...;
Observable.from(names)
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d(tag, name);
}
});
б) Получить картинку по id и отобразить ее
идентификатор файла для рисования, указанныйdrawableRes
Получите изображение и отобразите его вImageView
, и вывести ошибку Toast при возникновении исключения:
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
Как и в двух приведенных выше примерах, создайтеObservable
иSubscriber
, затем используйтеsubscribe()
Соедините их вместе, и базовое использование RxJava готово. очень простой.
Тем не мение,
В правилах RxJava по умолчанию события создаются и потребляются в одном и том же потоке. То есть, если используется только описанный выше метод, будет реализован только шаблон синхронного наблюдателя. Целью самого шаблона наблюдателя является асинхронный механизм «фоновой обработки, обратного вызова переднего плана», поэтому асинхронность имеет решающее значение для RxJava. Для достижения асинхронности нужно использовать другую концепцию RxJava:Scheduler
.
3. Управление потоками — Планировщик (1)
Без указания потока RxJava следует принципу инвариантности потока, то есть: какой поток вызываетsubscribe()
, какой поток создает события, какой поток создает события и какой поток потребляет события. Если вам нужно переключить потоки, вам нужно использоватьScheduler
(планировщик).
1) API планировщика (1)
В RxJava,Scheduler
—— Планировщик, эквивалентный контроллеру потока, RxJava использует его, чтобы указать, в каком потоке должен выполняться каждый фрагмент кода. RxJava имеет несколько встроенныхScheduler
, они уже подходят для большинства случаев использования:
-
Schedulers.immediate()
: запуск непосредственно в текущем потоке, что эквивалентно отсутствию указания потока. Это значение по умолчаниюScheduler
. -
Schedulers.newThread()
: Всегда запускайте новый поток и выполняйте операции в новом потоке. -
Schedulers.io()
: используется для операций ввода-вывода (чтение и запись файлов, чтение и запись баз данных, обмен сетевой информацией и т. д.)Scheduler
. модели поведения иnewThread()
почти, разницаio()
Внутренняя реализация заключается в использовании неограниченного количества пулов потоков, которые могут повторно использовать простаивающие потоки, поэтому в большинстве случаевio()
СравниватьnewThread()
более эффективным. Не ставьте вычислительную работу наio()
, вы можете избежать создания ненужных потоков. -
Schedulers.computation()
: рассчитано с использованиемScheduler
. Этот расчет относится к вычислениям с интенсивным использованием ЦП, т. е. к операциям, производительность которых не ограничивается такими операциями, как ввод-вывод, например, графическими вычислениями. этоScheduler
Используемый фиксированный пул потоков, размер которого равен количеству ядер ЦП. Не помещайте операции ввода-вывода вcomputation()
, в противном случае время ожидания операции ввода-вывода приведет к пустой трате ЦП. - Кроме того, в Android есть специальный
AndroidSchedulers.mainThread()
, указанная операция будет выполняться в основном потоке Android.
С этимиScheduler
, ты можешь использоватьsubscribeOn()
иobserveOn()
Существует два метода управления потоком. *subscribeOn()
: уточнитьsubscribe()
возникающий поток, т.е.Observable.OnSubscribe
Поток, в котором он был активирован. Или вызвать поток, в котором генерируется событие. *observeOn()
: уточнитьSubscriber
Поток, который выполняется. Или называется потоком потребления событий.
Текстовое описание всегда сложно понять, код выше:
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});
В приведенном выше коде, потому чтоsubscribeOn(Schedulers.io())
указывает содержание создаваемого события1
,2
,3
,4
будет выдаваться в потоке ввода-вывода; и из-заobserveOn(AndroidScheculers.mainThread()
), поэтомуsubscriber
Печать чисел будет происходить в основном потоке. На самом деле это вsubscribe()
Я написал два предложения раньшеsubscribeOn(Scheduler.io())
иobserveOn(AndroidSchedulers.mainThread())
Его очень часто используют, и он подходит для большинства программных стратегий «выборки данных из фоновых потоков и отображения их в основном потоке».
И вышеупомянутый пример получения картинки по id картинки и ее отображения, если еще добавить эти два предложения:
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
Затем загрузка изображения будет происходить в потоке ввода-вывода, а установка изображения будет установлена в основном потоке. Это означает, что даже если загрузка изображения занимает десятки или даже сотни миллисекунд, это не вызовет никаких задержек интерфейса.
2) Принцип планировщика (1)
RxJava Scheduler API очень удобен и волшебен (тред переключается добавлением предложения, как это сделать? Иsubscribe()
Разве это не метод, вызываемый непосредственно самым внешним слоем, ему также может быть назначен поток? ). Однако принцип Планировщика необходимо обсудить позже, поскольку его принцип основан на принципе следующего раздела «Трансформация».
Ну, на самом деле, я ничего не сказал в этом разделе, просто чтобы успокоить вас и дать вам понять, что я не забыл объяснить принцип, а поместил его в более подходящее место.
4. Преобразование
Наконец-то я добираюсь до прекрасного места, взволнованы вы или нет, но я все равно взволнован.
RxJava обеспечивает поддержку преобразования последовательностей событий, что является одной из его основных функций и основной причиной, по которой большинство людей говорят, что «RxJava так удобна в использовании».Так называемое преобразование заключается в обработке объектов или всей последовательности в последовательности событий и преобразовании их в другие события или последовательности событий.Концепции всегда расплывчаты и трудны для понимания, посмотрите на API.
1) API
Первый взгляд на одинmap()
пример:
Observable.just("images/logo.png") // 输入类型 String
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String filePath) { // 参数类型 String
return getBitmapFromPath(filePath); // 返回类型 Bitmap
}
})
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) { // 参数类型 Bitmap
showBitmap(bitmap);
}
});
Здесь естьFunc1
тип. это иAction1
Очень похоже, это тоже интерфейс RxJava, используемый для переноса метода с одним параметром.Func1
иAction
Разница в том, чтоFunc1
Обертывает метод возвращаемым значением. Кроме того, иActionX
Такой же,FuncX
Существует также несколько методов для разного количества параметров.FuncX
иActionX
Разница в том,FuncX
Обертывает метод возвращаемым значением.
можно увидеть,map()
Метод будет иметь параметры вString
объект превращается вBitmap
объект возвращается, и послеmap()
После метода также указывается тип параметра событияString
повернулся кBitmap
. Это прямое преобразование объекта и возврата является наиболее распространенным и простым для понимания преобразованием. Однако преобразование RxJava — это гораздо больше, оно может быть нацелено не только на объект события, но и на всю очередь событий, что делает RxJava очень гибким. Я перечисляю несколько часто используемых преобразований:
map()
: Прямое преобразование объекта события, специальные функции были представлены выше. Это наиболее часто используемое преобразование для RxJava.map()
Схема:flatMap()
: Это полезно, ноочень трудно понять, поэтому я решил потратить на него больше места. Сначала предположим такое требование: Предположим, есть структура данных «студенты», и теперь вам нужно вывести имена группы студентов. Реализация проста:
Student[] students = ...;
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onNext(String name) {
Log.d(tag, name);
}
...
};
Observable.from(students)
.map(new Func1<Student, String>() {
@Override
public String call(Student student) {
return student.getName();
}
})
.subscribe(subscriber);
Простой. Затем снова предположим: что, если вы хотите распечатать названия всех курсов, которые должен пройти каждый студент? (Разница в требованиях в том, что у каждого студента только одно имя, но несколько курсов.) Это можно сделать сначала:
Student[] students = ...;
Subscriber<Student> subscriber = new Subscriber<Student>() {
@Override
public void onNext(Student student) {
List<Course> courses = student.getCourses();
for (int i = 0; i < courses.size(); i++) {
Course course = courses.get(i);
Log.d(tag, course.getName());
}
}
...
};
Observable.from(students)
.subscribe(subscriber);
Еще очень просто. тогда, если я не хочу бытьSubscriber
используя цикл for вSubscriber
непосредственно пройти в одиночномCourse
А как насчет объектов (это важно для повторного использования кода)? использоватьmap()
Явно нет, потому чтоmap()
Это преобразование «один к одному», и мое текущее требование — преобразование «один ко многим». Итак, как вы можете преобразовать студента в несколько курсов?
В это время необходимо использоватьflatMap()
в настоящее время:
Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(subscriber);
Как видно из приведенного выше кода,flatMap()
иmap()
Есть одно общее: он также возвращает другой объект после преобразования входящего параметра. Но заметьте, иmap()
разница в том,flatMap()
вернулся вObservable
объект, и этоObservable
объекты не отправляются напрямую вSubscriber
в методе обратного вызова.flatMap()
Принцип таков: 1. Используйте объект входящего события для созданияObservable
объект; 2. не отправлять этоObservable
, но активируйте его, чтобы он начал отправлять события; 3. Каждый созданныйObservable
Все отправленные события импортируются в один и тот жеObservable
, и этоObservable
Отвечает за объединение этих событий вSubscriber
метод обратного вызова. Эти три шага разбивают событие на два уровня с помощью набора вновь созданныхObservable
Исходный объект «сплющен» и распределен по единому пути. И это "сглаживание"flatMap()
Так называемая квартира.
flatMap()
Схема:
Расширения: Поскольку можно использовать вложенныеObservable
Добавьте асинхронный код вflatMap()
Также обычно используется для вложенных асинхронных операций, таких как вложенные сетевые запросы. Пример кода (доработка + RxJava):
networkClient.token() // 返回 Observable<String>,在订阅时请求 token,并在响应后发送 token
.flatMap(new Func1<String, Observable<Messages>>() {
@Override
public Observable<Messages> call(String token) {
// 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
return networkClient.messages();
}
})
.subscribe(new Action1<Messages>() {
@Override
public void call(Messages messages) {
// 处理显示消息列表
showMessages(messages);
}
});
Традиционные вложенные запросы должны быть реализованы с использованием вложенных обратных вызовов. и черезflatMap()
, вы можете написать вложенные запросы в цепочку, чтобы сохранить логику программы понятной.
-
throttleFirst()
: отбрасывать новые события в течение определенного интервала времени после запуска каждого события. Обычно используется для фильтрации дребезга, например прослушивателей нажатия кнопок:RxView.clickEvents(button) // RxBinding 代码,后面的文章有解释 .throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms .subscribe(subscriber);
Мама больше не боится, что мои пользователи пожимают друг другу руки, чтобы открыть два одинаковых интерфейса.
Кроме того, RxJava также предоставляет множество удобных методов для реализации преобразования последовательности событий, и я не буду приводить здесь примеры.
2) Принцип трансформации: lift()
Хотя эти преобразования имеют разные функции, по сути они одинаковы.Обработка и повторная отправка последовательностей событий. А внутри RxJava они основаны на одном и том же базовом методе преобразования:lift(Operator)
. Первый взглядlift()
Внутренняя реализация (только основной код):
// 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
return Observable.create(new OnSubscribe<R>() {
@Override
public void call(Subscriber subscriber) {
Subscriber newSubscriber = operator.call(subscriber);
newSubscriber.onStart();
onSubscribe.call(newSubscriber);
}
});
}
Этот код интересен: он генерирует новыйObservable
и вернуться, и создать новыйObservable
используемые параметрыOnSubscribe
метод обратного вызоваcall()
Реализация вObservable.subscribe()
Такой же! Однако они не одинаковы~ Ключ к разнице лежит во второй строчке.onSubscribe.call(subscriber)
серединаonSubscribe
Референты разные(Предупреждение о высоком уровне энергии: следующие несколько предложений могут вызвать серьезный физический дискомфорт) -
-
subscribe()
в этом предложенииonSubscribe
ОтноситсяObservable
серединаonSubscribe
объект, это не проблема, ноlift()
После этого все стало немного сложнее. - при содержании
lift()
Время:
1.lift()
создалObservable
после плюс оригинал доObservable
, их уже дваObservable
;
2. А также новыйObservable
новое вOnSubscribe
плюс оригинал доObservable
оригинал вOnSubscribe
, есть дваOnSubscribe
;
3. Когда пользователь звонит черезlift()
ПослеObservable
изsubscribe()
когда используешьlift()
вернулся новыйObservable
, поэтому он вызываетonSubscribe.call(subscriber)
, также используя новыйObservable
новое вOnSubscribe
, то есть вlift()
тот, который генерируется вOnSubscribe
;
4. А этот новыйOnSubscribe
изcall()
в методеonSubscribe
, значит оригиналObservable
оригинал вOnSubscribe
,на этоcall()
метод, новыйOnSubscribe
использоватьoperator.call(subscriber)
создал новыйSubscriber
(Operator
прямо здесь, через свой собственныйcall()
метод будет новымSubscriber
и оригинальныйSubscriber
создайте ассоциацию и вставьте свой собственный код «преобразования», чтобы реализовать преобразование), а затем воспользуйтесь преимуществами этого новогоSubscriber
к оригиналуObservable
подписываться.
Вот и всеlift()
процесс, немногоПодобно прокси-механизму, последовательность событий преобразуется посредством перехвата и обработки событий.
То же самое можно сказать и об уменьшении деталей: вObservable
казненlift(Operator)
метод, вернет новыйObservable
, этот новыйObservable
будет действовать как прокси, ответственный за получение оригиналаObservable
Генерируемые события, а после обработки отправляются вSubscriber
.
Если вы предпочитаете конкретное мышление, можете посмотреть на картинку:
Или вы можете посмотреть анимацию:
дважды и многократноlift()
Аналогично, как показано ниже:
дать конкретноеOperator
реализация. Вот пример, который преобразует событиеInteger
объект, преобразованный вString
Пример, просто для справки:
observable.lift(new Observable.Operator<String, Integer>() {
@Override
public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {
// 将事件序列中的 Integer 对象转换为 String 对象
return new Subscriber<Integer>() {
@Override
public void onNext(Integer integer) {
subscriber.onNext("" + integer);
}
@Override
public void onCompleted() {
subscriber.onCompleted();
}
@Override
public void onError(Throwable e) {
subscriber.onError(e);
}
};
}
});
рассказать
lift()
Обоснование состоит в том, чтобы дать вам лучшее понимание RxJava, чтобы вы могли лучше его использовать. Но понимаешь ты или нетlift()
Принцип RxJava не рекомендуется разработчикам настраиватьOperator
использовать напрямуюlift()
, рекомендуется использовать существующийlift()
методы упаковки (например,map()
flatMap()
и т. д.) для реализации требований, потому что прямое использование lift() очень подвержено некоторым трудно обнаруживаемым ошибкам.
3) составить: трансформация Наблюдаемого в целом
Кромеlift()
Кроме,Observable
Существует также метод преобразования, называемыйcompose(Transformer)
. это иlift()
Разница в том, чтоlift()
предназначен для элементов событий и последовательностей событий, аcompose()
направлен наObservable
трансформировать себя.Например, пусть в программе есть несколькоObservable
, и они оба должны применять один и тот же наборlift()
трансформировать. Ты можешь написать:
observable1
.lift1()
.lift2()
.lift3()
.lift4()
.subscribe(subscriber1);
observable2
.lift1()
.lift2()
.lift3()
.lift4()
.subscribe(subscriber2);
observable3
.lift1()
.lift2()
.lift3()
.lift4()
.subscribe(subscriber3);
observable4
.lift1()
.lift2()
.lift3()
.lift4()
.subscribe(subscriber1);
Вы думаете, что это слишком программная инженерия, поэтому вы меняете это на это:
private Observable liftAll(Observable observable) {
return observable
.lift1()
.lift2()
.lift3()
.lift4();
}
...
liftAll(observable1).subscribe(subscriber1);
liftAll(observable2).subscribe(subscriber2);
liftAll(observable3).subscribe(subscriber3);
liftAll(observable4).subscribe(subscriber4);
Читаемость и ремонтопригодность улучшены. НоObservable
обернут в метод, который полезен дляObservale
Гибкость, кажется, добавляет немного ограничения. что делать? В это время вы должны использоватьcompose()
решать:
public class LiftAllTransformer implements Observable.Transformer<Integer, String> {
@Override
public Observable<String> call(Observable<Integer> observable) {
return observable
.lift1()
.lift2()
.lift3()
.lift4();
}
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);
Как и выше, используйтеcompose()
метод,Observable
можно использовать входящиеTransformer
объектcall
Метод обрабатывает сам себя напрямую, поэтому его не нужно оборачивать в метод.
compose()
Принцип относительно прост, не прилагается.
5. Управление потоком: Планировщик (2)
В дополнение к гибким преобразованиям, еще одна интересная вещь в RxJava — это свободное управление потоками.
1) API планировщика (2)
Как упоминалось ранее, вы можете использоватьsubscribeOn()
комбинироватьobserveOn()
Чтобы добиться управления потоком, чтобы генерация и потребление событий происходили в разных потоках. Но пониманиеmap()
flatMap()
После изменения метода некоторые хорошие вещи (на самом деле, когда я впервые столкнулся с RxJava) спросили: Могу ли я переключать потоки еще несколько раз?
Ответ: да. так какobserveOn()
указаноSubscriber
нить, и этоSubscriber
Не (строго говоря, должно быть "не обязательно", а здесь может пониматься как "нет")subscribe()
в параметреSubscriber
, ноobserveOn()
текущий во время выполненияObservable
соответствующийSubscriber
, т.е. его непосредственный подчиненныйSubscriber
. другими словами,observeOn()
Указывается поток, в котором выполняется операция после него. Поэтому, если есть необходимость переключать потоки несколько раз, просто вызовите его один раз в каждом месте, где вы хотите переключать потоки.observeOn()
Вот и все. Над кодом:
Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
как указано выше, черезobserveOn()
В программе реализовано многократное переключение потоков.
Однако, в отличие отobserveOn()
,subscribeOn()
Локацию можно разместить где угодно, но вызвать ее можно только один раз.
Еще одна хорошая вещь (на самом деле, это был я в начале) спросил: если мне придется звонить несколько разsubscribeOn()
Шерстяная ткань? Каков будет эффект?
Оставим этот вопрос в стороне, начнем с принципа управления потоком RxJava.
2) Принцип планировщика (2)
фактически,subscribeOn()
иobserveOn()
Также используется внутренняя реализация ,lift()
. Внимательно посмотрите на картинку (стрелки разного цвета обозначают разные нити):
subscribeOn()
Схема:
observeOn()
Схема:
Как видно из рисунка,subscribeOn()
иobserveOn()
Все выполнили работу по переключению потоков (часть "расписание..." на рисунке). разница в том,subscribeOn()
Переключение потоков происходит вOnSubscribe
в, т.е. информирует верхний уровеньOnSubscribe
, событие еще не начало отправляться, поэтомуsubscribeOn()
На управление потоком можно повлиять с момента возникновения события; в то время какobserveOn()
Переключение потоков происходит в его встроенномSubscriber
, это происходит, когда он собирается дать следующий уровеньSubscriber
При отправке события, поэтомуobserveOn()
Контроль — это нить, стоящая за этим.
Наконец, я использую диаграмму, чтобы объяснить, когда несколькоsubscribeOn()
иobserveOn()
Как происходит планирование потоков при смешанном использовании (из-за большого количества объектов на рисунке в структуру были внесены некоторые упрощенные корректировки по сравнению с рисунком выше):
На рисунке 5 мест, которые содержат операции над событиями. Как видно из рисунка, ① и ② подвержены влиянию первогоsubscribeOn()
Затронуты красной нитью; ③ и ④ затронуты первымobserveOn()
под влиянием зеленой нити; ⑤ под влиянием второгоonserveOn()
аффект, бегущий по пурпурной нити; а второйsubscribeOn()
, так как поток является первымsubscribeOn()
усечение, поэтому это не влияет на весь процесс. Это также отвечает на предыдущий вопрос: при использовании несколькихsubscribeOn()
, только первыйsubscribeOn()
ворваться.
3) Расширение: doOnSubscribe()
Однако, хотя более одногоsubscribeOn()
Не влияет на поток обработки событий, но доступен перед потоком.
говорить передSubscriber
, упомянулSubscriber
изonStart()
Может использоваться в качестве инициализации перед запуском процесса. тем не мениеonStart()
из-заsubscribe()
вызывается, когда это происходит, поэтому поток не может быть указан, а может быть выполнен только вsubscribe()
Поток при вызове. Это приводит к тому, еслиonStart()
Содержит код, требующий потоков (например, отображение ProgressBar на интерфейсе, который должен выполняться в основном потоке), будет риск нелегального потока, потому что иногда вы не можете предсказатьsubscribe()
В каком потоке это будет выполняться.
и сSubscriber.onStart()
Соответственно существует методObservable.doOnSubscribe()
. это иSubscriber.onStart()
Также вsubscribe()
Выполняется после вызова и перед отправкой события, но с той разницей, что может указывать поток. по умолчанию,doOnSubscribe()
выполнить вsubscribe()
поток, который происходит; и если вdoOnSubscribe()
после этого естьsubscribeOn()
, он будет выполнен в ближайшееsubscribeOn()
указанный поток.
Образец кода:
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
как указано выше, вdoOnSubscribe()
за которым следуетsubscribeOn()
, можно указать поток, который готовит работу.
Применимые сценарии и использование RxJava
1. Комбинация с дооснащением
Retrofit — известная библиотека сетевых запросов для Square. Те, кто не использовал Retrofit, могут пропустить этот раздел, это не имеет значения, каждый сценарий, который я упомянул, является просто примером, и между примерами нет контекста, это просто роль для привлечения других, поэтому вы пропускаете здесь чтобы увидеть другие сценарии Это тоже хорошо.
Модернизация обеспечивает в дополнение к традиционнымCallback
form API и RxJava-версияObservable
API формы. Ниже я использую метод сравнения, чтобы показать разницу между API версии Retrofit RxJava и традиционной версией.
чтобы получитьUser
Интерфейс объекта в качестве примера. Используя традиционный API Retrofit, вы можете определять запросы следующим образом:
@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);
Во время построения программы Retrofit автоматически реализует метод и генерирует код, а затем разработчик может использовать следующий метод для получения конкретного пользователя и обработки ответа:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
А с помощью API в виде RxJava тот же запрос определяется так:
@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);
При использовании так:
getUser(userId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
Увидеть разницу?
В форме RxJava Retrofit упаковывает запрос вObservable
, вызывается после завершения запросаonNext()
или вызывается после неудачного запросаonError()
.
По сравнению,Callback
форма иObservable
Форма выглядит иначе, но суть похожа, и в деталяхObservable
форма вроде быCallback
Форма почти. Так почему же Retrofit по-прежнему поддерживает RxJava?
Потому что это отлично работает! Из этого примера это не очевидно, потому что это самый простой случай. А когда ситуация усложняется,Callback
Форма может сразу начать давать головную боль. Например:
Предположим такую ситуацию: ваша программа получаетUser
Он не должен отображаться напрямую, но его необходимо сравнить и скорректировать с данными в базе данных перед отображением. использоватьCallback
Путь можно записать так:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
processUser(user); // 尝试修正 User 数据
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
Есть проблема?
Это легко, но не делайте этого. Зачем? Потому что это повлияет на производительность. Работа с базой данных очень тяжелая, и операции чтения и записи очень часто занимают 10–20 мс, что может легко привести к зависанию интерфейса. Поэтому в целом вам следует избегать работы с базой данных в основном потоке, если это возможно. Таким образом, для повышения производительности этот код можно оптимизировать:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
new Thread() {
@Override
public void run() {
processUser(user); // 尝试修正 User 数据
runOnUiThread(new Runnable() { // 切回 UI 线程
@Override
public void run() {
userView.setUser(user);
}
});
}).start();
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
Проблема с производительностью решена, но... этот код слишком беспорядочный, а отступы сумасшедшие! Беспорядочный код часто является не только вопросом эстетики, потому что чем беспорядочнее код, тем труднее его читать.Если проект полон беспорядочного кода, это, несомненно, снизит читабельность кода, что приведет к сокращению команды. эффективность разработки и более высокий уровень ошибок.
В настоящее время, если вы используете форму RxJava, с ней намного проще обращаться. Код в форме RxJava выглядит так:
getUser(userId)
.doOnNext(new Action1<User>() {
@Override
public void call(User user) {
processUser(user);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
Фоновый код и внешний код написаны в одной цепочке, что, очевидно, намного понятнее.
Другой пример: допустим/user
Доступ к интерфейсу невозможен напрямую, но необходимо заполнить полученную онлайн-форму.token
, как должен быть написан код?
Callback
способ, вы можете использовать вложенныйCallback
:
@GET("/token")
public void getToken(Callback<String> callback);
@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);
...
getToken(new Callback<String>() {
@Override
public void success(String token) {
getUser(token, userId, new Callback<User>() {
@Override
public void success(User user) {
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
});
Проблем с производительностью нет, но отступ тайны может испортить вам жизнь Вы меня знаете, и люди, которые делали большие проекты, должны знать лучше.
С RxJava код выглядит так:
@GET("/token")
public Observable<String> getToken();
@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);
...
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> onNext(String token) {
return getUser(token, userId);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
использовать одинflatMap()
Просто поймите логику, это все-таки цепочка. Выглядит круто, не правда ли?
Обновление 2016/03/31, а также пример проекта, который я написал:
rengwuxian RxJava Samples
Ну, вот и все, что касается дооснащения.
2. RxBinding
RxBinding— это библиотека с открытым исходным кодом Джейка Уортона, которая предоставляет набор API-интерфейсов Binding на основе RxJava для платформы Android. Так называемый Binding — аналогичная настройкаOnClickListener
,настраиватьTextWatcher
Такой API для регистрации объектов привязки.
Приведите пример настройки прослушивателей кликов. использоватьRxBinding
, вы можете установить прослушиватель событий следующим образом:
Button button = ...;
RxView.clickEvents(button) // 以 Observable 形式来反馈点击事件
.subscribe(new Action1<ViewClickEvent>() {
@Override
public void call(ViewClickEvent event) {
// Click handling
}
});
Кажется, что нет никакой разницы, кроме изменения формы, что верно и по существу. Даже если вы посмотрите на его исходный код, вы обнаружите, что он даже не удивляет реализацией: его внутренности непосредственно обернутыsetOnClickListener()
быть реализованным. Однако только это изменение формы оказываетсяRxBinding
Цель: расширяемость. пройти черезRxBinding
Преобразование прослушивателя кликов вObservable
После этого есть возможность его расширить. Есть много способов расширения, в зависимости от ваших потребностей. Пример упоминался ранееthrottleFirst()
, который используется для дешейка, то есть для устранения быстрых щелчков цепи, вызванных дрожанием рук:
RxView.clickEvents(button)
.throttleFirst(500, TimeUnit.MILLISECONDS)
.subscribe(clickAction);
если хотитеRxBinding
Для получения дополнительной информации вы можете перейти на егоGitHub-проектПосмотрите ниже.
3. Различные асинхронные операции
поднятый впередRetrofit
иRxBinding
Пример, два могут предоставить готовыеObservable
библиотека. И если у вас есть некоторые асинхронные операции, которые не могут быть автоматически сгенерированы с этими библиотекамиObservable
, вы также можете написать его самостоятельно. Например, чтение и запись базы данных, загрузка больших изображений, сжатие/распаковка файлов и другие трудоемкие операции, которые необходимо выполнять в фоновом режиме, могут быть реализованы с помощью RxJava.С примерами из предыдущих глав больше не должно быть примеры здесь. .
4. RxBus
Название RxBus похоже на библиотеку, но это не библиотека, а паттерн.Идея состоит в том, чтобы использовать RxJava для реализации EventBus, чтобы вам больше не нужно было использоватьOtto
или ГринРоботEventBus
. Что такое RxBus, вы можете увидетьэта статья. Кстати, Flipboard заменен на RxBus.Otto
, до сих пор никаких побочных реакций.
Наконец
Для Android-разработчиков RxJava — сложная библиотека для начала работы, поскольку в ней слишком много незнакомых для Android-разработчиков концепций. Но это действительно круто. Поэтому я написал это «Подробное объяснение RxJava для разработчиков Android», надеясь дать некоторое руководство тем, кто до сих пор не понимает, что такое RxJava, или позволить людям, которые используют RxJava, но все еще сомневаются, увидеть некоторые более подробные сведения. анализ. В любом случае, если это может помочь вам, коллегам-разработчикам Android, цель этой статьи достигнута.
Оригинальный адрес блога автора: http://gank.io/post/560e15be2dca930e00da1083