Panoramica su Reactive Programming e l'utilizzo di RxJava per lo sviluppo di applicazioni Android.
Talk tenuto in occasione della conferenza GDG Bari Devfest 2016 presso il Politecnico di Bari.
5. 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.
6. 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
7. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Reactive Programming
Erik Meijer Ben Christensen
Rx.Net RxJava
Dávid Karnok
RxJava
8. 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
9. 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
10. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Comunicazione Observable - Observer
Observer
subscribe
Observable
onNext()
onComplete()
onError()
onSubscribe()
11. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Android Time!
RecyclerView
Lista dei contatti
12. 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()
13. 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([...]);
14. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Creazione di un Observable
Observable.empty() Observable.never() Observable.throw()
15. 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);
16. 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
17. 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
18. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Andrea Gianni
Giorgio
getContacts().take(3)Take
Carlo Andrea
19. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Andrea Gianni
getContacts().takeLast(3)
Take
Last
Carlo Andrea Gianni
20. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Filtering
Giorgio Carlo Giorgio Gianni
getContacts().distinct()Distinct
Carlo GianniGiorgio
22. 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
23. 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
24. 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
25. 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
26. 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
27. 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)
28. 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();
}});}
29. 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)
30. Sviluppare App event-driven con RxJava su Android - Boris D’Amato
Soluzione
getContacts()
.subscribeOn( Schedulers.io() )
.subscribe([...]);
Schedulers.
computation()
io()
immediate()
newThread()
trampoline()
31. 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
32. 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
33. 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
34. 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( [...] )