App网络请求实战四:rxjava操作符flatMap使用以及rxjava源码解析

rxjava有很多强大的操作符,前面几篇我们其实已经介绍了map转换操作符的实战用法,今天来看看一个更牛逼的flatMap操作符。小老板,多捞哦。

上图

rxjava的map和flatmap rxjava flatmap源码_rxjava中flatMap

flatMap实战

其实我个人认为rxjava最牛逼的地方在于,它很轻易的解决了Android中比较棘手的一个问题,那就是线程切换。在Android的编码过程中,我们切换线程一般都是开启子线程,然后在子线程中调用runOnMain或者利用handler,或者说利用AsnycTask等(多说一句,其实最后底层都是利用的handler),真的是比较麻烦的。但是在rxjava中就很容易了,而且提供了逻辑清晰的链式编程范式。

在上述我们的需求中,我们需要把一张秀色可餐的美女图片保存到相册中。

这其中包含了三个操作:

  • 得到图片bitmap(子线程)
  • 将bitmap写入file文件中(子线程)
  • 写完后提示用户图片保存成功(主线程)

按照正常的方式虽然第一步我们用glide就可以轻易完成,但是后面两个步骤同样需要进行很麻烦的切换线程,我&*&%#@。不多说,懂得兄弟自然都懂。接下来用rxjava来实现,如下:

第一步:

Observable.create(new ObservableOnSubscribe<Bitmap>()
{
    @Override
    public void subscribe(ObservableEmitter<Bitmap> e) throws Exception
    {
        Bitmap bitmap = Glide.with(mContext).asBitmap().load(imgUrl).submit().get();
        if (bitmap == null)
        {
            e.onError(new Exception("无法下载图片"));
        }
        e.onNext(bitmap);
        e.onComplete();
    }
}).subscribeOn(Schedulers.io());

onNext发射的是一个bitmap,注意我们最后需要的是一个Uri from a file。所以我们需要转换

第二步:

.flatMap(new Function<Bitmap, ObservableSource<Uri>>()
{

    @Override
    public ObservableSource<Uri> apply(Bitmap bitmap) throws Exception
    {
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(),
                "http_exception");
        if (!file.exists())
        {
            file.mkdir();
        }
        String fileName = "meizi.jpg";
        imgFile = new File(file, fileName);

        FileOutputStream fileOutputStream = new FileOutputStream(imgFile);
        assert bitmap != null;
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
        fileOutputStream.flush();
        fileOutputStream.close();
        Uri uri = Uri.fromFile(imgFile);
        // 通知图库更新
        Intent scannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
        mContext.sendBroadcast(scannerIntent);
        //最后发射一个uri单项
        return Observable.just(uri);
    }
}).subscribeOn(Schedulers.io())//连起来调用后,此指定线程只需要指定一次

第三步:

.observeOn(AndroidSchedulers.mainThread())//切换到主线程,大胸弟,你说切换线程方不方便。
.subscribe(new Consumer<Uri>()
{
    @Override
    public void accept(Uri uri) throws Exception
    {
        String msg = String.format("图片已保存至 %s 文件夹", imgFile.getAbsoluteFile());
        Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
    }
}, new Consumer<Throwable>()
{
    @Override
    public void accept(Throwable throwable) throws Exception
    {
        Toast.makeText(mContext, throwable.getMessage(), Toast.LENGTH_SHORT).show();
    }
});

有没有懂的兄弟? rxjava的线程切换机制和链式编程范式是多么多么的强大。那有没有人想知道flatMap的转换流程内部是怎么执行的呢? 我以前用着的时候就觉得挺爽,用多之后就感觉一定要弄清楚代码在rxjava中是怎么走的,下面介绍一下flatMap的源码。

flatMap源码执行流程

rxjava的map和flatmap rxjava flatmap源码_操作符_02

开局一张图,过程全靠编!

看图之前记住这几个点:

  • 我把Observable叫被观察者,Observer叫做观察者
  • Observable.create是工厂方法,生产Observable被观察者;Observable.flatMap方法类似包装类,返回的还是Observable被观察者。

然后记住几个Observable中的源码方法:

create方法:

@SchedulerSupport(SchedulerSupport.NONE)
public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {
    ObjectHelper.requireNonNull(source, "source is null");
    return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
}

flatMap方法:

@SchedulerSupport(SchedulerSupport.NONE)
public final <R> Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper) {
    return flatMap(mapper, false);
}
//最后调用到
@SchedulerSupport(SchedulerSupport.NONE)
    public final <R> Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper,
            boolean delayErrors, int maxConcurrency, int bufferSize) {
        //省略大段代码,可以看到返回的是ObservableFlatMap对象
        return RxJavaPlugins.onAssembly(new ObservableFlatMap<T, R>(this, mapper, delayErrors, maxConcurrency, bufferSize));
    }

subscribe方法:

@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {
    return subscribe(onNext, onError, Functions.EMPTY_ACTION, Functions.emptyConsumer());
}
//一直往下调用,直到下面这个
@Override
public final void subscribe(Observer<? super T> observer) {
    ObjectHelper.requireNonNull(observer, "observer is null");
    try {
        //省略大段代码,可以认为调用subscribe方法最终都会调用被观察者Observable自己的
        //subscribeActual方法, 而这个方法是抽象的方法, 需要各个实现类去实现。
        subscribeActual(observer);
    } catch (NullPointerException e) { // NOPMD
        throw e;
    } catch (Throwable e) {

    }
}

后话

有时候吧,看一些大神博客能学到很多知识。但用多了之后,我总有一种不安全感,我很多时候在想这种不安全感是什么呢? 后来我想明白了,这种不安全感就是我只知道用它,而不知道为什么我们可以这么用。

Github:Demo