[RxJava学习]操作符flatMap源码分析

与上文的思路相同,先写出常见的调用方式,然后逐句代码替换;最后根据代码执行顺序,理出调用关系。

首先,借用文章:RxJava基本流程和lift源码分析中flatMap的使用例子。

1.map和flatMap的创建参数都是Func1,之前一直没有想明白为啥要用flatMap。这里根据现有的学习程度总结如下:

我们都习惯于处理入参是基本类型的数据源,当Func1>时,flatMap下游显然比map的下游更幸福,call(Observable fileObservable)里的函数实现该是怎样的一个处理啊

        ArrayList folders = new ArrayList<>();
        Observable.from(folders)
                .map(new Func1>() {
                    @Override
                    public Observable call(File file) {
                        return Observable.from(file.listFiles());
                    }
                })
                .subscribe(new Action1>() {
                    @Override
                    public void call(Observable fileObservable) {
                        
                    }
                });


        ArrayList folders = new ArrayList<>();
        Observable.from(folders)
                .flatMap(new Func1>() {
                    @Override
                    public Observable call(File file) {
                        return Observable.from(file.listFiles());
                    }
                })
                .subscribe(new Action1() {
                    @Override
                    public void call(File file) {
                        
                    }
                });
所以,当我们发现数据源的转换不能从A到B一步搞定时,我们就要考虑中间加一个过渡的Observable,如果A-> Observable->B可以满足目标,那使用flatMap再合适不过;

2.下面我们开始以上面的例子来分析源码,上面代码等价于:

Observable.from(onSubscribe1)  
        .flatMap(transformer1)
        .subscribe(subscriber1);

其中:

    public static  Observable from(Iterable iterable) {
        return create(new OnSubscribeFromIterable(iterable));
    }

所以这里的onSubscribe1为:

onSubscribe1 = OnSubscribeFromIterable (dataSource);

其中dataSource就是folders。

transformer1为:

transformer1 = new Func1>() {
                    @Override
                    public Observable call(File file) {
                        return Observable.from(file.listFiles());
                    }
                };


逐句替换的过程如下:

[RxJava学习]操作符flatMap源码分析_第1张图片
3.代码执行逻辑如下:

[RxJava学习]操作符flatMap源码分析_第2张图片

接上图,继续分析代码流:

[RxJava学习]操作符flatMap源码分析_第3张图片

从上述流程图可以看到,Iterable类型的数据源的每项数据,都会进行transformer1的处理,然后输出一个Observable类型的新数据,该新数据源的每项数据都将会一次发射给初始的订阅者subScriber1(准确点说应该是下游最临近的订阅者)。

4.附上相关源码:

//OnSubscribeFromIterable
    public void call(final Subscriber o) {
        final Iterator it;
        boolean b;
        
        try {
            it = is.iterator();
            
            b = it.hasNext();
        } catch (Throwable ex) {
            Exceptions.throwOrReport(ex, o);
            return;
        }
            
        if (!o.isUnsubscribed()) {
            if (!b) {
                o.onCompleted();
            } else { 
                o.setProducer(new IterableProducer(o, it));
            }
        }
    }

//IterableProducer
        public void request(long n) {
            if (get() == Long.MAX_VALUE) {
                // already started with fast-path
                return;
            }
            if (n == Long.MAX_VALUE && compareAndSet(0, Long.MAX_VALUE)) {
                fastpath(); // for(;;)
            } else 
            if (n > 0 && BackpressureUtils.getAndAddRequest(this, n) == 0L) {
                slowpath(n); // for(;;)
            }

        }

//MergeSubscriber
static final class MergeSubscriber extends Subscriber> {
        final Subscriber child; 
        MergeProducer producer;
        volatile Queue queue;
       
        public MergeSubscriber(Subscriber child, boolean delayErrors, int maxConcurrent) {
            this.child = child;
            this.delayErrors = delayErrors;
            this.maxConcurrent = maxConcurrent;
            this.nl = NotificationLite.instance();
            this.innerGuard = new Object();
            this.innerSubscribers = EMPTY;
            if (maxConcurrent == Integer.MAX_VALUE) {
                scalarEmissionLimit = Integer.MAX_VALUE;
                request(Long.MAX_VALUE);
            } else {
                scalarEmissionLimit = Math.max(1, maxConcurrent >> 1);
                request(maxConcurrent);
            }
        }
        
        @Override
        public void onNext(Observable t) {
            if (t == null) {
                return;
            }
            if (t == Observable.empty()) {
                emitEmpty();
            } else
            if (t instanceof ScalarSynchronousObservable) {
                tryEmit(((ScalarSynchronousObservable)t).get());
            } else {
                InnerSubscriber inner = new InnerSubscriber(this, uniqueId++);
                addInner(inner);
                t.unsafeSubscribe(inner);
                emit();
            }
        }
        
        @Override
        public void onError(Throwable e) {
            getOrCreateErrorQueue().offer(e);
            done = true;
            emit();
        }
        @Override
        public void onCompleted() {
            done = true;
            emit();
        }
        
		void emit() {
            synchronized (this) {
                if (emitting) {
                    missed = true;
                    return;
                }
                emitting = true;
            }
            emitLoop();
        }
		
        /**
         * Tries to emit the value directly to the child if
         * no concurrent emission is happening at the moment.
         * 

* Since the scalar-value queue optimization applies * to both the main source and the inner subscribers, * we handle things in a shared manner. * * @param subscriber * @param value */ void tryEmit(T value) { boolean success = false; long r = producer.get(); if (r != 0L) { synchronized (this) { // if nobody is emitting and child has available requests r = producer.get(); if (!emitting && r != 0L) { emitting = true; success = true; } } } if (success) { emitScalar(value, r); } else { queueScalar(value); } } protected void queueScalar(T value) { /* * If the attempt to make a fast-path emission failed * due to lack of requests or an ongoing emission, * enqueue the value and try the slow emission path. */ Queue q = this.queue; if (q == null) { int mc = maxConcurrent; if (mc == Integer.MAX_VALUE) { q = new SpscUnboundedAtomicArrayQueue(RxRingBuffer.SIZE); } else { if (Pow2.isPowerOfTwo(mc)) { if (UnsafeAccess.isUnsafeAvailable()) { q = new SpscArrayQueue(mc); } else { q = new SpscAtomicArrayQueue(mc); } } else { q = new SpscExactAtomicArrayQueue(mc); } } this.queue = q; } if (!q.offer(nl.next(value))) { unsubscribe(); onError(OnErrorThrowable.addValueAsLastCause(new MissingBackpressureException(), value)); return; } emit(); } protected void emitScalar(T value, long r) { boolean skipFinal = false; try { try { child.onNext(value); } catch (Throwable t) { if (!delayErrors) { Exceptions.throwIfFatal(t); skipFinal = true; this.unsubscribe(); this.onError(t); return; } getOrCreateErrorQueue().offer(t); } if (r != Long.MAX_VALUE) { producer.produced(1); } int produced = scalarEmissionCount + 1; if (produced == scalarEmissionLimit) { scalarEmissionCount = 0; this.requestMore(produced); } else { scalarEmissionCount = produced; } // check if some state changed while emitting synchronized (this) { skipFinal = true; if (!missed) { emitting = false; return; } missed = false; } } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } /* * In the synchronized block below request(1) we check * if there was a concurrent emission attempt and if there was, * we stay in emission mode and enter the emission loop * which will take care all the queued up state and * emission possibilities. */ emitLoop(); } /** * The standard emission loop serializing events and requests. */ void emitLoop() { boolean skipFinal = false; try { final Subscriber child = this.child; for (;;) { // eagerly check if child unsubscribed or we reached a terminal state. if (checkTerminate()) { skipFinal = true; return; } Queue svq = queue; long r = producer.get(); boolean unbounded = r == Long.MAX_VALUE; // count the number of 'completed' sources to replenish them in batches int replenishMain = 0; // try emitting as many scalars as possible if (svq != null) { for (;;) { int scalarEmission = 0; Object o = null; while (r > 0) { o = svq.poll(); // eagerly check if child unsubscribed or we reached a terminal state. if (checkTerminate()) { skipFinal = true; return; } if (o == null) { break; } T v = nl.getValue(o); // if child throws, report bounce it back immediately try { child.onNext(v); } catch (Throwable t) { if (!delayErrors) { Exceptions.throwIfFatal(t); skipFinal = true; unsubscribe(); child.onError(t); return; } getOrCreateErrorQueue().offer(t); } replenishMain++; scalarEmission++; r--; } if (scalarEmission > 0) { if (unbounded) { r = Long.MAX_VALUE; } else { r = producer.produced(scalarEmission); } } if (r == 0L || o == null) { break; } } } /* * We need to read done before innerSubscribers because innerSubscribers are added * before done is set to true. If it were the other way around, we could read an empty * innerSubscribers, get paused and then read a done flag but an async producer * might have added more subscribers between the two. */ boolean d = done; // re-read svq because it could have been created // asynchronously just before done was set to true. svq = queue; // read the current set of inner subscribers InnerSubscriber[] inner = innerSubscribers; int n = inner.length; // check if upstream is done, there are no scalar values // and no active inner subscriptions if (d && (svq == null || svq.isEmpty()) && n == 0) { Queue e = errors; if (e == null || e.isEmpty()) { child.onCompleted(); } else { reportError(); } skipFinal = true; return; } boolean innerCompleted = false; if (n > 0) { // let's continue the round-robin emission from last location long startId = lastId; int index = lastIndex; // in case there were changes in the array or the index // no longer points to the inner with the cached id if (n <= index || inner[index].id != startId) { if (n <= index) { index = 0; } // try locating the inner with the cached index int j = index; for (int i = 0; i < n; i++) { if (inner[j].id == startId) { break; } // wrap around in round-robin fashion j++; if (j == n) { j = 0; } } // if we found it again, j will point to it // otherwise, we continue with the replacement at j index = j; lastIndex = j; lastId = inner[j].id; } int j = index; // loop through all sources once to avoid delaying any new sources too much for (int i = 0; i < n; i++) { // eagerly check if child unsubscribed or we reached a terminal state. if (checkTerminate()) { skipFinal = true; return; } @SuppressWarnings("unchecked") InnerSubscriber is = (InnerSubscriber)inner[j]; Object o = null; for (;;) { int produced = 0; while (r > 0) { // eagerly check if child unsubscribed or we reached a terminal state. if (checkTerminate()) { skipFinal = true; return; } RxRingBuffer q = is.queue; if (q == null) { break; } o = q.poll(); if (o == null) { break; } T v = nl.getValue(o); // if child throws, report bounce it back immediately try { child.onNext(v); } catch (Throwable t) { skipFinal = true; Exceptions.throwIfFatal(t); try { child.onError(t); } finally { unsubscribe(); } return; } r--; produced++; } if (produced > 0) { if (!unbounded) { r = producer.produced(produced); } else { r = Long.MAX_VALUE; } is.requestMore(produced); } // if we run out of requests or queued values, break if (r == 0 || o == null) { break; } } boolean innerDone = is.done; RxRingBuffer innerQueue = is.queue; if (innerDone && (innerQueue == null || innerQueue.isEmpty())) { removeInner(is); if (checkTerminate()) { skipFinal = true; return; } replenishMain++; innerCompleted = true; } // if we run out of requests, don't try the other sources if (r == 0) { break; } // wrap around in round-robin fashion j++; if (j == n) { j = 0; } } // if we run out of requests or just completed a round, save the index and id lastIndex = j; lastId = inner[j].id; } if (replenishMain > 0) { request(replenishMain); } // if one or more inner completed, loop again to see if we can terminate the whole stream if (innerCompleted) { continue; } // in case there were updates to the state, we loop again synchronized (this) { if (!missed) { skipFinal = true; emitting = false; break; } missed = false; } } } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } } /** * Check if the operator reached some terminal state: child unsubscribed, * an error was reported and we don't delay errors. * @return true if the child unsubscribed or there are errors available and merge doesn't delay errors. */ boolean checkTerminate() { if (child.isUnsubscribed()) { return true; } Queue e = errors; if (!delayErrors && (e != null && !e.isEmpty())) { try { reportError(); } finally { unsubscribe(); } return true; } return false; } }
//MapSubscriber
    static final class MapSubscriber extends Subscriber {
        
        final Subscriber actual;
        
        final Func1 mapper;

        boolean done;
        
        public MapSubscriber(Subscriber actual, Func1 mapper) {
            this.actual = actual;
            this.mapper = mapper;
        }
        
        @Override
        public void onNext(T t) {
            R result;
            
            try {
                result = mapper.call(t);
            } catch (Throwable ex) {
                Exceptions.throwIfFatal(ex);
                unsubscribe();
                onError(OnErrorThrowable.addValueAsLastCause(ex, t));
                return;
            }
            
            actual.onNext(result);
        }
        
        @Override
        public void onError(Throwable e) {
            if (done) {
                RxJavaPluginUtils.handleException(e);
                return;
            }
            done = true;
            
            actual.onError(e);
        }
        
        
        @Override
        public void onCompleted() {
            if (done) {
                return;
            }
            actual.onCompleted();
        }
        
        @Override
        public void setProducer(Producer p) {
            actual.setProducer(p);
        }
    }

//OperatorMerge
    public Subscriber> call(final Subscriber child) {
        MergeSubscriber subscriber = new MergeSubscriber(child, delayErrors, maxConcurrent);
        MergeProducer producer = new MergeProducer(subscriber);
        subscriber.producer = producer;
        
        child.add(subscriber);
        child.setProducer(producer);
        
        return subscriber;
    }

//OnSubscribeLift
    public void call(Subscriber o) {
        try {
            Subscriber st = hook.onLift(operator).call(o);
            try {
                // new Subscriber created and being subscribed with so 'onStart' it
                st.onStart();
                parent.call(st);
            } catch (Throwable e) {
                // localized capture of errors rather than it skipping all operators 
                // and ending up in the try/catch of the subscribe method which then
                // prevents onErrorResumeNext and other similar approaches to error handling
                Exceptions.throwIfFatal(e);
                st.onError(e);
            }
        } catch (Throwable e) {
            Exceptions.throwIfFatal(e);
            // if the lift function failed all we can do is pass the error to the final Subscriber
            // as we don't have the operator available to us
            o.onError(e);
        }
    }



你可能感兴趣的:(RxJava)