introduction to retrofit and rxjava
TRANSCRIPT
Introduction to Retrofit andRxJava
AppDays 2015 Pordenone
Ego slide@fabioCollini
linkedin.com/in/fabiocollini
Folder Organizer
GDG Firenze
cosenonjaviste.it
nana bianca
Freapp
instal.com
Rain tomorrow?
2
Retrofit
• Turns your REST API into a Java interface
• Simple to use
• JSON conversion using Gson
• Custom converters
• …
3
RxJava
RxJava is a Java VM implementation of ReactiveX (Reactive Extensions): a
library for composing asynchronous and event-based programs by using
observable sequences.
github.com/ReactiveX/RxJava“
4
RxJava is not simple… 5
Demo project
github.com/fabioCollini/IntroToRetrofitRxJava
6
HTTP request definitionpublic interface StackOverflowService {
@GET("/users")
UserResponse getTopUsers();
}
01.
02.
03.
04.
05.
06.
7
HTTP request definitionpublic interface StackOverflowService {
@GET("/users")
UserResponse getTopUsers();
}
public class UserResponse {
private List<User> items;
public List<User> getItems() {
return items;
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
8
Service creationRestAdapter restAdapter =
new RestAdapter.Builder()
01.
02.
9
Service creationRestAdapter restAdapter =
new RestAdapter.Builder()
.setEndpoint("http://api.stackexchange.com/2.2/")
01.
02.
03.
10
Service creationRestAdapter restAdapter =
new RestAdapter.Builder()
.setEndpoint("http://api.stackexchange.com/2.2/")
.build();
01.
02.
03.
04.
11
Service creationRestAdapter restAdapter =
new RestAdapter.Builder()
.setEndpoint("http://api.stackexchange.com/2.2/")
.build();
StackOverflowService service =
restAdapter.create(StackOverflowService.class);
01.
02.
03.
04.
05.
06.
12
Service creationRestAdapter restAdapter =
new RestAdapter.Builder()
.setEndpoint("http://api.stackexchange.com/2.2/")
.setRequestInterceptor(request -> {
request.addQueryParam("site", "stackoverflow");
request.addQueryParam("key", "...");
})
.build();
StackOverflowService service =
restAdapter.create(StackOverflowService.class);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
13
Synchronous request
private List<User> loadItemsSync() {
List<User> users =
service.getTopUsers().getItems();
if (users.size() > 5) {
users = users.subList(0, 5);
}
return users;
}
01.
02.
03.
04.
05.
06.
07.
08.
14
Request parameters@GET("/users/{userId}/top-tags")
TagResponse getTags(@Path("userId") int userId);
@GET("/users/{userId}/badges")
BadgeResponse getBadges(@Path("userId") int userId);
01.
02.
03.
04.
05.
15
Request parameters@GET("/users/{userId}/top-tags")
TagResponse getTags(@Path("userId") int userId);
@GET("/users/{userId}/badges")
BadgeResponse getBadges(@Path("userId") int userId);
service.getTags(12345);
/users/12345/top-tags?site=stackoverflow&key=…
01.
02.
03.
04.
05.
16
Other annotations• @GET, @POST, @PUT, @DELETE, @HEAD
• @Path
• @Query
• @QueryMap
• @Body
• @FormUrlEncoded
• @Field
• @Headers
17
CompositionList<User> users = service.getTopUsers().getItems();
if (users.size() > 5) {
users = users.subList(0, 5);
}
List<UserStats> statsList = new ArrayList<>();
for (User user : users) {
TagResponse tags =
service.getTags(user.getId());
BadgeResponse badges =
service.getBadges(user.getId());
statsList.add(new UserStats(user,
tags.getItems(), badges.getItems()));
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
18
AsyncTasknew AsyncTask<Void, Void, List<User>>() {
@Override
protected List<User> doInBackground(Void... p) {
try {
return loadItemsSync();
} catch (Exception e) {
return null;
}
}
@Override
protected void onPostExecute(List<User> users) {
if (users != null) {
adapter.addAll(users);
} else {
showError();
}
}
}.execute();
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19
Synchronous request
public interface StackOverflowService {
@GET("/users")
UserResponse getTopUsers();
}
01.
02.
03.
04.
05.
06.
20
Callbacks
public interface StackOverflowService {
@GET("/users")
void getTopUsers(Callback<UserResponse> callback);
}
01.
02.
03.
04.
05.
06.
21
Callbacks in actionservice.getTopUsers(new Callback<UserResponse>() {
@Override public void success(
UserResponse userResponse, Response r) {
List<User> users = userResponse.getItems();
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
}
@Override public void failure(RetrofitError e) {
showError();
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
22
Callbacks in actionservice.getTopUsers(new Callback<UserResponse>() {
@Override public void success(
UserResponse userResponse, Response r) {
List<User> users = userResponse.getItems();
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
}
@Override public void failure(RetrofitError e) {
showError();
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
23
Callback hellservice.getBadges(userId, new Callback<BadgeResponse>() {
@Override public void success(BadgeResponse badges, Response r) {
service.getTags(userId, new Callback<TagResponse>() {
@Override public void success(TagResponse tags, Response r) {
callback.success(new UserStats(user,
tags.getItems(), badges.getItems()), r);
}
@Override public void failure(RetrofitError error) {
callback.failure(error);
}
});
}
@Override public void failure(RetrofitError error) {
callback.failure(error);
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
24
Retrofit
public interface StackOverflowService {
@GET("/users")
void getTopUsers(Callback<UserResponse> callback);
}
01.
02.
03.
04.
05.
06.
25
Retrofit + RxJava
public interface StackOverflowService {
@GET("/users")
Observable<UserResponse> getTopUsers();
}
01.
02.
03.
04.
05.
06.
26
RxJava in actionservice.getTopUsers()
.subscribe(new Action1<UserResponse>() {
@Override public void call(UserResponse r) {
List<User> users = r.getItems();
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
}
}, new Action1<Throwable>() {
@Override public void call(Throwable t) {
showError();
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
27
Java 8 / Retrolambda
service.getTopUsers()
.subscribe(
r -> {
List<User> users = r.getItems();
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
},
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
28
Threadingservice
.getTopUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
r -> {
List<User> users = r.getItems()
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
},
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
29
subscribe
public final Subscription subscribe(
final Action1<? super T> onNext,
final Action1<Throwable> onError) {
//...}
01.
02.
03.
04.
05.
30
onNext | onError 31
onNext* (onComplete | onError)? 32
Observable creationObservable.just(1, 2, 3);
33
Observable creationObservable.just(1, 2, 3);
Observable.from(Arrays.asList("A", "B", "C", "D"));
01.
02.
34
Observable creationObservable.just(1, 2, 3);
Observable.from(Arrays.asList("A", "B", "C", "D"));
Observable.error(new IOException());
01.
02.
03.
35
Observable creationObservable.just(1, 2, 3);
Observable.from(Arrays.asList("A", "B", "C", "D"));
Observable.error(new IOException());
Observable.interval(5, TimeUnit.SECONDS);
01.
02.
03.
04.
36
Observable creationObservable.just(1, 2, 3);
Observable.from(Arrays.asList("A", "B", "C", "D"));
Observable.error(new IOException());
Observable.interval(5, TimeUnit.SECONDS);
Observable.create(subscriber -> {
try {
subscriber.onNext(createFirstValue());
subscriber.onNext(createSecondValue());
subscriber.onCompleted();
} catch (Throwable t) {
subscriber.onError(t);
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
37
Observable in actionpublic Subscription subscribe(
Action1<? super T> onNext,
Action1<Throwable> onError,
Action0 onComplete
);
01.
02.
03.
04.
05.
38
Observable in actionpublic Subscription subscribe(
Action1<? super T> onNext,
Action1<Throwable> onError,
Action0 onComplete
);
Observable.just(1, 2, 3).subscribe(
System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Completed")
);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
39
ObserverObservable.just(1, 2, 3)
.subscribe(new Observer<Integer>() {
@Override public void onNext(Integer i) {
System.out.println(i);
}
@Override public void onError(Throwable t) {
t.printStackTrace();
}
@Override public void onCompleted() {
System.out.println("Completed");
}
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
40
map 41
mapservice.getTopUsers()
.subscribe(
r -> {
List<User> users = r.getItems()
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
},
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
42
mapservice.getTopUsers()
.map(r -> r.getItems())
.subscribe(
users -> {
if (users.size() > 5)
users = users.subList(0, 5);
adapter.addAll(users);
},
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
43
mapservice.getTopUsers()
.map(r -> r.getItems())
.map(users -> users.size() > 5 ?
users.subList(0, 5) : users)
.subscribe(
users -> adapter.addAll(users),
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
44
mapservice.getTopUsers()
.map(UserResponse::getItems)
.map(users -> users.size() > 5 ?
users.subList(0, 5) : users)
.subscribe(
adapter::addAll,
t -> showError()
);
01.
02.
03.
04.
05.
06.
07.
08.
45
zip 46
zip
Observable.zip(
service.getTags(user.getId()),
service.getBadges(user.getId()),
(tags, badges) ->
new UserStats(
user, tags.getItems(), badges.getItems())
)
);
01.
02.
03.
04.
05.
06.
07.
08.
47
zip
Observable.zip(
service.getTags(user.getId()),
service.getBadges(user.getId()),
(tags, badges) ->
new UserStats(
user, tags.getItems(), badges.getItems())
)
);
01.
02.
03.
04.
05.
06.
07.
08.
48
zip
Observable.zip(
service.getTags(user.getId()),
service.getBadges(user.getId()),
(tags, badges) ->
new UserStats(
user, tags.getItems(), badges.getItems())
)
);
01.
02.
03.
04.
05.
06.
07.
08.
49
zip
Observable.zip(
service.getTags(user.getId()),
service.getBadges(user.getId()),
(tags, badges) ->
new UserStats(
user, tags.getItems(), badges.getItems())
)
);
01.
02.
03.
04.
05.
06.
07.
08.
50
zip
Observable.zip(
service.getTags(user.getId()),
service.getBadges(user.getId()),
(tags, badges) ->
new UserStats(
user, tags.getItems(), badges.getItems()
)
);
01.
02.
03.
04.
05.
06.
07.
08.
51
zip
Observable.zip(
service.getTags(user.getId())
.map(TagResponse::getItems),
service.getBadges(user.getId())
.map(BadgeResponse::getItems),
(tags, badges) ->
new UserStats(user, tags, badges)
);
01.
02.
03.
04.
05.
06.
07.
08.
52
Multi value map Observable.just(1, 2, 3).map(
i -> Observable.just(i * 10, i * 10 + 1)
);
01.
02.
03.
53
Multi value mapObservable<Observable<Integer>> observable =
Observable.just(1, 2, 3).map(
i -> Observable.just(i * 10, i * 10 + 1)
);
01.
02.
03.
04.
54
Multi value mapObservable<Observable<Integer>> observable =
Observable.just(1, 2, 3).map(
i -> Observable.just(i * 10, i * 10 + 1)
);
[1, 2, 3]
[[10, 11], [20, 21], [30, 31]]
01.
02.
03.
04.
05.
06.
07.
08.
55
flatMap 56
flatMap
Observable<Integer> observable =
Observable.just(1, 2, 3).flatMap(
i -> Observable.just(i * 10, i * 10 + 1)
);
[1, 2, 3]
[10, 11, 20, 21, 30, 31]
01.
02.
03.
04.
05.
06.
07.
08.
57
flatMap
Observable<Profile> observable =
service.login(userName, password)
.flatMap(token -> service.getProfile(token));
01.
02.
03.
58
flatMap
Observable<Profile> observable =
service.login(userName, password)
.flatMap(token -> service.getProfile(token));
Observable<Profile> observable =
service.login(userName, password)
.flatMap(service::getProfile);
01.
02.
03.
04.
05.
06.
07.
59
flatMap
service
.getTopUsers()//1<UserResponse>01.
02.
60
flatMap
service
.getTopUsers()//1<UserResponse> .flatMap(r -> Observable.from(r.getItems()))
//20<User>
01.
02.
03.
04.
61
flatMap
service
.getTopUsers()//1<UserResponse> .flatMapIterable(UserResponse::getItems)//20<User>
01.
02.
03.
62
flatMap
service
.getTopUsers()//1<UserResponse> .flatMapIterable(UserResponse::getItems)//20<User> .limit(5)//5<User>
01.
02.
03.
04.
63
flatMap
service
.getTopUsers()//1<UserResponse> .flatMapIterable(UserResponse::getItems)//20<User> .limit(5)//5<User> .flatMap(this::loadUserStats)//5<UserStats>
01.
02.
03.
04.
05.
64
flatMap
service
.getTopUsers()//1<UserResponse> .flatMapIterable(UserResponse::getItems)//20<User> .limit(5)//5<User> .flatMap(this::loadUserStats)//5<UserStats> .toList();//1<List<UserStats>>
01.
02.
03.
04.
05.
06.
65
Order is not preserved 66
flatMap source code
public final <R> Observable<R> flatMap(
Func1<
? super T,
? extends Observable<? extends R>
> func) {
return merge(map(func));
}
01.
02.
03.
04.
05.
06.
07.
67
concatMap 68
concatMap source code
public final <R> Observable<R> concatMap(
Func1<
? super T,
? extends Observable<? extends R>
> func) {
return concat(map(func));
}
01.
02.
03.
04.
05.
06.
07.
69
concatMap
service
.getTopUsers()
.flatMapIterable(UserResponse::getItems)
.limit(5)
.concatMap(this::loadUserStats)
.toList();
01.
02.
03.
04.
05.
06.
70
timeout
service
.getTopUsers()
.flatMapIterable(UserResponse::getItems)
.limit(5)
.concatMap(this::loadRepoStats)
.toList()
.timeout(20, TimeUnit.SECONDS);
01.
02.
03.
04.
05.
06.
07.
71
retry
service
.getTopUsers()
.retry(2)
.flatMapIterable(UserResponse::getItems)
.limit(5)
.concatMap(this::loadRepoStats)
.toList()
.timeout(20, TimeUnit.SECONDS)
.retry(1);
01.
02.
03.
04.
05.
06.
07.
08.
09.
72
Cachepublic class Cache {
private List<UserStats> items;
public void save(List<UserStats> users) {
items = users;
}
public Observable<List<UserStats>> load(
Throwable t) {
if (items == null)
return Observable.error(t);
else
return Observable.just(items);
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
73
doOnNext / onErrorResumeNext
service.getTopUsers()
.retry(2)
.flatMapIterable(UserResponse::getItems)
.limit(5)
.concatMap(this::loadRepoStats)
.toList()
.timeout(20, TimeUnit.SECONDS)
.retry(1)
.doOnNext(cache::save)
.onErrorResumeNext(cache::load);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
74
SubscriptionObservable
.interval(1, TimeUnit.SECONDS)
.timestamp()
.subscribe(System.out::println);
01.
02.
03.
04.
75
SubscriptionSubscription subscription = Observable
.interval(1, TimeUnit.SECONDS)
.timestamp()
.subscribe(System.out::println);
Thread.sleep(2500);
subscription.unsubscribe();
01.
02.
03.
04.
05.
06.
07.
08.
76
SubscriptionSubscription subscription = Observable
.interval(1, TimeUnit.SECONDS)
.timestamp()
.subscribe(System.out::println);
Thread.sleep(2500);
subscription.unsubscribe();
Timestamped(timestampMillis = 1429360406807, value = 0)
Timestamped(timestampMillis = 1429360407805, value = 1)
01.
02.
03.
04.
05.
06.
07.
08.
77
How many requests?Observable<UserResponse> observable =
service.getTopUsers();
Subscription s1 = observable.subscribe(
System.out::println, Throwable::printStackTrace);
Subscription s2 = observable.subscribe(
System.out::println, Throwable::printStackTrace);
01.
02.
03.
04.
05.
06.
07.
78
How many requests?Observable<UserResponse> observable =
service.getTopUsers();
Subscription s1 = observable.subscribe(
System.out::println, Throwable::printStackTrace);
Subscription s2 = observable.subscribe(
System.out::println, Throwable::printStackTrace);
• 2 requests
• Retrofit Observables are cold
01.
02.
03.
04.
05.
06.
07.
79
Hot observablesObservable<UserResponse> observable =
service.getTopUsers();
ConnectableObservable<UserResponse> replayObservable
= observable.replay(1);
Subscription s1 = replayObservable.subscribe(
System.out::println, Throwable::printStackTrace);
Subscription s2 = replayObservable.subscribe(
System.out::println, Throwable::printStackTrace);
Subscription s3 = replayObservable.connect();
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
80
Activity lifecycle@Override public View onCreateView(...) {
...
retainedFragment = RetainedFragment
.getOrCreate(getActivity());
if (retainedFragment.get() == null) {
Observable<List<T>> observable = loadItems()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
retainedFragment.bind(observable.replay(1));
}
...
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
81
Activity lifecycle@Override public void onResume() {
super.onResume();
subscription = retainedFragment.get()
.subscribe(
this::showDataInList,
t -> showError()
);
}
@Override public void onPause() {
super.onPause();
subscription.unsubscribe();
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
82
RetainedFragmentpublic class RetainedFragment<T> extends Fragment {
private Subscription connectableSubscription = Subscriptions.empty();
private ConnectableObservable<T> observable;
public RetainedFragment() {
setRetainInstance(true);
}
public void bind(ConnectableObservable<T> observable) {
this.observable = observable;
connectableSubscription = observable.connect();
}
@Override public void onDestroy() {
super.onDestroy();
connectableSubscription.unsubscribe();
}
public Observable<T> get() {
return observable;
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
83
RetainedFragmentpublic class RetainedFragment<T> extends Fragment {
private Subscription connectableSubscription = Subscriptions.empty();
private ConnectableObservable<T> observable;
public RetainedFragment() {
setRetainInstance(true);
}
public void bind(ConnectableObservable<T> observable) {
this.observable = observable;
connectableSubscription = observable.connect();
}
@Override public void onDestroy() {
super.onDestroy();
connectableSubscription.unsubscribe();
}
public Observable<T> get() {
return observable;
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
84
RetainedFragmentpublic class RetainedFragment<T> extends Fragment {
private Subscription connectableSubscription = Subscriptions.empty();
private ConnectableObservable<T> observable;
public RetainedFragment() {
setRetainInstance(true);
}
public void bind(ConnectableObservable<T> observable) {
this.observable = observable;
connectableSubscription = observable.connect();
}
@Override public void onDestroy() {
super.onDestroy();
connectableSubscription.unsubscribe();
}
public Observable<T> get() {
return observable;
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
85
Thanks for your attention!
Questions?
github.com/fabioCollini/IntroToRetrofitRxJava
@fabioCollini
linkedin.com/in/fabiocollini
cosenonjaviste.it
86
@fabioCollini cosenonjaviste.it