preface

There are a lot of articles on the web about rxJava, which explain what rxJava is and some of the high quality operators, but there are few real scenarios for the use of RxJava in the real project. This article mainly discusses the application scenarios of RxJava in the real project, and the use of RxBinding in the real project. Since RXBind2 itself depends on RxJava2, it is ok to introduce rxBinding in the project and not rxJava2.

Implementation 'com. Jakewharton. Rxbinding2: rxbinding: 2.1.1'Copy the code

After introduction, let’s take a look at common usage scenarios:

1. Optimize search

Basically, there is a requirement of search function in APP. It monitors the text changes of ET and requests the server to pull data. If the processor is not optimized, the server will be requested every time the et value changes, and data confusion is likely to occur in weak network environment. If you do not use RxJava to handle the various timer will be confused, so let’s see how rxJava elegant handling:

Rxtextview.textchanges (mbinding.etSearch) // Optimize search rxTextView.textChanges (mbinding.etSearch) // skip(1) // Debounce sends an event if there is no action for a certain amount of time .debounce(1000, timeunit.milliseconds) // Both of these are data conversions //flatMap: When multiple network requests are received at the same time, the previous network data overwrites the previous network data //switchMap: When multiple network requests are received at the same time, the last sent request takes effect. SwitchMap (new Function<CharSequence, ObservableSource<List<String>>>() { @Override public ObservableSource<List<String>> apply(CharSequence charSequence) throws Exception { String searchKey = charSequence.toString(); System.out.println("binding======= search content :" + searchKey); List<String> List = new ArrayList<String>(); List.add (" xiao Liu brother "); List.add (" cute "); return Observable.just(list); } //. OnErrorResumeNext () Access to the data we need. SubscribeOn (Schedulers. IO ()) / / update in the main thread. The interface observeOn (AndroidSchedulers. MainThread ()). The subscribe (new Consumer<List<String>>() { @Override public void accept(List<String> strings) throws Exception { System. The out. Println (" binding = = = = = = = to search "+ strings. The size () +" data "); }});Copy the code

Skip (1); skip(1); skip(1); skip(1);

2. Combine rxBinding to prevent hand shaking

Rxview. clicks(mbinding.btclick).throttlefirst (2, Timeunit.seconds).subscribe(c -> system.out.println ("binding======= click the button "));Copy the code

If a page has a button, click a request to the server or other operations can be done, here did 2 seconds to respond to a click event, very common scenario.

3. Long press event

Subscribe (c-> system.out.println ("binding======= ")); / / rxview. longClicks(mbinding.btclick). Subscribe (c-> system.out.println ("binding======= "));Copy the code

Long press event, this needless to say.

4. Listen for the selected state of the view

/ * * * changes to the checkbox to select textview * / RxCompoundButton checkedChanges (mBinding. The checkbox). The subscribe (new Consumer < Boolean > () { @Override public void accept(Boolean aBoolean) throws Exception { mBinding.tvCb.setText(aBoolean ? "Button selected" : "button not selected "); }});Copy the code

If the page has a CB, such as check to agree to read the user agreement, used to monitor the selected status to do some logical operations, a few lines of code to do it.

5, registration, login and other verification code to obtain the countdown operation

Public void clickTimer(View View) {public void clickTimer(View View) {public void clickTimer(View View) {public void clickTimer(View View) {public void clickTimer(View View) { TimeUnit.SECONDS) .subscribe(new Observer<Long>() { @Override public void onSubscribe(Disposable d) { } @Override public  void onNext(Long value) { System.out.println("binding=======value:" + value); //0 } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); Final int count = 10; Observable.interval(0, 1, timeunit.seconds) Take (count + 1)// Set the number of cycles. Map (new Function<Long, Long>() { @Override public Long apply(Long aLong) throws Exception { return count - aLong; } }) .doOnSubscribe(new Consumer<Disposable>() { @Override public void accept(Disposable disposable) throws Exception { / / when sending data set to not click mBinding. BtCutdown. SetEnabled (false); / / background Color mBinding. BtCutdown. SetBackgroundColor (Color. ParseColor (" # 39 c6c1 ")); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Long>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Long value) { mBinding.btCutdown.setText("" + value); } @Override public void onError(Throwable e) { } @Override public void onComplete() { MBinding. BtCutdown. SetText (" get back "); mBinding.btCutdown.setEnabled(true); mBinding.btCutdown.setBackgroundColor(Color.parseColor("#d1d1d1")); }}); }Copy the code

Very simple!

6. In the case of registration and login, all input is legal and then turn on the login button

Observable<CharSequence> name = rxTextView.textChanges (mbinding.etName).skip(1); / / Observable<CharSequence> name = rxTextView.textChanges (mBinding. Observable<CharSequence> age = RxTextView.textChanges(mBinding.etAge).skip(1); Observable.combineLatest(name, age, new BiFunction<CharSequence, CharSequence, Boolean>() { @Override public Boolean apply(CharSequence charSequence, CharSequence charSequence2) throws Exception { boolean isNameEmpty = TextUtils.isEmpty(mBinding.etName.getText()); boolean isAgeEmpty = TextUtils.isEmpty(mBinding.etAge.getText()); return ! isNameEmpty && ! isAgeEmpty; } }) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean aBoolean) throws Exception { System.out.println("bt======" + aBoolean); mBinding.btSubmit.setEnabled(aBoolean); }});Copy the code

7. Use interval to perform periodic operations

/* Disposable mDisposable; /* Disposable mDisposable; public void clickIntervar(View view) { Observable.interval(2, TimeUnit.SECONDS) .subscribe(new Observer<Long>() { @Override public void onSubscribe(Disposable d) { mDisposable =d; } @override public void onNext(Long value) {system.out.println ("binding======= "+ value); if (value == 5L) { System.out.println("binding=======dispose"); mDisposable.dispose(); } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); }Copy the code

Use schedulePeriodically to do polling requests

Create (new ObservableOnSubscribe<String>() {@override public periodically polling requests once every 3 seconds */ Observable void subscribe(final ObservableEmitter<String> e) throws Exception { Schedulers.newThread().createWorker() .schedulePeriodically(new Runnable() { @Override public void run() { e.onNext("net work-----"); } }, 0, 3, TimeUnit.SECONDS); } }).subscribe(new Consumer<String>() { @Override public void accept(String s) throws Exception { System.out.println("binding=======net work"); }});Copy the code

9. Network error and retry

/** * Network error retry * the just operator is retrofit. */ int mRetryCount; public void clickRetry(View view) { Observable.just("retry") .retryWhen(new Function<Observable<Throwable>, ObservableSource<? >>() { @Override public ObservableSource<? > Apply (Observable<Throwable> throwableObservable) throws Exception {// Generics in the parameter Observable<Throwable> = an Exception thrown by the upstream operator, By the conditions to determine the type of abnormal return throwableObservable. FlatMap (new Function < Throwable, ObservableSource <? >>() { @Override public ObservableSource<? > apply(Throwable Throwable) throws Exception {// Determine whether to retry based on Exception information. If (Throwable instanceof IOException) { System.out.println("retry======y=="); If (mRetryCount < 5) {mRetryCount++; / * * * 1, sent via the returned observables = Next events, thus making retryWhen () subscriptions, finally realizes the retry function * 2, 1 time delay and try again using delay operator = delay for a period of time to send, */ int time = 1000 + mRetryCount * 1000; */ mRetryCount * 1000; return Observable.just(1).delay(time, TimeUnit.MILLISECONDS); } else { System.out.println("retry======5=="); Return Observable. Error (new Throwable(" aborted 5 times ")); }} else {/ / no retry System. Out. Println (" retry = = = = = = n = = "); Error (new Throwable(" non-network exception (non-I /O exception) ")); }}}); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<String>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(String value) { System.out.println("retry======suc==" + value); } @Override public void onError(Throwable e) { System.out.println("retry======err==" + e.toString()); } @Override public void onComplete() { } }); }Copy the code

10. Resolve network nesting requests

Public void clickRequest(View View) {Observable<String> requestLogin = Observable.just("requestLogin"); final Observable<String> request2 = Observable.just("request2"); requestLogin.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(new Consumer<String>() { @Override public void accept(String s) throws Exception { System.out.println("flat=======loginsuccess"); } }) .observeOn(Schedulers.io()) .flatMap(new Function<String, ObservableSource<String>>() { @Override public ObservableSource<String> apply(String s) throws Exception { // Convert network request 1 to network request2, that is, send network request2 and return request2. }}) // (the new observer is also the new observer) switch to the IO thread to initiate the login request. Since flatMap is applied to the original observer, for the old observer, it is the new observer, so switch threads via observeOn // but for the original observer, It is a new observed. ObserveOn (AndroidSchedulers. MainThread ()). The subscribe (new Consumer < String > () {@ Override public void Accept (String s) throws Exception {system.out. println("flat======= second request succeeded "); }}, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { System.out.println("flat=======loginerr"); }}); }Copy the code

11. Back pressure — This is just a note

/ observer using back-pressure Flowable g * * * * send and subscribe to events is inconsistent with the velocity of solution * < p > * note: synchronous subscription, observed & observer work in with one thread, no buffer in synchronous subscribe relationship. * After sending an event, the observed must wait for the observer to receive it before sending another event. If the Subscription. The request is not set, * observer receives less than events, throws MissingBackpressureException anomalies. */ Subscription mSubscription; public void clickFlow(View view) { Flowable.create(new FlowableOnSubscribe<Integer>() { @Override public void Subscribe (FlowableEmitter<Integer> e) throws Exception {/** * * In the case of a synchronous subscription, the e.equested () method is called to retrieve the number of events that the current observer needs to receive. * due to both in different threads, so cannot be observed by FlowableEmitter. Requested () know the viewer ability to receive events. * Asynchronous reverse control: */ long count = e.ested (); System.out.println("flowable====== Number of events to receive =" + count); e.onNext(1); e.onNext(2); e.onNext(3); e.onNext(4); e.onNext(5); e.onComplete(); }}, BackpressureStrategy.ERROR) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Integer>() {@override public void onSubscribe(Subscription s) {// Determines how many events the observer can receive. If this is not set, events will not be received. // However, the observed still sends events (stored in the cache, size 128), and can retrieve the observed events when the observer needs them (such as click events). // However, when the cache is full, an overflow will be reported. Namely, s.r equest (Long. MAX_VALUE); mSubscription = s; s.request(2); // s.request(1); // If a synchronous subscriber observer requests to receive events continuously, Ested () returns 3} @override public void onNext(Integer Integer) {system.out.println ("flowable=======" + Integer);  } @Override public void onError(Throwable t) { } @Override public void onComplete() { } }); }Copy the code

12. Add a dynamic permission

Add depend on the compile 'com. Tbruyelle. Rxpermissions2: rxpermissions: 0.9.5 @ aar' / / remember dangerous permissions Also need to configure the manifest file. RxPermissions = new RxPermissions(this); RxPermissions = new RxPermissions(this); RxView.clicks(mBinding.btPermission) .throttleFirst(1, TimeUnit.SECONDS) .subscribeOn(AndroidSchedulers.mainThread()) .compose(permissions.ensure(Manifest.permission.CAMERA)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean aBoolean) throws Exception { if (aBoolean) { System. The out. Println (" binding = = = = = = = allow "); } else {system.out. println("binding======= reject "); }}});Copy the code

13. Combine RetroFIT network request encapsulation and unified error preprocessing

The above application scenarios are in the existing project, and the network encapsulation and unified error preprocessing involved in the initial stage of project architecture construction should be written separately due to space problems. Generally, the data returned by the network is a fixed format encapsulated by the background (such as error codes and error messages set by the background interface), which is easier to process. However, sometimes the data format returned by the API is the native HTTP response format, which is encapsulated with a layer of response generic class, which is slightly more complicated than the first case. Save that for my next blog post.

Scenarios used in other projects, encountered will be updated in this blog…

Finally, the international convention posted the project address: point I point I view the demo, if it is helpful to you, please start to encourage you, thank you.