Reactive Functional Programming with Java 8 on Android N Shipeng Xu May 6th 2016
What is Reactive Programming?
Observer Pattern
An Observable emits items. A Subscriber consumes those items. (from RxJava in practice) Observable Subscriber Items Observable & Subscriber
Observable Transform Items Subscriber Observable & Subscriber
Why Reactive Programming?
Quick example • Find all png images under a folder • Load the images into a gallery view
http://gank.io/post/560e15be2dca930e00da1083 new Thread() { @Override public void run() { super.run(); for (Folder folder : folders) { File[] files = folder.listFiles(); for (File file : files) { if (file.getName().endsWith(".png")) { final Bitmap bitmap = getBitmapFromFile(file); getActivity().runOnUiThread(new Runnable() { @Override public void run() { imageCollectorView.addImage(bitmap); } }); } } } } }.start(); Vanilla Java
http://gank.io/post/560e15be2dca930e00da1083 Observable.from(folders) .flatMap((folder) -> Observable.from(folder.listFiles()) ) .filter((file) -> file.getName().endsWith(".png") ) .map((file) -> getBitmapFromFile(file) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((bitmap) -> imageCollectorView.addImage(bitmap) ); RxJava
Create an Observable Observable observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("World"); subscriber.onCompleted(); } }); observable.subscribe(subscriber);To subscribe to an observable: Observable.just("Hello", "World") Or the shorter version:
Subscriber<String> subscriber = new Subscriber<String>() { @Override public void onNext(String s) { Log.d(tag, "Item: " + s); } @Override public void onCompleted() { Log.d(tag, "Completed!"); } @Override public void onError(Throwable e) { Log.d(tag, "Error!"); } }; Subscriber Sample
http://reactivex.io/documentation/observable.html http://rxmarbles.com/
Demo project
Get started with Java 8 on Android N android { compileSdkVersion 'android-N' buildToolsVersion "24.0.0 rc1" defaultConfig { applicationId "me.billhsu.rxdemo" minSdkVersion 'N' targetSdkVersion 'N' versionCode 1 versionName "1.0" jackOptions { enabled true } } compileOptions { targetCompatibility 1.8 sourceCompatibility 1.8 } }
Rx libraries for Android RxAndroid - Provide a Scheduler that schedules on the main thread or any given Looper. RxLifecycle - Lifecycle handling APIs for Android apps using RxJava RxBinding - RxJava binding APIs for Android's UI widgets. SqlBrite - A lightweight wrapper around SQLiteOpenHelper and ContentResolver which introduces reactive stream semantics to queries. Android-ReactiveLocation - Library that wraps location play services API boilerplate with a reactive friendly API. rx-preferences - Reactive SharedPreferences for Android RxFit - Reactive Fitness API Library for Android RxWear - Reactive Wearable API Library for Android RxPermissions - Android runtime permissions powered by RxJava RxNotification - Easy way to register, remove and manage notifications using RxJava
Android Scheduler Schedulers.io() Schedulers.computation() Schedulers.newThread() Schedulers.from(Executor) Schedulers.immediate() Schedulers.trampoline() Observable.just("Hello", "World") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* update UI*/);
REST Responses to Observables public interface GitHubApi { @GET("users/{users}/followers") Observable<List<GitHubUser>> getFollowers(@Path("users") String user); @GET("users/{users}") Observable<GitHubUser> getUser(@Path("users") String user); } private void setupRetrofit() { OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(5, TimeUnit.SECONDS); Retrofit retrofit = new Retrofit.Builder() .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl("https://api.github.com/") .build(); gitHubApi = retrofit.create(GitHubApi.class); } https://api.github.com/users/billhsu
RxView.clicks(button).subscribe((a) -> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users -> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); }); The click stream
Click stream to GitHubUser Stream RxView.clicks(button).subscribe((a) -> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users -> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); });
Subscribe to GitHubUser Stream RxView.clicks(button).subscribe((a) -> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users -> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); });
Summary

Reactive Functional Programming with Java 8 on Android N

  • 1.
    Reactive Functional Programming withJava 8 on Android N Shipeng Xu May 6th 2016
  • 2.
  • 3.
  • 4.
    An Observable emitsitems. A Subscriber consumes those items. (from RxJava in practice) Observable Subscriber Items Observable & Subscriber
  • 5.
  • 6.
  • 7.
    Quick example • Findall png images under a folder • Load the images into a gallery view
  • 8.
    http://gank.io/post/560e15be2dca930e00da1083 new Thread() { @Override publicvoid run() { super.run(); for (Folder folder : folders) { File[] files = folder.listFiles(); for (File file : files) { if (file.getName().endsWith(".png")) { final Bitmap bitmap = getBitmapFromFile(file); getActivity().runOnUiThread(new Runnable() { @Override public void run() { imageCollectorView.addImage(bitmap); } }); } } } } }.start(); Vanilla Java
  • 9.
    http://gank.io/post/560e15be2dca930e00da1083 Observable.from(folders) .flatMap((folder) -> Observable.from(folder.listFiles())) .filter((file) -> file.getName().endsWith(".png") ) .map((file) -> getBitmapFromFile(file) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((bitmap) -> imageCollectorView.addImage(bitmap) ); RxJava
  • 10.
    Create an Observable Observableobservable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("World"); subscriber.onCompleted(); } }); observable.subscribe(subscriber);To subscribe to an observable: Observable.just("Hello", "World") Or the shorter version:
  • 11.
    Subscriber<String> subscriber =new Subscriber<String>() { @Override public void onNext(String s) { Log.d(tag, "Item: " + s); } @Override public void onCompleted() { Log.d(tag, "Completed!"); } @Override public void onError(Throwable e) { Log.d(tag, "Error!"); } }; Subscriber Sample
  • 12.
  • 14.
  • 15.
    Get started withJava 8 on Android N android { compileSdkVersion 'android-N' buildToolsVersion "24.0.0 rc1" defaultConfig { applicationId "me.billhsu.rxdemo" minSdkVersion 'N' targetSdkVersion 'N' versionCode 1 versionName "1.0" jackOptions { enabled true } } compileOptions { targetCompatibility 1.8 sourceCompatibility 1.8 } }
  • 16.
    Rx libraries forAndroid RxAndroid - Provide a Scheduler that schedules on the main thread or any given Looper. RxLifecycle - Lifecycle handling APIs for Android apps using RxJava RxBinding - RxJava binding APIs for Android's UI widgets. SqlBrite - A lightweight wrapper around SQLiteOpenHelper and ContentResolver which introduces reactive stream semantics to queries. Android-ReactiveLocation - Library that wraps location play services API boilerplate with a reactive friendly API. rx-preferences - Reactive SharedPreferences for Android RxFit - Reactive Fitness API Library for Android RxWear - Reactive Wearable API Library for Android RxPermissions - Android runtime permissions powered by RxJava RxNotification - Easy way to register, remove and manage notifications using RxJava
  • 17.
  • 18.
    REST Responses to Observables publicinterface GitHubApi { @GET("users/{users}/followers") Observable<List<GitHubUser>> getFollowers(@Path("users") String user); @GET("users/{users}") Observable<GitHubUser> getUser(@Path("users") String user); } private void setupRetrofit() { OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(5, TimeUnit.SECONDS); Retrofit retrofit = new Retrofit.Builder() .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl("https://api.github.com/") .build(); gitHubApi = retrofit.create(GitHubApi.class); } https://api.github.com/users/billhsu
  • 19.
    RxView.clicks(button).subscribe((a) -> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users-> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); }); The click stream
  • 20.
    Click stream toGitHubUser Stream RxView.clicks(button).subscribe((a) -> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users -> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); });
  • 21.
    Subscribe to GitHubUser Stream RxView.clicks(button).subscribe((a)-> { button.setClickable(false); adapter.getGitHubUserList().clear(); adapter.notifyDataSetChanged(); progressBar.setVisibility(View.VISIBLE); gitHubApi.getFollowers(userName.getText().toString()) .flatMapIterable(users -> users) .flatMap(user -> gitHubApi.getUser(user.getLogin())) .filter(user -> !TextUtils.isEmpty(user.getCompany())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { adapter.getGitHubUserList().add(user); adapter.notifyDataSetChanged(); }, error -> { Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show(); button.setClickable(true); progressBar.setVisibility(View.GONE); }, () -> { button.setClickable(true); progressBar.setVisibility(View.GONE); }); });
  • 22.