Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RX - Jerzy Chałupski

RX - Jerzy Chałupski

Android Tech Talks #10 [PL]
14/04/2015

Avatar for Tech Space guests

Tech Space guests

April 14, 2015
Tweet

More Decks by Tech Space guests

Other Decks in Technology

Transcript

  1. RX

  2. RX?

  3. public interface GitHubService {
 @GET("/users")
 List<User> getUsers();
 
 @GET("/users/{user}/repos")
 List<Repo>

    listRepos(@Path("user") User user);
 }
 
 public class User {
 String getLogin();
 }
 
 public class Repo {
 String getDescription(); User getOwner();
 }
  4. GitHubService service = /* retrofit magic */
 
 for (User

    user : service.getUsers()) {
 for (Repo repo : service.listRepos(user)) {
 if (repo.getDescription().contains("android")) {
 /* send invitation email */
 break;
 }
 }
 }
  5. public interface GitHubService {
 @GET("/users")
 List<User> getUsers();
 
 @GET("/users/{user}")
 User

    getUserDetails(@Path("user") User user);
 
 @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") User user);
 }
 
 public class User {
 String getLogin();
 String getLocation();
 }
 
 public class Repo {
 String getName();
 String getDescription();
 String getLanguage();
 boolean isFork(); User getOwner();
 }
  6. GitHubService service = /* retrofit magic */
 
 for (User

    user : service.getUsers()) {
 for (Repo repo : service.listRepos(user)) {
 boolean isAndroidRepo =
 repo.getDescription().contains("android") ||
 repo.getName().contains("android");
 boolean writtenInJava = repo.getLanguage().equals("Java");
 if (isAndroidRepo && writtenInJava && !repo.isFork()) {
 if (service.getDetails(user).getLocation().equals("Kraków")) { /* send invitation email */
 }
 break;
 }
 }
 }
  7. public interface GitHubService {
 @GET("/users")
 List<User> getUsers();
 
 @GET("/users/{user}")
 User

    getUserDetails(@Path("user") User user);
 
 @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") User user);
 }
 
 public class User {
 String getLogin();
 String getLocation();
 }
 
 public class Repo {
 String getName();
 String getDescription();
 String getLanguage();
 boolean isFork(); User getOwner();
 }
  8. public interface GitHubService {
 @GET("/users")
 void getUsers(Callback<List<User>> users);
 
 @GET("/users/{user}")


    void getUserDetails(@Path("user") User user, Callback<User> …);
 
 @GET("/users/{user}/repos")
 void listRepos(@Path("user") User user, Callback<List<Repo>> …); }
 
 public class User {
 String getLogin();
 String getLocation();
 }
 
 public class Repo {
 String getName();
 String getDescription();
 String getLanguage();
 boolean isFork(); User getOwner();
 }
  9. GitHubService service = /* retrofit magic */
 
 service.getUsers(new Callback<List<User>>()

    {
 @Override
 public void success(List<User> users, Response response) {
 for (User user : users) {
 service.listRepos(user, new Callback<List<Repo>>() {
 @Override
 public void success(List<Repo> repos, Response response) {
 for (Repo repo : repos) {
 boolean isAndroidRepo = repo.getDescription().contains("android") || repo.getName().contains("android");
 boolean writtenInJava = repo.getLanguage().equals("Java");
 if (isAndroidRepo && writtenInJava && !repo.isFork()) {
 service.getUserDetails(user, new Callback<User>() {
 @Override
 public void success(User user, Response response) {
 if (user.getLocation().equals("Kraków")) {
 /* send invitation email */
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 break;
 }
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });

  10. GitHubService service = /* retrofit magic */
 
 service.getUsers(new Callback<List<User>>()

    {
 @Override
 public void success(List<User> users, Response response) {
 for (User user : users) {
 service.listRepos(user, new Callback<List<Repo>>() {
 @Override
 public void success(List<Repo> repos, Response response) {
 for (Repo repo : repos) {
 boolean isAndroidRepo = repo.getDescription().contains("android") || repo.getName().contains("android");
 boolean writtenInJava = repo.getLanguage().equals("Java");
 if (isAndroidRepo && writtenInJava && !repo.isFork()) {
 service.getUserDetails(user, new Callback<User>() {
 @Override
 public void success(User user, Response response) {
 if (user.getLocation().equals("Kraków")) {
 /* send invitation email */
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 break;
 }
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });

  11. GitHubService service = /* retrofit magic */
 
 for (User

    user : service.getUsers()) {
 for (Repo repo : service.listRepos(user)) {
 boolean isAndroidRepo =
 repo.getDescription().contains("android") ||
 repo.getName().contains("android");
 boolean writtenInJava = repo.getLanguage().equals("Java");
 if (isAndroidRepo && writtenInJava && !repo.isFork()) {
 if (service.getDetails(user).getLocation().equals("Kraków")) { /* send invitation email */
 }
 break;
 }
 }
 }
  12. GitHubService service = /* retrofit magic */
 
 service.getUsers().stream()
 .flatMap(user

    -> service.listRepos(user).stream())
 .filter(repo -> repo.getName().contains(“android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .map(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .forEach(user -> { /* send invitation email */ });
  13. public interface GitHubService {
 @GET("/users")
 void getUsers(Callback<List<User>> users);
 }
 service.getUsers(new

    Callback<List<User>>() {
 @Override
 public void success(List<User> users, Response response) {
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });

  14. public interface GitHubService {
 @GET(“/users") Observable<List<User>> getUsers(); } 
 service.getUsers().subscribe(


    new Action1<List<User>>() {
 @Override
 public void call(List<User> users) { // onNext
 }
 },
 new Action1<Throwable>() {
 @Override
 public void call(Throwable throwable) { // onError
 }
 }
 );
  15. GitHubService service = /* retrofit magic */
 
 service.getUsers().stream()
 .flatMap(user

    -> service.listRepos(user).stream())
 .filter(repo -> repo.getName().contains(“android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .map(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .forEach(user -> { /* send invitation email */ });
  16. public interface GitHubService {
 @GET("/users")
 List<User> getUsers();
 
 @GET("/users/{user}")
 User

    getUserDetails(@Path("user") User user);
 
 @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") User user);
 }
 
 public class User {
 String getLogin();
 String getLocation();
 }
 
 public class Repo {
 String getName();
 String getDescription();
 String getLanguage();
 boolean isFork(); User getOwner();
 }
  17. public interface GitHubService {
 @GET("/users")
 Observable<List<User>> getUsers();
 
 @GET("/users/{user}")
 Observable<User>

    getUserDetails(@Path("user") User user);
 
 @GET("/users/{user}/repos")
 Observable<List<Repo>> listRepos(@Path("user") User user); }
 
 public class User {
 String getLogin();
 String getLocation();
 }
 
 public class Repo {
 String getName();
 String getDescription();
 String getLanguage();
 boolean isFork(); User getOwner();
 }
  18. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(Observable::from)


    .flatMap(service::listRepos)
 .flatMap(Observable::from)
 .filter(repo -> repo.getName().contains("android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .flatMap(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .subscribe(user -> { /* send invitation email */ });
  19. GitHubService service = /* retrofit magic */
 
 service.getUsers().stream()
 .flatMap(user

    -> service.listRepos(user).stream()) 
 .filter(repo -> repo.getName().contains(“android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .map(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .forEach(user -> { /* send invitation email */ });
  20. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(Observable::from)


    .flatMap(service::listRepos)
 .flatMap(Observable::from)
 .filter(repo -> repo.getName().contains("android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .flatMap(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .subscribe(user -> { /* send invitation email */ });
  21. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(Observable::from)


    .flatMap(service::listRepos)
 .flatMap(Observable::from)
 .filter(repo -> repo.getName().contains("android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .flatMap(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .subscribe(user -> { /* send invitation email */ },
 error -> { /* report to analytics */ }
 );
  22. GitHubService service = /* retrofit magic */
 
 service.getUsers(new Callback<List<User>>()

    {
 @Override
 public void success(List<User> users, Response response) {
 for (User user : users) {
 service.listRepos(user, new Callback<List<Repo>>() {
 @Override
 public void success(List<Repo> repos, Response response) {
 for (Repo repo : repos) {
 boolean isAndroidRepo = repo.getDescription().contains("android") || repo.getName().contains("android");
 boolean writtenInJava = repo.getLanguage().equals("Java");
 if (isAndroidRepo && writtenInJava && !repo.isFork()) {
 service.getUserDetails(user, new Callback<User>() {
 @Override
 public void success(User user, Response response) {
 if (user.getLocation().equals("Kraków")) {
 /* send invitation email */
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 break;
 }
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });
 }
 }
 
 @Override
 public void failure(RetrofitError error) {
 }
 });

  23. RX!

  24. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(Observable::from)


    .flatMap(service::listRepos)
 .flatMap(Observable::from)
 .filter(repo -> repo.getName().contains("android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .flatMap(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .subscribe(user -> { /* send invitation email */ },
 error -> { /* report to analytics */ }
 );
  25. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(new

    Func1<List<User>, Observable<? extends User>>() {
 @Override
 public Observable<? extends User> call(List<User> users) {
 return Observable.from(users);
 }
 })
 .flatMap(new Func1<User, Observable<? extends List<Repo>>>() {
 @Override
 public Observable<? extends List<Repo>> call(User user) {
 return service.listRepos(user);
 }
 })
 .flatMap(new Func1<List<Repo>, Observable<? extends Repo>>() {
 @Override
 public Observable<? extends Repo> call(List<Repo> repos) {
 return Observable.from(repos);
 }
 })
 .filter(new Func1<Repo, Boolean>() {
 @Override
 public Boolean call(Repo repo) {
 return repo.getName().contains("android") ||
 repo.getDescription().contains("android");
 }
 })
 .filter(new Func1<Repo, Boolean>() {
 @Override
 public Boolean call(Repo repo) {
 return repo.getLanguage().equals("Java");
 }
 })
 .filter(new Func1<Repo, Boolean>() {
 @Override
 public Boolean call(Repo repo) {
 return !repo.isFork();
 }
 })
 .map(new Func1<Repo, User>() {
 @Override
 public User call(Repo repo) {
 return repo.getOwner();
 }
 })
 .distinct()
 .flatMap(new Func1<User, Observable<? extends User>>() {
 @Override
 public Observable<? extends User> call(User user) {
 return service.getUserDetails(user);
 }
 })
 .filter(new Func1<User, Boolean>() {
 @Override
 public Boolean call(User user) {
 return user.getLocation().equals("Kraków");
 }
 })
 .subscribe(
 new Action1<User>() {
 @Override
 public void call(User user) { /* send invitation email */ }
 },
 new Action1<Throwable>() {
 @Override
 public void call(Throwable error) { /* report to analytics */ }
 }
 );

  26. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .map(it

    -> { /* time consuming operation */ } )
 .subscribe( /* show users on UI */ );
  27. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .subscribeOn(Schedulers.io())


    .observeOn(Schedulers.computation())
 .map(it -> { /* time consuming operation */ } )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show users on UI */ );
  28. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .subscribeOn(Schedulers.io())


    .observeOn(Schedulers.computation())
 .map(it -> { /* time consuming operation */ } )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show users on UI */ ); subscribeOn(): “Create the Observable on this thread”
  29. observeOn(): “Perform all other operations on this thread” GitHubService service

    = /* retrofit magic */
 
 service.getUsers()
 .subscribeOn(Schedulers.io())
 .observeOn(Schedulers.computation())
 .map(it -> { /* time consuming operation */ } )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show users on UI */ );
  30. WidgetObservable
 .text(textView)
 .filter(t -> t.text().length() > 2)
 .debounce(300, TimeUnit.MILLISECONDS)
 .observeOn(Schedulers.io())


    .map( /* fetch suggestions */ )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show suggestions in autocomplete */ );
  31. WidgetObservable
 .text(textView)
 .filter(t -> t.text().length() > 2)
 .observeOn(Schedulers.io())
 .map( /*

    fetch suggestions */ )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show suggestions in autocomplete */ );
  32. Preconditions.checkState(isUserAMonkey()); WidgetObservable
 .text(textView)
 .filter(t -> t.text().length() > 2)
 .observeOn(Schedulers.io())
 .map(

    /* fetch suggestions */ )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show suggestions in autocomplete */ );
  33. https://github.com/ReactiveX/RxJava/wiki/Backpressure Preconditions.checkState(isUserAMonkey()); WidgetObservable
 .text(textView)
 .filter(t -> t.text().length() > 2)
 .observeOn(Schedulers.io())


    .map( /* fetch suggestions */ )
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe( /* show suggestions in autocomplete */ );
  34. I want to… • load data in background, • process

    data in background, • cache processed data, • subscribe on changes and reload automatically… • …unless user left the UI which needs this data, in that case just mark the data as dirty and reload as needed, • have a basic backpressure handling.
  35. public class RxLoaderManager {
 public RxLoaderManager(Fragment fragment);
 
 public static

    abstract class LoaderBuilder<T> {
 public LoaderBuilder(int loaderId);
 protected abstract Loader<T> onCreateLoader(Context context);
 }
 
 public <T> Observable<T> load(LoaderBuilder<T> loaderBuilder); }
  36. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap(Observable::from)


    .flatMap(service::listRepos)
 .flatMap(Observable::from)
 .filter(repo -> repo.getName().contains("android") ||
 repo.getDescription().contains("android"))
 .filter(repo -> repo.getLanguage().equals("Java"))
 .filter(repo -> !repo.isFork())
 .map(Repo::getOwner)
 .distinct()
 .flatMap(service::getUserDetails)
 .filter(user -> user.getLocation().equals("Kraków"))
 .subscribe(user -> { /* send invitation email */ });
  37. GitHubService service = /* retrofit magic */
 
 service.getUsers()
 .flatMap()


    .flatMap()
 .flatMap()
 .filter( )
 .filter()
 .filter()
 .map()
 .distinct()
 .flatMap()
 .filter()
 .subscribe();
  38. GitHubService service = /* retrofit magic */
 
 Observable<T> t

    = service.getUsers()
 .flatMap()
 .flatMap()
 .flatMap(); Observable<U> u = t .filter() .map() .flatMap() .distinct(); Observable<V> v = t .filter() .map() .filter(); Observable.combineLatest(u, v) .distinct()
 .flatMap()
 .filter()
 .subscribe();
  39. GitHubService service = /* retrofit magic */
 
 Observable<T> t

    = service.getUsers()
 .flatMap()
 .flatMap()
 .flatMap(); Observable<U> u = t .filter() .map() .flatMap() .distinct(); Observable<V> v = t .filter() .map() .filter(); Observable.combineLatest(u, v) .distinct()
 .flatMap()
 .filter()
 .subscribe();
  40. /*
 RxFlowGraph
 deal ~> b1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~> assoc_contact_ids.in1
 b3 ~> assoc_contact_ids.in2


    assoc_contact_ids.out ~> assoc_contacts_list ~> associated_contact_data.in1
 b1 ~> primary_contact ~~~> b3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~> associated_contact_data.in2
 b1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~> associated_contact_data.in3
 b1 ~> owner_id ~> b2 ~> users_with_contacts_permission ~> permissions_map.in1
 b2 ~> users_with_accounts_permission ~> permissions_map.in2
 permissions_map.out ~> associated_contact_data.in4
 associated_contact_data.out ~>
 
 .subscribe(
 onNext ->
 );
 */
 • Inspired by Akka Streams (http://goo.gl/gjG4FZ) • Java API & implementation ~ready, but a bit ugly • Big pain: RxFlowGraph elements != Observables • To be revisited if/when Kotlin is officially supported?
  41. “Well, Mr. Frankel, who started this program, began to suffer

    from the computer disease that anybody who works with computers now knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is you *play* with them. They are so wonderful. You have these switches - if it's an even number you do this, if it's an odd number you do that - and pretty soon you can do more and more elaborate things if you are clever enough, on one machine. After a while the whole system broke down. Frankel wasn't paying any attention; he wasn't supervising anybody. The system was going very, very slowly - while he was sitting in a room figuring out how to make one tabulator automatically print arc- tangent X, and then it would start and it would print columns and then bitsi, bitsi, bitsi, and calculate the arc-tangent automatically by integrating as it went along and make a whole table in one operation. Absolutely useless. We *had* tables of arc-tangents. But if you've ever worked with computers, you understand the disease - the *delight* in being able to see how much you can do. But he got the disease for the first time, the poor fellow who invented the thing.” ― Richard P. Feynman, Surely You're Joking, Mr. Feynman!
  42. ?