RxJava + RxAndroid
Android
Александр Вайцеховский
Реактивное программирование
это парадигма программирования, ориентированная на
потоки данных
и
распространение изменений
© Wikipedia
ООР[еактивное]П
вместо методов и полей — реакции
автоматическое пересчёт значений
реакции зависят от изменений значений
ReactiveX
Это комбинация лучших идей: паттерн Наблюдатель (Observer), паттерн Перечислитель (Iterator)
и функциональное программирование.
1 void hello(String... names) {
2 Observable.from(names).subscribe(new Action1<String>() {
3 @Override
4 public void call(String s) {
5 Log.d("Hello " + s + "!");
6 }
7 });
8 }
9
10 hello("Ben", "George", "World");
Output:
Hello Ben!
Hello George!
Hello World!
Что такое Observable
Объект, предоставляющий асинхронный доступ ко множеству элементов:
Связь с Iterator:
Примитивы RxJava
1 //Наблюдатель
2 interface Observer <T> {
3 void onCompleted();
4 void onError(Throwable throwable);
5 void onNext(T t);
6 }
7
8 //Объект-наблюдения
9 class Observable <T> {
10 public final static <T> Observable<T> create(OnSubscribe<T> f);
11 public Subscription subscribe(Observer<? super T> observer);
12 }
13
14 //логика оповещения
15 interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {}
16
17 //подписка
18 interface Subscription {
19 public void unsubscribe();
20 public boolean isUnsubscribed();
21 }
22
23 abstract class Subscriber<T> implements Observer<T>, Subscription {...}
Создание Observable
1 Observable<String> rxHelloWorld =
2 Observable.create(new Observable.OnSubscribe<String>() {
3 @Override
4 public void call(Subscriber<? super String> subscriber) {
5 subscriber.onNext("Hello");
6 subscriber.onNext("World");
7 subscriber.onCompleted();
8 }
9 });
Создание Observable
1 void doWork(final Subscriber<? super String> subscriber) {
2 subscriber.onNext("Hello");
3 subscriber.onNext("World");
4 subscriber.onCompleted();
5 }
6
7 Observable<String> rxHelloWorld =
8 Observable.create(new Observable.OnSubscribe<String>() {
9 @Override
10 public void call(Subscriber<? super String> subscriber) {
11 try {
12 doWork();
13 } catch (Exception ex) {
14 subscriber.onError(ex);
15 }
16 }
17 });
Подписываемся на Observable
1 Subscription subsctiption = rxHelloWorld.subscribe(new Observer<String>() {
2 @Override
3 public void onCompleted() {
4 Log.d("completed");
5 }
6
7 @Override
8 public void onError(Throwable throwable) {
9 Log.e(throwable);
10 }
11
12 @Override
13 public void onNext(String s) {
14 Log.d(s);
15 }
16 });
Hello
World
Completed
Потоки и Observable
Предоставляет методы:
observeOn(Thread) - поток, в котором выполнять логику
налюдаемого объекта
subscribeOn(Thread) - поток, в котором будут вызываться
методы подписчика
Потоки и Observable
Трансформации
1 Observable<Integer> toLength(Observable<String> source){
2 return source.map(new Func1<String, Integer>() {
3 @Override
4 public Integer call(String s) {
5 return s.length();
6 }
7 });
8 }
9
10 toLength(rxHelloWorld).subscribe(new Observer<Integer>() {
11 @Override
12 public void onCompleted() { Log.d("completed"); }
13
14 @Override
15 public void onError(Throwable throwable) { Log.e(throwable); }
16
17 @Override
18 public void onNext(Integer i) { Log.d(String.format("length: %d", i)); }
19 });
length: 5
length: 5
completed
Может быть красиво
Java7
1 rxHelloWorld.map(new Func1<String, Integer>() {
2 @Override
3 public Integer call(String s) {
4 return s.length();
5 }
6 });
Java8
1 rxHelloWorld.map((String s) -> { //
2 return s.length(); //можно так!
3 }); //
4 rxHelloWorld.map((String s) -> s.length()); //или так
5 rxHelloWorld.map((s) -> s.length()); //или упростить
6 rxHelloWorld.map(s -> s.length()); //или совсем просто
7 rxHelloWorld.map(String::length); //o_O
* Но Android не поддерживает Java8:(
** Зато есть Retrolambda
Обычная логика
Реактивная логика
Упрощенное создание
Create - создать обычный Observable
Just - создает Observable из Объекта (его и возвращает)
From - создает Observable из Array, Iterable, Future
Empty, Never, Throw - на каждый метод подписчика
Timer, Interval - созданный периодичный Observable
Repeat - Observable, который возвращает одно и то же
Другие операторы
Трансформация
Map - преобразовывает возвращаемое
значение
FlatMap - преобразовывает значение в
результат другого Observable
Buffer - возвращает комбинации значений
Window, Scan, GroupBy...
Комбинирование
CombineLatest - возвращает самые
последние значения исходных Observable
Merge - объединяет возвращаемые
значения
Zip - объединяет каждое и возвращает
результат объединения
Математические операции
Max, Min - ждет конца, возвращает нужное
Sum, Reduce - применяет функцию последовательно
Marble diagram
RxJava wiki для объяснение каждого оператора
использует Marble Diagram
Merge и Zip
Map и FlatMap
Filter и Debounce
Примеры
Async
1 Observable
2 .create((Subscriber<? super Bitmap> subscriber) -> {
3 try{
4 Bitmap image = loadImage();
5 subscriber.onNext(image);
6 subscriber.onCompleted();
7 } catch (Exception ex) {
8 subscriber.onError(ex);
9 }
10 })
11 .subscribeOn(Schedulers.io())
12 .subscribe(() -> toast("loaded"));
View click
1 public static <T extends View> Observable<T> clicksFrom(T view) {
2 PublishSubject publishSubject = PublishSubject.create();
3 view.setOnClickListener(new OnClickListener() {
4 @Override
5 public void onClick(View view) {
6 publishSubject.onNext(view));
7 }
8 });
9 return publishSubject.asObservable();
10 }
11 clicksFrom(button).subscribe(view -> toast("clicked"));
Несколько событий
1 final ImageView imageView = (ImageView) findViewById(R.id.image);
2
3 Observable<Bitmap> imageObservable =
4 loadImage()
5 .cache()
6 .observeOn(AndroidSchedulers.mainThread());
7
8
9 imageObservable.subscribe((Bitmap image) -> imageView.setImageBitmap(image));
10 imageObservable
11 .flatMap(image -> {
12 clicksFrom(imageView).map((ImageView view) -> image)
13 })
14 .subscribe((Bitmap image) -> processImage(image));
Что здесь происходит?
Когда будет вызван setImageBitmap?
Когда будет вызван processImage?
Гамбургер
Поддержка Retro t
1 class SimpleProfile {
2 private int id;
3 public String getId() {return id;}
4 }
5
6 class Network {
7 @GET("/users")
8 Observable<List<SimpleProfile>> getUsers();
9
10 @GET("/users/{id}")
11 Observable<ProfileDetails> getUser(@Path("id") int id);
12 }
13
14 network.getUsers()
15 .flatMap((List<SimpleProfile> list) -> Observable.from(list))
16 .flatMap((SimpleProfile profile) -> api.getUser(profile.getId()))
17 //.filter((ProfileDetails details) -> details.getSex() == Sex.FEMALE)
18 //.take(5)
19 //.doOnNext((ProfileDetails details) -> saveToDatabase(details))
20 .subscribe((ProfileDetails details) -> handleProfile(details));
Обработка ошибок!
1 showProgressBar();
2 network.getUsers()
3 .flatMap(Observable::from)
4 .subscribe(new Subscriber<String>() {
5 @Override
6 public void onNext(String user) {
7 listAdapter.add(user);
8 }
9
10 @Override
11 public void onCompleted() {
12 hideProgerssBar();
13 showListView();
14 }
15
16 @Override
17 public void onError(Throwable e) {
18 hideProgerssBar();
19 hideListView();
20 showErrorView();
21 }
22 });
RxAndroid
1. Удобная работу с потоками
2. Работа с Android Lifecycle - RxLifecycle
3. Работа с View - RxBinding
4. SqlBrite - обертка над базой
5. и еще RxLocation / RxPermissions / RxNoti cation
RxAndroid - Threads
Main Thread
1 Observable.just("one", "two", "three", "four", "five")
2 .subscribeOn(Schedulers.newThread())
3 .observeOn(AndroidSchedulers.mainThread());
Handler Thread
1 final Handler handler = new Handler(); // bound to this thread
2 Observable.just("one", "two", "three", "four", "five")
3 .subscribeOn(Schedulers.newThread())
4 .observeOn(AndroidSchedulers.handlerThread(handler));
RxLifecycle
Bindings
1 IntentFilter intentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
2 RxBroadcastReceiver.create(context, intentFilter)
RxBinding
clicks - обработка нажатий
1 ViewObservable.clicks(mCardNameEditText, /*emit initial value*/ false)
2 .subscribe((View view) -> handleClick(view));
text - обработка ввода текста
1 ViewObservable.text(editTextName, false)
2 .subscribe((EditText editText) -> handleText(editText.getText())));
input - обработка "чекбоксов"
1 ViewObservable.input(compoundButton, false)
2 .subscribe((Boolean value) -> changeState(value));
Полезные ссылки
ReactiveX
RxJava
RxAndroid
Grokking RxJava, part 1 ищите там же части 2, 3, 4
..или перевод на русский
FRP on Android

RxJava+RxAndroid (Lecture 20 – rx java)

  • 1.
  • 2.
    Реактивное программирование это парадигмапрограммирования, ориентированная на потоки данных и распространение изменений © Wikipedia ООР[еактивное]П вместо методов и полей — реакции автоматическое пересчёт значений реакции зависят от изменений значений
  • 3.
    ReactiveX Это комбинация лучшихидей: паттерн Наблюдатель (Observer), паттерн Перечислитель (Iterator) и функциональное программирование. 1 void hello(String... names) { 2 Observable.from(names).subscribe(new Action1<String>() { 3 @Override 4 public void call(String s) { 5 Log.d("Hello " + s + "!"); 6 } 7 }); 8 } 9 10 hello("Ben", "George", "World"); Output: Hello Ben! Hello George! Hello World!
  • 4.
    Что такое Observable Объект,предоставляющий асинхронный доступ ко множеству элементов: Связь с Iterator:
  • 5.
    Примитивы RxJava 1 //Наблюдатель 2interface Observer <T> { 3 void onCompleted(); 4 void onError(Throwable throwable); 5 void onNext(T t); 6 } 7 8 //Объект-наблюдения 9 class Observable <T> { 10 public final static <T> Observable<T> create(OnSubscribe<T> f); 11 public Subscription subscribe(Observer<? super T> observer); 12 } 13 14 //логика оповещения 15 interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {} 16 17 //подписка 18 interface Subscription { 19 public void unsubscribe(); 20 public boolean isUnsubscribed(); 21 } 22 23 abstract class Subscriber<T> implements Observer<T>, Subscription {...}
  • 6.
    Создание Observable 1 Observable<String>rxHelloWorld = 2 Observable.create(new Observable.OnSubscribe<String>() { 3 @Override 4 public void call(Subscriber<? super String> subscriber) { 5 subscriber.onNext("Hello"); 6 subscriber.onNext("World"); 7 subscriber.onCompleted(); 8 } 9 });
  • 7.
    Создание Observable 1 voiddoWork(final Subscriber<? super String> subscriber) { 2 subscriber.onNext("Hello"); 3 subscriber.onNext("World"); 4 subscriber.onCompleted(); 5 } 6 7 Observable<String> rxHelloWorld = 8 Observable.create(new Observable.OnSubscribe<String>() { 9 @Override 10 public void call(Subscriber<? super String> subscriber) { 11 try { 12 doWork(); 13 } catch (Exception ex) { 14 subscriber.onError(ex); 15 } 16 } 17 });
  • 8.
    Подписываемся на Observable 1Subscription subsctiption = rxHelloWorld.subscribe(new Observer<String>() { 2 @Override 3 public void onCompleted() { 4 Log.d("completed"); 5 } 6 7 @Override 8 public void onError(Throwable throwable) { 9 Log.e(throwable); 10 } 11 12 @Override 13 public void onNext(String s) { 14 Log.d(s); 15 } 16 }); Hello World Completed
  • 9.
    Потоки и Observable Предоставляетметоды: observeOn(Thread) - поток, в котором выполнять логику налюдаемого объекта subscribeOn(Thread) - поток, в котором будут вызываться методы подписчика
  • 10.
  • 11.
    Трансформации 1 Observable<Integer> toLength(Observable<String>source){ 2 return source.map(new Func1<String, Integer>() { 3 @Override 4 public Integer call(String s) { 5 return s.length(); 6 } 7 }); 8 } 9 10 toLength(rxHelloWorld).subscribe(new Observer<Integer>() { 11 @Override 12 public void onCompleted() { Log.d("completed"); } 13 14 @Override 15 public void onError(Throwable throwable) { Log.e(throwable); } 16 17 @Override 18 public void onNext(Integer i) { Log.d(String.format("length: %d", i)); } 19 }); length: 5 length: 5 completed
  • 12.
    Может быть красиво Java7 1rxHelloWorld.map(new Func1<String, Integer>() { 2 @Override 3 public Integer call(String s) { 4 return s.length(); 5 } 6 }); Java8 1 rxHelloWorld.map((String s) -> { // 2 return s.length(); //можно так! 3 }); // 4 rxHelloWorld.map((String s) -> s.length()); //или так 5 rxHelloWorld.map((s) -> s.length()); //или упростить 6 rxHelloWorld.map(s -> s.length()); //или совсем просто 7 rxHelloWorld.map(String::length); //o_O * Но Android не поддерживает Java8:( ** Зато есть Retrolambda
  • 13.
  • 14.
  • 15.
    Упрощенное создание Create -создать обычный Observable Just - создает Observable из Объекта (его и возвращает) From - создает Observable из Array, Iterable, Future Empty, Never, Throw - на каждый метод подписчика Timer, Interval - созданный периодичный Observable Repeat - Observable, который возвращает одно и то же
  • 16.
    Другие операторы Трансформация Map -преобразовывает возвращаемое значение FlatMap - преобразовывает значение в результат другого Observable Buffer - возвращает комбинации значений Window, Scan, GroupBy... Комбинирование CombineLatest - возвращает самые последние значения исходных Observable Merge - объединяет возвращаемые значения Zip - объединяет каждое и возвращает результат объединения Математические операции Max, Min - ждет конца, возвращает нужное Sum, Reduce - применяет функцию последовательно
  • 17.
    Marble diagram RxJava wikiдля объяснение каждого оператора использует Marble Diagram
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
    Async 1 Observable 2 .create((Subscriber<?super Bitmap> subscriber) -> { 3 try{ 4 Bitmap image = loadImage(); 5 subscriber.onNext(image); 6 subscriber.onCompleted(); 7 } catch (Exception ex) { 8 subscriber.onError(ex); 9 } 10 }) 11 .subscribeOn(Schedulers.io()) 12 .subscribe(() -> toast("loaded"));
  • 23.
    View click 1 publicstatic <T extends View> Observable<T> clicksFrom(T view) { 2 PublishSubject publishSubject = PublishSubject.create(); 3 view.setOnClickListener(new OnClickListener() { 4 @Override 5 public void onClick(View view) { 6 publishSubject.onNext(view)); 7 } 8 }); 9 return publishSubject.asObservable(); 10 } 11 clicksFrom(button).subscribe(view -> toast("clicked"));
  • 24.
    Несколько событий 1 finalImageView imageView = (ImageView) findViewById(R.id.image); 2 3 Observable<Bitmap> imageObservable = 4 loadImage() 5 .cache() 6 .observeOn(AndroidSchedulers.mainThread()); 7 8 9 imageObservable.subscribe((Bitmap image) -> imageView.setImageBitmap(image)); 10 imageObservable 11 .flatMap(image -> { 12 clicksFrom(imageView).map((ImageView view) -> image) 13 }) 14 .subscribe((Bitmap image) -> processImage(image)); Что здесь происходит? Когда будет вызван setImageBitmap? Когда будет вызван processImage?
  • 25.
  • 26.
    Поддержка Retro t 1class SimpleProfile { 2 private int id; 3 public String getId() {return id;} 4 } 5 6 class Network { 7 @GET("/users") 8 Observable<List<SimpleProfile>> getUsers(); 9 10 @GET("/users/{id}") 11 Observable<ProfileDetails> getUser(@Path("id") int id); 12 } 13 14 network.getUsers() 15 .flatMap((List<SimpleProfile> list) -> Observable.from(list)) 16 .flatMap((SimpleProfile profile) -> api.getUser(profile.getId())) 17 //.filter((ProfileDetails details) -> details.getSex() == Sex.FEMALE) 18 //.take(5) 19 //.doOnNext((ProfileDetails details) -> saveToDatabase(details)) 20 .subscribe((ProfileDetails details) -> handleProfile(details));
  • 27.
    Обработка ошибок! 1 showProgressBar(); 2network.getUsers() 3 .flatMap(Observable::from) 4 .subscribe(new Subscriber<String>() { 5 @Override 6 public void onNext(String user) { 7 listAdapter.add(user); 8 } 9 10 @Override 11 public void onCompleted() { 12 hideProgerssBar(); 13 showListView(); 14 } 15 16 @Override 17 public void onError(Throwable e) { 18 hideProgerssBar(); 19 hideListView(); 20 showErrorView(); 21 } 22 });
  • 28.
    RxAndroid 1. Удобная работус потоками 2. Работа с Android Lifecycle - RxLifecycle 3. Работа с View - RxBinding 4. SqlBrite - обертка над базой 5. и еще RxLocation / RxPermissions / RxNoti cation
  • 29.
    RxAndroid - Threads MainThread 1 Observable.just("one", "two", "three", "four", "five") 2 .subscribeOn(Schedulers.newThread()) 3 .observeOn(AndroidSchedulers.mainThread()); Handler Thread 1 final Handler handler = new Handler(); // bound to this thread 2 Observable.just("one", "two", "three", "four", "five") 3 .subscribeOn(Schedulers.newThread()) 4 .observeOn(AndroidSchedulers.handlerThread(handler));
  • 30.
    RxLifecycle Bindings 1 IntentFilter intentFilter= new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION) 2 RxBroadcastReceiver.create(context, intentFilter)
  • 31.
    RxBinding clicks - обработканажатий 1 ViewObservable.clicks(mCardNameEditText, /*emit initial value*/ false) 2 .subscribe((View view) -> handleClick(view)); text - обработка ввода текста 1 ViewObservable.text(editTextName, false) 2 .subscribe((EditText editText) -> handleText(editText.getText()))); input - обработка "чекбоксов" 1 ViewObservable.input(compoundButton, false) 2 .subscribe((Boolean value) -> changeState(value));
  • 32.
    Полезные ссылки ReactiveX RxJava RxAndroid Grokking RxJava,part 1 ищите там же части 2, 3, 4 ..или перевод на русский FRP on Android