introduction to retrofit and rxjava

86
Introduction to Retrofit and RxJava AppDays 2015 Pordenone

Upload: fabio-collini

Post on 20-Jul-2015

462 views

Category:

Engineering


2 download

TRANSCRIPT

Introduction to Retrofit andRxJava

AppDays 2015 Pordenone

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