RxAndroid 基础

RxAndroid 基础#

参考hi大头鬼hi 的微博,写代码进行测试学习,以下记录共享,同时以便之后查阅。 由于不熟悉lambda,同时开始学习也不建议直接使用lambda,以下大部分代码均使用常规方法编写。

RxJava在github上的地址
RxAndroid在github上的地址

首先,工程中引入:

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'

//引入RxAndroid----begin
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
//引入RxAndroid----end

// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
}

一、基本使用

常规语法

Observable<String> myObservable = Observable.create(
            new Observable.OnSubscribe<String>() {
                @Override
                public void call(Subscriber<? super String> sub) {
                    sub.onNext("Observable Hello, world!");
                    sub.onCompleted();
                }
            }
    );
    
    Subscriber<String> mySubscriber = new Subscriber<String>() {
        @Override
        public void onNext(String s) {
            L.d("onNext s: " + s);
           }

        @Override
        public void onCompleted() { }

        @Override
        public void onError(Throwable e) { }
    };

    myObservable.subscribe(mySubscriber);

简化语法

Observable.just(1)
            .subscribe(new Action1<Integer>() {
                @Override
                public void call(Integer integer) {
                    L.d("onNext integer: " + integer);
                }
            });

map操作符变换

Observable.just("Hello, world!")
            .map(new Func1<String, String>() {
                @Override
                public String call(String s) {
                    return s + " -Map";
                }
            })
            .subscribe(new Action1<String>() {
                @Override
                public void call(String s) {
                    L.d("onNext s: " + s);
                }
            });

二、操作符

Observable.from()

Observable.from()方法,它接收一个集合作为输入,然后每次输出一个元素给subscriber:

 List<String> urls = new ArrayList<>();
    urls.add("url1");
    urls.add("url2");
    urls.add("url3");
    Observable.from(urls)
            .subscribe(new Action1<String>() {
                @Override
                public void call(String s) {
                    L.d("onNext s: " + s);
                }
            });

输出如下:
nNext s: url1
onNext s: url2
onNext s: url3

   Observable<List<String>> myObservableUrls = Observable.create(
            new Observable.OnSubscribe<List<String>>() {
                @Override
                public void call(Subscriber<? super List<String>> subscriber) {
                    List<String> urls = new ArrayList<>();
                    urls.add("url1");
                    urls.add("url2");
                    urls.add("url3");
                    subscriber.onNext(urls);
                }
            }
    );

    myObservableUrls.subscribe(new Action1<List<String>>() {
        @Override
        public void call(List<String> strings) {
            Observable.from(strings)
                    .subscribe(new Action1<String>() {
                        @Override
                        public void call(String s) {
                            L.d("myObservableUrls onNext s: " + s);
                        }
                    });
        }
    });

输出如下:
myObservableUrls onNext s: url1
myObservableUrls onNext s: url2
myObservableUrls onNext s: url3
在前一个Observable输出是list时,嵌套太多,看起来比较乱。

Observable.flatMap()

Observable.flatMap()接收一个Observable的输出作为输入,同时输出另外一个Observable。

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
            L.d("myObservableUrls.flatMap onNext s: " + s);
        }
    }); 

避免了上面直接使用Observable.from时需要内如再次subscribe导致的内部嵌套。理解flatMap的关键点在于,flatMap输出的新的Observable正是我们在Subscriber想要接收的。现在Subscriber不再收到List<String>,而是收到一些列单个的字符串,就像Observable.from()的输出一样。

flatMap(),它可以返回任何它想返回的Observable对象。
接着前面的例子,现在我不想打印URL了,而是要打印收到的每个网站的标题。

现在的方法每次只能传入一个URL,并且返回值不是一个String,而是一个输出String的Observabl对象:

  private Observable<String> getTitle(final String url) {
    return Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("title: " + url);
        }
    });
}

实现:

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).flatMap(new Func1<String, Observable<?>>() {
        @Override
        public Observable<?> call(String s) {
            return getTitle(s);
        }
    }).subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
            L.d("myObservableUrls.flatMap onNext o: " + o);
        }
    });

filter()

filter()输出和输入相同的元素,并且会过滤掉那些不满足检查条件的

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).flatMap(new Func1<String, Observable<?>>() {
        @Override
        public Observable<?> call(String s) {
            return getTitle(s);
        }
    }).filter(new Func1<Object, Boolean>() {
        @Override
        public Boolean call(Object o) {
            return !o.equals("title: url2");
        }
    }).subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
            L.d("myObservableUrls.flatMap onNext o: " + o);
        }
    });

take()

take()输出最多指定数量的结果。

doOnNext()

doOnNext()允许我们在每次输出一个元素之前做一些额外的事情,比如这里的保存标题。

直接上网上的示例:

query("Hello, world!")  
.flatMap(urls -> Observable.from(urls))  
.flatMap(url -> getTitle(url))  
.filter(title -> title != null)  
.take(5)  
.doOnNext(title -> saveTitle(title))  
.subscribe(title -> System.out.println(title));  

三、响应式的好处

错误处理

每一个Observerable对象在终结的时候都会调用onCompleted()或者onError()方法,

这种模式有以下几个优点:

1.只要有异常发生onError()一定会被调用

这极大的简化了错误处理。只需要在一个地方处理错误即可以。

2.操作符不需要处理异常

将异常处理交给订阅者来做,Observerable的操作符调用链中一旦有一个抛出了异常,就会直接执行onError()方法。

3.你能够知道什么时候订阅者已经接收了全部的数据。

知道什么时候任务结束能够帮助简化代码的流程。(虽然有可能Observable对象永远不会结束)

使用RXJAVA,OBSERVABLE对象根本不需要知道如何处理错误!操作符也不需要处理错误状态-一旦发生错误,就会跳过当前和后续的操作符。所有的错误处理都交给订阅者来做。

调度器

使用RxJava,你可以使用subscribeOn()指定观察者代码运行的线程,使用observerOn()指定订阅者运行的线程:

myObservableServices.retrieveImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

订阅(Subscriptions)

调用Observable.subscribe(),会返回一个Subscription对象。这个对象代表了被观察者和订阅者之间的联系。

ubscription subscription = Observable.just("Hello, World!")
.subscribe(s -> System.out.println(s));

你可以在后面使用这个Subscription对象来操作被观察者和订阅者之间的联系.

subscription.unsubscribe();
System.out.println("Unsubscribed=" + subscription.isUnsubscribed());

RxJava的另外一个好处就是它处理unsubscribing的时候,会停止整个调用链。如果你使用了一串很复杂的操作符,调用unsubscribe将会在他当前执行的地方终止。不需要做任何额外的工作!

当然也不是所有的代码都使用响应式的方式,仅仅当代码复杂到我想将它分解成简单的逻辑的时候,我才使用响应式代码。

四、在Android中使用响应式编程

这部分主要是摘录,有些方法现在已过时,后续在实际应用中有用到再进行补充更新,

RxAndroid

首先,AndroidSchedulers提供了针对Android的线程系统的调度器。需要在UI线程中运行某些代码?很简单,只需要使用AndroidSchedulers.mainThread():

retrofitService.getImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

throttleFirst(): 在每次事件触发后的一定时间间隔内丢弃新的事件。常用作去抖动过滤,例如按钮的点击监听器:

RxView.clickEvents(button) // RxBinding 代码,后面的文章有解释
.throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms
.subscribe(subscriber);

遗留代码,运行极慢的代码

绝大多数时候Observable.just() 和 Observable.from() 能够帮助你从遗留代码中创建 Observable 对象:

private Object oldMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.just(oldMethod());
}

上面的例子中如果oldMethod()足够快是没有什么问题的,但是如果很慢呢?调用oldMethod()将会阻塞住他所在的线程。
为了解决这个问题,可以参考我一直使用的方法–使用defer()来包装缓慢的代码:

private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

现在,newMethod()的调用不会阻塞了,除非你订阅返回的observable对象。

生命周期

如何处理Activity的生命周期?主要就是两个问题:
1.在configuration改变(比如转屏)之后继续之前的Subscription。
比如你使用Retrofit发出了一个REST请求,接着想在listview中展示结果。如果在网络请求的时候用户旋转了屏幕怎么办?你当然想继续刚才的请求,但是怎么搞?

2.Observable持有Context导致的内存泄露
这个问题是因为创建subscription的时候,以某种方式持有了context的引用,尤其是当你和view交互的时候,这太容易发生!如果Observable没有及时结束,内存占用就会越来越大。

第一个问题的解决方案就是使用RxJava内置的缓存机制,这样你就可以对同一个Observable对象执行unsubscribe/resubscribe,却不用重复运行得到Observable的代码。cache() (或者 replay())会继续执行网络请求(甚至你调用了unsubscribe也不会停止)。这就是说你可以在Activity重新创建的时候从cache()的返回值中创建一个新的Observable对象。

Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));

// ...When the Activity is being recreated...
sub.unsubscribe();

// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

注意,两次sub是使用的同一个缓存的请求。当然在哪里去存储请求的结果还是要你自己来做,和所有其他的生命周期相关的解决方案一延虎,必须在生命周期外的某个地方存储。(retained fragment或者单例等等)。

第二个问题的解决方案就是在生命周期的某个时刻取消订阅。一个很常见的模式就是使用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的订阅。

private CompositeSubscription mCompositeSubscription
= new CompositeSubscription();

private void doSomething() {
    mCompositeSubscription.add(
        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
        .subscribe(s -> System.out.println(s)));
}

@Override
protected void onDestroy() {
    super.onDestroy();

    mCompositeSubscription.unsubscribe();
}

你可以在Activity/Fragment的基类里创建一个CompositeSubscription对象,在子类中使用它。

注意! 一旦你调用了 CompositeSubscription.unsubscribe(),这个CompositeSubscription对象就不可用了, 如果你还想使用CompositeSubscription,就必须在创建一个新的对象了。
最后,附上测试时使用的完整工程,代码上面均有贴出来了。
RxAndroid基本使用测试代码
http://download.csdn.net/detail/yaodong379/9486905

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容