Sviluppare app event-driven con
RxJava su Android
Rx
Boris D’Amato
Android Developer @
+BorisDAmato
@borisdamato
damatoboris
borisdamato
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Il Problema
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Il Problema
UI Thread
Main Thread
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
La Soluzione
RxJava RxJava è un’implementazione delle
Reactive Extensions per la Java VM
Una libreria per realizzare programmi asincroni
ed event-based utilizzando sequenze di
observable.
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Cosa significa “Reactive”?
Un sistema Reattivo deve essere
ovvero
alle richieste
degli utenti
a guasti
ed errori
Responsivo Resiliente Elastico Orientato a messaggi
al carico di
lavoro
a eventi e messaggi
reagire
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Reactive Programming
Erik Meijer Ben Christensen
Rx.Net RxJava
Dávid Karnok
RxJava
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Reactive Programming
“Paradigma di programmazione basato su flussi di dati e propagazione dei
cambiamenti” [Wikipedia]
Data flow Observer pattern Push vs Pull
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Gli Attori
Observable Observer Subscriber Subject
Generatore di
dati
Reagisce alla
ricezione dei dati
Ibrido tra
Observable e
Observer
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Comunicazione Observable - Observer
Observer
subscribe
Observable
onNext()
onComplete()
onError()
onSubscribe()
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Android Time!
RecyclerView
Lista dei contatti
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Creazione di un Observable
private Observable<String> getContacts() {
return Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
List<String> contacts = new ArrayList<>();
Cursor cursor = [...];
while (cursor.moveToNext()) {
String displayName = [...];
contacts.add(displayName);
}
cursor.close();
for (String contact : contacts) {
if (e.isDisposed()) return;
e.onNext(contact);
}
if (!e.isDisposed()) e.onComplete();
}});}
onError(Throwable e)
onNext(T item)
onComplete()
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Creazione di un Observable
List<Contact> contacts = [...];
Observable.fromArray(contacts).subscribe([...])
;
Observable.just()
.fromIterable()
.fromFuture()
.fromCallable()
.fromPublisher()
Observable.fromArray()
List<Contact> contacts = [...];
Observable.just(contacts.get(0), contacts.get(1))
.subscribe([...]);
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Creazione di un Observable
Observable.empty() Observable.never() Observable.throw()
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Observer<Contact> observer = new Observer<Contact>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe");
Log.d(TAG, "isDisposed: " + d.isDisposed());
}
@Override
public void onNext(Contact item) {
Log.d(TAG, "onNext");
adapter.addItem(item);
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "onError: " + e.getMessage);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
};
Subscribe
List<Contact> contacts = [...];
Observable.fromArray(contacts)
.subscribe(observer);
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Andrea Gianni
Giorgio Gianni
getContacts().filter(new Predicate<Contact>() {
@Override
public boolean test(Contact contact) throws Exception {
return contact.getName().startsWith("G");
}
})
Filter
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering (Lambda Version)
Giorgio Carlo Andrea Gianni
Giorgio Gianni
getContacts().filter(contact -> contact.getName().startsWith("G"))Filter
https://github.com/evant/gradle-retrolambda
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Andrea Gianni
Giorgio
getContacts().take(3)Take
Carlo Andrea
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Andrea Gianni
getContacts().takeLast(3)
Take
Last
Carlo Andrea Gianni
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Giorgio Gianni
getContacts().distinct()Distinct
Carlo GianniGiorgio
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
21
getTemperature().distinct()
21 20 19 20 21 22 23 23 21 22 21
21 20 19 22 23
Distinct
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
21
getTemperature().distinctUntilChanged()
21 20 19 20 21 22 23 23 21 22 21
Distinct
Until
Changed
21 20 19 20 21 22 23 21 22 21
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
21
getTemperature().sample(30, TimeUnit.SECONDS)
21 20 19 20 21 22 23 23 21 22 21
Sample
o
ThrottleLast
20 21
30 sec 30 sec
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
21
getTemperature().throttleFirst(30, TimeUnit.SECONDS)
21 20 19 20 21 22 23 23 21 22 21
Throttle
First
30 sec 30 sec
21 21 22
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Transforming
Giorgio Carlo Andrea Gianni
giorgio
getContacts().map((contact) -> {
contact.setName(
contact.getName().toLowerCase());
return contact;
})
Map
carlo andrea gianni
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Combining
Giorgio Carlo Andrea Gianni
Giorgio
Observable.merge(getContacts(), getImportingContacts())Merge
Carlo Andrea Gianni
Marco Francesco
Marco Francesco
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Combining
21° 22° 20° 18°
21° - 17:30
Zip
17:30 18:00 18:30 19:00
16°
22° - 18:00 20° - 18:30 18° - 19:00
Observable.zip(getTemperature(), getTime(), this::updateMonitor)
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Torniamo ad Android...
private Observable<Contact> getContacts() {
return Observable.create(new ObservableOnSubscribe<Contact>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
List<String> contacts = new ArrayList<>();
SharedPreferences prefs = getActivity().
getPreferences(Context.MOODE_PRIVATE);
Type contactType = new TypeToken<List<Contact>>(){}.getType();
String contactsGson = prefs.getString(“CONTACTS”, “”);
if (!contactsGson.isEmpty())
contacts = new Gson().fromJson(contactsGson, contactType);
for (Contact contact : contacts) {
e.onNext(contact);
}
if (!e.isDisposed()) e.onComplete();
}});}
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
… e accade questo...
D/StrictMode: StrictMode policy violation;
~duration=253 ms:
android.os.StrictMode$ StrictModeDiskReadViol
ation: policy=31violation=2 at
android.os.StrictMode
$AndroidBlockGuardPolicy. onReadFromDisk (Stri
ctMode.java:1135)
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Soluzione
getContacts()
.subscribeOn( Schedulers.io() )
.subscribe([...]);
Schedulers.
computation()
io()
immediate()
newThread()
trampoline()
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
… altro problema
android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
getContacts()
.subscribeOn(Schedulers.io())
.observeOn( AndroidSchedulers.mainThread() )
.subscribe([...]);
Soluzione
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Nella pratica
● La ricerca debba essere eseguita ogni
500 millisecondi
Vogliamo che:
● La ricerca debba partire solo dopo che
sia stati inseriti almeno 3 caratteri
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-support-v4:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
[ . . .]
https://github.com/JakeWharton/RxBinding
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Nella pratica
RxTextView
.textChanges(searchField)
.debounce(500, TimeUnit.MILLISECONDS)
.filter( (charSequence) -> return charSequence.length() >= 3 )
.subscribe( [...] )
Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Grazie.
@borisdamato
+BorisDAmato

Sviluppare app event-driven con RxJava su Android

  • 1.
    Sviluppare app event-drivencon RxJava su Android Rx
  • 2.
    Boris D’Amato Android Developer@ +BorisDAmato @borisdamato damatoboris borisdamato
  • 3.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Il Problema
  • 4.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Il Problema UI Thread Main Thread
  • 5.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato La Soluzione RxJava RxJava è un’implementazione delle Reactive Extensions per la Java VM Una libreria per realizzare programmi asincroni ed event-based utilizzando sequenze di observable.
  • 6.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Cosa significa “Reactive”? Un sistema Reattivo deve essere ovvero alle richieste degli utenti a guasti ed errori Responsivo Resiliente Elastico Orientato a messaggi al carico di lavoro a eventi e messaggi reagire
  • 7.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Reactive Programming Erik Meijer Ben Christensen Rx.Net RxJava Dávid Karnok RxJava
  • 8.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Reactive Programming “Paradigma di programmazione basato su flussi di dati e propagazione dei cambiamenti” [Wikipedia] Data flow Observer pattern Push vs Pull
  • 9.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Gli Attori Observable Observer Subscriber Subject Generatore di dati Reagisce alla ricezione dei dati Ibrido tra Observable e Observer
  • 10.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Comunicazione Observable - Observer Observer subscribe Observable onNext() onComplete() onError() onSubscribe()
  • 11.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Android Time! RecyclerView Lista dei contatti
  • 12.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Creazione di un Observable private Observable<String> getContacts() { return Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> e) throws Exception { List<String> contacts = new ArrayList<>(); Cursor cursor = [...]; while (cursor.moveToNext()) { String displayName = [...]; contacts.add(displayName); } cursor.close(); for (String contact : contacts) { if (e.isDisposed()) return; e.onNext(contact); } if (!e.isDisposed()) e.onComplete(); }});} onError(Throwable e) onNext(T item) onComplete()
  • 13.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Creazione di un Observable List<Contact> contacts = [...]; Observable.fromArray(contacts).subscribe([...]) ; Observable.just() .fromIterable() .fromFuture() .fromCallable() .fromPublisher() Observable.fromArray() List<Contact> contacts = [...]; Observable.just(contacts.get(0), contacts.get(1)) .subscribe([...]);
  • 14.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Creazione di un Observable Observable.empty() Observable.never() Observable.throw()
  • 15.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Observer<Contact> observer = new Observer<Contact>() { @Override public void onSubscribe(Disposable d) { Log.d(TAG, "onSubscribe"); Log.d(TAG, "isDisposed: " + d.isDisposed()); } @Override public void onNext(Contact item) { Log.d(TAG, "onNext"); adapter.addItem(item); } @Override public void onError(Throwable e) { Log.e(TAG, "onError: " + e.getMessage); } @Override public void onComplete() { Log.d(TAG, "onComplete"); } }; Subscribe List<Contact> contacts = [...]; Observable.fromArray(contacts) .subscribe(observer);
  • 16.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering Giorgio Carlo Andrea Gianni Giorgio Gianni getContacts().filter(new Predicate<Contact>() { @Override public boolean test(Contact contact) throws Exception { return contact.getName().startsWith("G"); } }) Filter
  • 17.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering (Lambda Version) Giorgio Carlo Andrea Gianni Giorgio Gianni getContacts().filter(contact -> contact.getName().startsWith("G"))Filter https://github.com/evant/gradle-retrolambda
  • 18.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering Giorgio Carlo Andrea Gianni Giorgio getContacts().take(3)Take Carlo Andrea
  • 19.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering Giorgio Carlo Andrea Gianni getContacts().takeLast(3) Take Last Carlo Andrea Gianni
  • 20.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering Giorgio Carlo Giorgio Gianni getContacts().distinct()Distinct Carlo GianniGiorgio
  • 21.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering 21 getTemperature().distinct() 21 20 19 20 21 22 23 23 21 22 21 21 20 19 22 23 Distinct
  • 22.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering 21 getTemperature().distinctUntilChanged() 21 20 19 20 21 22 23 23 21 22 21 Distinct Until Changed 21 20 19 20 21 22 23 21 22 21
  • 23.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering 21 getTemperature().sample(30, TimeUnit.SECONDS) 21 20 19 20 21 22 23 23 21 22 21 Sample o ThrottleLast 20 21 30 sec 30 sec
  • 24.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Filtering 21 getTemperature().throttleFirst(30, TimeUnit.SECONDS) 21 20 19 20 21 22 23 23 21 22 21 Throttle First 30 sec 30 sec 21 21 22
  • 25.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Transforming Giorgio Carlo Andrea Gianni giorgio getContacts().map((contact) -> { contact.setName( contact.getName().toLowerCase()); return contact; }) Map carlo andrea gianni
  • 26.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Combining Giorgio Carlo Andrea Gianni Giorgio Observable.merge(getContacts(), getImportingContacts())Merge Carlo Andrea Gianni Marco Francesco Marco Francesco
  • 27.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Combining 21° 22° 20° 18° 21° - 17:30 Zip 17:30 18:00 18:30 19:00 16° 22° - 18:00 20° - 18:30 18° - 19:00 Observable.zip(getTemperature(), getTime(), this::updateMonitor)
  • 28.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Torniamo ad Android... private Observable<Contact> getContacts() { return Observable.create(new ObservableOnSubscribe<Contact>() { @Override public void subscribe(ObservableEmitter<String> e) throws Exception { List<String> contacts = new ArrayList<>(); SharedPreferences prefs = getActivity(). getPreferences(Context.MOODE_PRIVATE); Type contactType = new TypeToken<List<Contact>>(){}.getType(); String contactsGson = prefs.getString(“CONTACTS”, “”); if (!contactsGson.isEmpty()) contacts = new Gson().fromJson(contactsGson, contactType); for (Contact contact : contacts) { e.onNext(contact); } if (!e.isDisposed()) e.onComplete(); }});}
  • 29.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato … e accade questo... D/StrictMode: StrictMode policy violation; ~duration=253 ms: android.os.StrictMode$ StrictModeDiskReadViol ation: policy=31violation=2 at android.os.StrictMode $AndroidBlockGuardPolicy. onReadFromDisk (Stri ctMode.java:1135)
  • 30.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Soluzione getContacts() .subscribeOn( Schedulers.io() ) .subscribe([...]); Schedulers. computation() io() immediate() newThread() trampoline()
  • 31.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato … altro problema android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. getContacts() .subscribeOn(Schedulers.io()) .observeOn( AndroidSchedulers.mainThread() ) .subscribe([...]); Soluzione
  • 32.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Nella pratica ● La ricerca debba essere eseguita ogni 500 millisecondi Vogliamo che: ● La ricerca debba partire solo dopo che sia stati inseriti almeno 3 caratteri
  • 33.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato RxBinding compile 'com.jakewharton.rxbinding:rxbinding:0.4.0' compile 'com.jakewharton.rxbinding:rxbinding-support-v4:0.4.0' compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0' [ . . .] https://github.com/JakeWharton/RxBinding
  • 34.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Nella pratica RxTextView .textChanges(searchField) .debounce(500, TimeUnit.MILLISECONDS) .filter( (charSequence) -> return charSequence.length() >= 3 ) .subscribe( [...] )
  • 35.
    Sviluppare App event-drivencon RxJava su Android - Boris D’Amato Grazie. @borisdamato +BorisDAmato