RxJava2.0的简单使用Seminar(一)

关于RxJava2.0的文章,各大论坛上已经有很多大神写的很详细了。

作为一个开发新人,在大佬面前只有递茶的份的渣渣。对Rxjava原理只有肤浅的理解,所以本篇只是简单介绍和记录一下Rxjava的使用,一些定义解析都是挪用大神的文字,请见谅。

关于深入原理,同学可以找度娘观摩大神的文章。

-------------------------------------------------------------------------------------------------

1. RxJava到底是什么:

• RxJava GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。

• 其实RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的

 

2. RxJava 好在哪

• 同样是做异步,为什么人们用它,而不用现成的 AsyncTask / Handler / XXX / ...

• 异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTask Handler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁

• RxJava 的实现,是一条从上到下的链式调用,没有任何嵌套,这在逻辑的简洁性上是具有优势的。当需求变得复杂时,这种优势将更加明显

• 所以, RxJava 好在哪?就好在简洁,好在那把什么复杂逻辑都能穿成一条线的简洁

 

3. 概念:扩展的观察者模式

RxJava 的异步实现,是通过一种扩展的观察者模式来实现

观察者模式面向的需求是:A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 变化的一瞬间做出反应观察者不需要时刻盯着被观察者,而是采用注册(Register)或者称为订阅(Subscribe)的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度

RxJava 有四个基本概念:Observable (可观察者,即被观察者)Observer (观察者)subscribe (订阅)、事件

• Observable Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer

• 传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext() 之外,还定义了两个特殊的事件:onCompleted() onError()

• onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志

• onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出

• 在一个正确运行的事件序列中, onCompleted() onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted() onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个

 

4. 基本实现:

基于以上的概念, RxJava 的基本实现主要有三点

创建 Observer

  Observer 即观察者,它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式:

Observer<Integer> mObserver = new Observer<Integer>() {
            private Disposable mDisposable;
            private int index = 0;
            @Override
            public void onSubscribe(Disposable d) {
                //Disposable用于解除订阅关系
                mDisposable = d;
            }

            @Override
            public void onNext(Integer integer) {
                /* 对Observable传入的数据进行处理 
                *  在onNext执行mDisposable.dispose()来解除订阅关系
                *  解除后则不再接收Observable发射来的数据
                */
                index++;
                if (integer == 2) {
                    mDisposable.dispose();
                }
                Log.d(TAG, "OnNext Value -> " + index);
            }

            @Override
            public void onError(Throwable e) {
                //处理错误信息
            }

            @Override
            public void onComplete() {
                //进行Observable发射数据完成后的操作
            }
        };

• 创建 Observable

   Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。 RxJava 使用 create() 方法来创建一个 Observable ,并为它定义事件触发规则

Observable<Integer> mObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                Log.d(TAG, "Emitter Value -> " + 1);
                emitter.onNext(1);
                Log.d(TAG, "Emitter Value -> " + 2);
                emitter.onNext(2);
                Log.d(TAG, "Emitter Value -> " + 3);
                emitter.onNext(3);
                emitter.onComplete();
                Log.d(TAG, "Emitter Value -> " + 4);
                emitter.onNext(4);
                Log.d(TAG, "Emitter Value -> " + 5);
                emitter.onNext(5);
                Log.d(TAG, "Emitter Value -> " + 6);
                emitter.onNext(6);
            }
        });

Subscribe (订阅)

  创建Observable Observer 之后,用 subscribe() 方法将它们联结起来,整条链子就可以工作了

mObservable.subscribe(mObserver);

  运行一下程序,结果:

com.example.ram D/RxjavaTest: Emitter Value -> 1
com.example.ram D/RxjavaTest: OnNext Value -> 1
com.example.ram D/RxjavaTest: Emitter Value -> 2
com.example.ram D/RxjavaTest: OnNext Value -> 2
com.example.ram D/RxjavaTest: Emitter Value -> 3
com.example.ram D/RxjavaTest: Emitter Value -> 4
com.example.ram D/RxjavaTest: Emitter Value -> 5
com.example.ram D/RxjavaTest: Emitter Value -> 6

  对输出结果,可以看到在执行disposable.dispose()之后,Observer就没有再接收Observable的信息了。

  但仔细看的同学可以发现,在Observable发送emitter.onCompelte()之后,observerOnComplete()并没有调用。

  因也跟上面一样,解除订阅关系之后,Observable可以继续传递状态参数,但Observer不再执行对应操作。

 

5. Rxjava中的操作符

• 转换操作符:RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一。
                        所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

   Map :将原有Observable通过某种的关系转换成另一个Observable

        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                for (int i = 0; i < 3; i++) {
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }).map(new Function<Integer, String>() {
            @Override
            public String apply(Integer s) throws Exception {
                return s.toString() + "00";
            }
        }).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(String integer) {
                Log.e(TAG, integer);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {
                Log.e(TAG, "Complete");
            }
        });

  运行结果:Integer类型转换成String之后发射给Observer进行处理

com.example.ram E/RxjavaTest: 000
com.example.ram E/RxjavaTest: 100
com.example.ram E/RxjavaTest: 200
com.example.ram E/RxjavaTest: Complete

   flatMap:它可以把一个发射器 Observable 通过某种方法转换为多个 Observables,然后再把这些分散的 Observables装进一个单一的发射器 Observable。但需要注意的是,flatMap 并不能保证事件的顺序,如果需要保证,需要用到 ConcatMap

        for (int j = 0; j < 10; j++) {
            List<String> mStringList = new ArrayList<>();
            for (int i = j * 10; i < j * 10 + 10; i++) {
                mStringList.add(i % 10, "the number -> " + i);
            }
            mLists.add(mStringList);
        }

        Observable.fromIterable(mLists)
                .flatMap(new Function<List<String>, ObservableSource<String>>() {
                    @Override
                    public ObservableSource<String> apply(List<String> strings) throws Exception {
                        int delay = 0;
                        if (strings.get(2).contains("2")) {
                            delay = 500;
                        }
                        return Observable.fromIterable(strings).delay(delay, TimeUnit.MILLISECONDS);
                    }
                })
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String s) {
                        Log.e(TAG, s);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

  运行结果:

com.example.ram E/RxjavaTest: the number -> 10
com.example.ram E/RxjavaTest: the number -> 11
com.example.ram E/RxjavaTest: the number -> 20
com.example.ram E/RxjavaTest: the number -> 12
com.example.ram E/RxjavaTest: the number -> 13

   concatMap:可以做到不交错的发射两个甚至多个 Observable 的发射事件,并且只有前一个 Observable 终止(onComplete) 后才会订阅下一个 Observable与flatMap的原理相同,唯一区别就是concatMap以保证输出顺序,而flatMap不可以。

 

6. Map变换的原理:

  举个栗子:Map的源码分析

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {
        ObjectHelper.requireNonNull(mapper, "mapper is null");
        return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
    }

  可以看到map方法的参数Function类有两个泛型参数,第一个数转换前的T类型,第二个是转换后的R类型

  且函数需要返回的是一个R类型Observable

  数最终返回的是ObservableMap<T,R>类的对象,ObservableMap的部分源码:

   final Function<? super T, ? extends U> function;

    public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {
        super(source);
        this.function = function;
    }

    @Override
    public void subscribeActual(Observer<? super U> t) {
        source.subscribe(new MapObserver<T, U>(t, function));
    }

  subscribeActual 是个重要的钩子方法,在该方法传入一个U类型(也就是R类型)的Observer对象,

  在方法里调用subscribe方法,订阅一个MapObserver类的对象。

  MapObserver类中,通过apply方法(在代码中重写的方法)返回U类型参数,然后把R类型的数通过onNext接口传到ObserveronNext方法。以下是MapObserver的onNext方法源码:

 @Override
        public void onNext(T t) {
            if (done) {
                return;
            }

            if (sourceMode != NONE) {
                downstream.onNext(null);
                return;
            }

            U v;

            try {
                v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
            } catch (Throwable ex) {
                fail(ex);
                return;
            }
            downstream.onNext(v);
        }

  所以Map的内部转换原理:将原来T类型的Observable重新订阅一个R类型的Observer,把代码中返回的R值,通过OnNext传到R类型的Observer中。

 

7. 线程控制 -- Scheduler

   在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器

    在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 Scheduler ,它们已经适合大多数的使用场景

• Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。

• Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。

• Schedulers.computation()::计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU

• Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。

  代码演示:

        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                for (int i = 0; i < 5; i++) {
                    Log.e(TAG, "number -> " + i + ", Thread -> " + Thread.currentThread().getName());
                    emitter.onNext( String.valueOf(i) );
                }
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String s) {
                        Log.e(TAG, "Str ->" + s + ", Thread -> " + Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

    这段代码中,由于 subscribeOn(Schedulers.io()) 的指定,被创建的事件的内容 01234 将会在 IO 线程发出

    由于 observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 数字的打印将发生在主线程

    这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式很常见,适用于多数的 后台线程取数据,主线程显示的程序策略。

    运行结果:

com.example.ram E/RxjavaTest: number -> 0, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 1, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 2, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 3, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 4, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: Str ->0, Thread -> main
com.example.ram E/RxjavaTest: Str ->1, Thread -> main
com.example.ram E/RxjavaTest: Str ->2, Thread -> main
com.example.ram E/RxjavaTest: Str ->3, Thread -> main
com.example.ram E/RxjavaTest: Str ->4, Thread -> main

 

8. 今天总结了一下Observable的简单使用、Map/flatMap/concatMap等基本转换符、非常简单的介绍一下Scheduler

    所以贴下具体实际应用的代码,让同学们有更深的理解:

    功能描述:根据图库返回的多张图片url,复制生成一张缩略图片。对缩略图进行旋转角处理,完成后返回新图片的绝对路径

    代码:

List<Uri> uriList = data.getParcelableArrayListExtra("selectedItems");
Observable
.fromIterable(uriList)
.flatMap(new Function<Uri, ObservableSource<Uri>>() {
	@Override
	public ObservableSource<Uri> apply(Uri uri) throws Exception {
		return Observable.just(uri);
	}
})
.map(new Function<Uri, String>() {
	@Override
	public String apply(Uri uri) throws Exception {
        /*
        * 具体的骚操作可以围观文章“如何避免Image上传到服务器后发生转角度Rotation错误” ^ ^
        */
		String pathStr = FileUtil.getImageAttrFromUri(mContext, FileUtil.PATH, uri);
		String fileExtension = FileUtil.getFileExtension(pathStr);
		String newFilePath = appDirtPath + File.separator + System.currentTimeMillis() + "." + fileExtension;
		File mFile = BitmapUtil.correctImageRotateWithoutExif(pathStr, newFilePath, fileExtension, true);
	
		if (mFile != null && mFile.exists()) {
			appendFile(newFilePath);
			return newFilePath;
		} else {
			return pathStr;
		}
	}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
	private int time = 0;	
	@Override
	public void accept(String s) throws Exception {
		time++;
        //当返回filePath数据次数>=list的个数代表图片已经全部处理完
		if (time >= list.size()) {
		Toast.makeText(mContext, "图片加载完成", Toast.LENGTH_SHORT).show();
		}
	}
}, new Consumer<Throwable>() {
	@Override
	public void accept(Throwable throwable) throws Exception {
		Toast.makeText(mContext, "图片加载失败", Toast.LENGTH_SHORT).show();
		throwable.printStackTrace();
	}
});

    步骤分析:

    • fromIterable()创建一个Observable,并接收数据UriList

    • 通过flatMap将UriList中每个uri通过Observable.just()发送到Observer

    • map将Uri经特定函数转换成String类型数据

    • 因为map进行数据转换耗时较多,所以通过subscribeOn()把当前线程切换为io()线程。

    • 接收到处理后的数据,需要在UI上显示信息。所以通过observerOn()把之前切换io线程再切换为Main线程(UI线程)

    • 最后根据返回的最终数据,在subscribe()的Consumer中进行处理。

注意:1. flatMap会对传入的listObservable建多个list项的Observable,需要注意的是:当list的子项还需分解子项时,return的是Observable.fromIterable()

           2.此时returnObservable并不会马上发送给subscribe()处 理,而是把子项的Observable入到同一Observable,这个Observable再把事件统一发送给subscribe()处理。

----------------------------------------------------------------------------------------------------------------------------------------------------------------

            记录一下RxJava2.0的使用方法,加深自己的理解。

            文中若有错误地方,欢迎各位指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值