-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
51 lines (51 loc) · 174 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[RxJava2 源码解析:Observable 系列]]></title>
<url>%2F2017%2F05%2F17%2FRxJava2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%B8%80%EF%BC%89%2F</url>
<content type="text"><![CDATA[自 RxJava 在2016年11月12日正式发布2.0.1版本以来也有一段事件了,其内部实现也逐渐趋于稳定,截止到笔者开始这篇文章时已更新到2.1.0。 Version 2.1.0 is the next minor release of the 2.x era and contains the standardization of many experimental API additions from the past half a year since version 2.0.0. Therefore, the following components are now considered stable and will be supported throughout the rest of the life of RxJava 2.x. 从版本信息来看应该是比较稳定的版本,所以这篇文章将会使用 2.1.0 版本的代码进行分析。 RxJava2.0 是遵循 Reactive Streams Specification 的规范完成的,跟 RxJava1.x 相比,RxJava2 的变化还是很大的,最明显的就是接口的变化和对背压支持的完善,更详细的区别可以阅读官方文档 What’s-different-in-2.0。 由于 RxJava1.x 中背压的支持是在后面加入的,对背压的支持不是很完善,并不是所有的操作符都能正确响应下游的 request 请求。而且背压的实现机制上,RxJava1.x 采用的是中游阻塞的方式,RxJava2 中则不存在中游阻塞的过程,完全由下游自己决定去多少。因此 RxJava2 将背压的支持从原来的 Observable 中抽取出来,RxJava2 中的 Observable 不再支持背压,而 Flowable 支持非阻塞式的背压,并且 Flowable 中所有操作符强制支持背压。 因此对于 RxJava2 的源码分析将分为两篇,这一篇主要分析 Observable 及相关操作符。下一篇主要分析 Flowable 及相关操作符还有背压的实现原理。 我们还是从最简单的使用场景为例子来分析 12345678910111213141516171819202122232425262728293031323334Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception { observableEmitter.onNext(1); observableEmitter.onComplete(); } }).map(new Function<Integer, String>() { @Override public String apply(@NonNull Integer integer) throws Exception { return integer.toString(); } }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<String>() { @Override public void onSubscribe(Disposable disposable) { Log.d(TAG, "onSubscribe"); } @Override public void onNext(String s) { Log.d(TAG, "" + s); } @Override public void onError(Throwable throwable) { Log.d(TAG, "onError"); } @Override public void onComplete() { Log.d(TAG, "onComplete"); } }); 这个例子非常简单,但是已经涵盖了 Observable 和 Observer 的创建、数据的转换(map)、线程调度(subscribeOn/observeOn) 这些常见的场景。 在这个过程中应该关注以下几点: Observable 怎么发送数据到 Observer 怎么对数据流进行变换 线程的调度 那么下面就开始进行分析。 create1234public static <T> Observable<T> create(ObservableOnSubscribe<T> source) { ObjectHelper.requireNonNull(source, "source is null"); return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));} Observable 的 create() 方法接收一个 ObservableOnSubscribe 参数,也就是例子中创建的匿名类。ObservableOnSubscribe 接口中只有一个 subscribe() 方法,该方法传进来一个 ObservableEmitter 实例,使用onNext()、onError() 和 onComplete() 就能向 Obsever 发送数据了,其实这些方法最终会调用 Observer 的相应方法。 回到 create() 方法,程序用 ObservableOnSubscribe 实例来创建了一个 ObservableCreate 对象,然后作为参数来调用 RxJavaPlugins.onAssembly() 方法。 onAssembly() 方法里面调用了相关的 hook 方法,在正常流程中可以当作这个方法什么也没做,只是简单的返回传入的对象。后面见到的 RxJavaPlugins 的相关方法也是如此。 那么主要的逻辑就是在 ObservableCreate 对象的创建上了 1234567public final class ObservableCreate<T> extends Observable<T> { final ObservableOnSubscribe<T> source; public ObservableCreate(ObservableOnSubscribe<T> source) { this.source = source; }} ObservableCreate 的构造函数非常简单,只是简单保存了我们创建的 ObservableOnSubscribe 对象。这里是适配器模式的体现,用 ObservableCreate 将 ObservableOnSubscribe 适配成 Observable,以方便后面的链式调用。 creat() 方法的流程就分析到这里,目前我们得到了一个 ObservableCreate 对象,里面封装了我们创建的 ObservableOnSubscribe 实例。里面的一些逻辑要等到它被订阅之后才会调用。 mapObservableCreate 继承了 Observable,所以这里的 map() 方法调用的就是父类 Observable 的 map() 方法。 1234public 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() 方法的作用是对于 Observable 发送的每一个数据项,都是用一个用户指定的方法将数据项转型,是 Observable 发送的是转型后的数据项。 所以 map() 方法接收一个 Function 对象作为参数,用于执行其 apply() 方法进行数据项转型,在例子中我们只是简单地将整型转成字符串。 回到 map() 方法本身,其实就是用 ObservableCreate 实例和 Function 对象作为参数来创建一个 ObservableMap 然后返回,依然是 Observable 类型。我们来看一下它的构造方法 12345678910111213141516171819202122public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> { final Function<? super T, ? extends U> function; public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) { super(source); this.function = function; }}///具有可消耗事件源源的操作符的基类,也是一个Observableabstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> { AbstractObservableWithUpstream(ObservableSource<T> source) { this.source = source; } @Override public final ObservableSource<T> source() { return source; }} 其实就是简单的赋值操作。ObservableMap 继承了 AbstractObservableWithUpstream ,表示该实例也是一个 Observable,但是它本身并不产生数据,由它的上游即内部保存的 source Observable 来发射数据。 到现在为止还没有什么实质逻辑,因为订阅还没有开始。目前我们得到了一个 ObservableMap 对象,里面封装了 ObservableCreate 实例和 Function 对象。 接下来就是线程调度了。 subscribeOn1234public final Observable<T> subscribeOn(Scheduler scheduler) { ObjectHelper.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));} subscribeOn 操作符用于指定上游执行的线程,通过一个 Scheduler 实例来指定。在例子中我们指定的是 IO 线程,那么该实例就是 IoScheduler。 Schedulers 类中已经预定义了一系列 Schedulers 供我们选择,满足了大部分的使用场景 123456789101112131415161718public final class Schedulers { static final class SingleHolder { static final Scheduler DEFAULT = new SingleScheduler(); } static final class ComputationHolder { static final Scheduler DEFAULT = new ComputationScheduler(); } static final class IoHolder { static final Scheduler DEFAULT = new IoScheduler(); } static final class NewThreadHolder { static final Scheduler DEFAULT = new NewThreadScheduler(); }} 回到 subscribeOn() 方法,方法内部使用 ObservableMap 和 Scheduler 实例作为构造参数来创建了一个 ObservableSubscribeOn 然后返回。那么我们来看看它的构造方法 123456789public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> { final Scheduler scheduler; public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) { ///保存ObservableMap super(source); this.scheduler = scheduler; }} 跟 ObservableMap 的构造方法还是一样的套路,简单的属性赋值,在 ObservableMap 上再封装一层 ObservableSubscribeOn。 可以预见 observeOn 操作符也是一样的逻辑。 observeOn12345678910public final Observable<T> observeOn(Scheduler scheduler) { ///默认errordelay=false,bufferSize=128 return observeOn(scheduler, false, bufferSize());}public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) { ObjectHelper.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));} observeOn() 方法也是接收一个 Scheduler 对象作为参数,这里具体是 HandlerScheduler 对象。方法内部调用了另一个重载方法,指定 errordelay=false 和 bufferSize=128,errordelay 表示当发生异常时是否马上结束事件流,bufferSize 表示缓存队列的大小。 重载方法中将一系列值作为参数用来创建一个 ObservableObserveOn 对象然后返回。它的构造方法 1234567891011public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> { final Scheduler scheduler; final boolean delayError; final int bufferSize; public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) { super(source); this.scheduler = scheduler; this.delayError = delayError; this.bufferSize = bufferSize; }} 保存了 ObservableSubscribeOn 实例和一些参数。 现在我们得到了一个 ObservableObserveOn 对象,Observable 类型,但是它是由 ObservableOnSubscribe -> ObservableCreate -> ObservableMap -> ObservableSubscribeOn -> ObservableObserveOn 这样层层封装而来,跟调用的操作符顺序对应。目前它已经涵盖了数据发射、数据处理、线程调度这些流程了,只要调用 subscribe 就能触发这些流程运转起来。 subscribe123456789101112131415161718192021public final void subscribe(Observer<? super T> observer) { ObjectHelper.requireNonNull(observer, "observer is null"); try { observer = RxJavaPlugins.onSubscribe(this, observer); ObjectHelper.requireNonNull(observer, "Plugin returned null Observer"); subscribeActual(observer); } catch (NullPointerException e) { // NOPMD throw e; } catch (Throwable e) { Exceptions.throwIfFatal(e); // can't call onError because no way to know if a Disposable has been set or not // can't call onSubscribe because the call might have set a Subscription already RxJavaPlugins.onError(e); NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS"); npe.initCause(e); throw npe; }} subscribe() 方法接收一个 Observer 对象作为参数,Observer 接口包含4个方法,用来响应不同的事件 123456public interface Observer<T> { void onSubscribe(@NonNull Disposable d); void onNext(@NonNull T t); void onError(@NonNull Throwable e); void onComplete();} 当一个 Observer 订阅了 Observable 之后,首先被调用的是 onSubscribe() 方法,附带一个 Disposable 对象,可以结束事件序列。然后 onNext() 被调用若干次表示有数据项发送过来,最后以 onError() 或 onComplete() 方法结束。 回到 subscribe() 方法,这段代码的重点只有一行,就是调用了 subscribeActual(observer) 方法。subscribeActual() 方法在 Observable 是抽象方法,有具体的子类实现,subscribeActual() 方法的实现体现了操作符作用。在这里调用的自然就是我们最后得到的 ObservableObserveOn 对象的 subscribeActual() 方法了。 123456789101112131415public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> { @Override protected void subscribeActual(Observer<? super T> observer) { if (scheduler instanceof TrampolineScheduler) { source.subscribe(observer); } else { ///这里scheduler实际上是HandlerScheduler对象,返回HandlerWorker Scheduler.Worker w = scheduler.createWorker(); ///订阅上游数据源,这里具体是ObservableSubscribeOn source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize)); } }} 这里程序进入的是第二个分支。首先创建了一个 Worker 对象,将来负责线程的切换。然后创建了一个 ObserveOnObserver 对象,由它来订阅上游的数据源,这里具体是 ObservableSubscribeOn。 那么这样就会调用 ObservableSubscribeOn 的父类 Observable 的 subscribe() 方法,subscribe() 方法的逻辑我们已经见过,方法内会调用具体子类也就是 ObservableSubscribeOn 的 subscribeActual() 方法。 12345678910111213public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> { @Override public void subscribeActual(final Observer<? super T> s) { ///创建一个包装类包装了ObserveOnObserver final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s); ///调用下游ObserveOnObserver的onSubscribe()方法,所以onSubscribe()方法执行在订阅处所在的线程 s.onSubscribe(parent); ///setDisposable()是为了将子线程的操作加入Disposable管理中。scheduler实际是IoScheduler parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent))); } 这里创建一个包装类 SubscribeOnObserver 来包装刚刚传进来的 ObserveOnObserver。然后将其作为 Disposable 类型调用了下游也就是 SubscribeOnObserver 的onSubscribe() 方法,那我们回头去看一下 1234567891011121314151617public void onSubscribe(Disposable s) { ///校验this.s为空,s不为空 if (DisposableHelper.validate(this.s, s)) { ///保存Disposable this.s = s; ///SubscribeOnObserver 不是QueueDisposable类型 if (s instanceof QueueDisposable) { ...... } ///创建一个queue 用于保存上游 onNext() push的数据 128 queue = new SpscLinkedArrayQueue<T>(bufferSize); ///actual就是我们自己创建的Observer回调下游观察者onSubscribe方法。 actual.onSubscribe(this); } } 这里的关键是调用了 actual 实例的 onSubscribe() 方法,这里的 actual 实际上就是我们自己创建的 Observer 对象,Observer 收到的第一个回调 onSubscribe() 方法就是在这里调用的,其中 Disposable 参数就是 ObserveOnObserver 对象。其实 onSunscribe() 回调中的 Disposable 参数都是上游包装 Observable 内部的包装 Observer。 由于这里还没切换线程,所以我们创建的 Observer 的 onSubscribe() 回调是在订阅处所在线程执行的。 然后回到 ObservableSubscribeOn 的 subscribeActual() 方法,后续的代码先创建了 SubscribeTask 对象,然后调用 IoScheduler 对象的 scheduleDirect() 方法进行线程切换。 先来看看 SubscribeTask 是个什么东西 12345678910111213final class SubscribeTask implements Runnable { private final SubscribeOnObserver<T> parent; SubscribeTask(SubscribeOnObserver<T> parent) { this.parent = parent; } @Override public void run() { ///用SubscribeOnObserver订阅了上游ObservableMap,将运行在相应的Scheduler线程中 source.subscribe(parent); }} 其实就是一个 Runnable,会执行订阅上游 Observable 的操作,将会在 IoScheduler 的线程中执行。 下面就是线程调度的关键了,看看 IoScheduler 的 scheduleDirect() 方法是怎样切换线程的。 1234567891011121314151617181920public abstract class Scheduler { public Disposable scheduleDirect(@NonNull Runnable run) { return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS); } public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { ///调用具体IoScheduler实例的createWorker方法,返回EventLoopWorker final Worker w = createWorker(); ///直接返回run final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); ///在原始runnable上添加dispose流程,执行完毕后调用Worker的dispose方法 DisposeTask task = new DisposeTask(decoratedRun, w); w.schedule(task, delay, unit); return task; }} 这里调用了另一个重载的 scheduleDirect() 方法,方法中首先调用 IoScheduler 实例的 createWorker() 创建了 EventLoopWorker 对象。然后调用它的 seheduler() 方法进行实际的线程调度 1234567891011static final class EventLoopWorker extends Scheduler.Worker { public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) { if (tasks.isDisposed()) { // don't schedule, we are unsubscribed return EmptyDisposable.INSTANCE; } ///调用NewThreadWorker的scheduleActual,threadWorker从CachedWorkerPool中获取 return threadWorker.scheduleActual(action, delayTime, unit, tasks); }} threadWorker 是一个 ThreadWorker 实例,这里调用的 scheduleActual() 方法是其父类 NewThreadWorker 的方法。 12345678910111213141516171819202122232425262728293031public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) { Runnable decoratedRun = RxJavaPlugins.onSchedule(run); ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent); if (parent != null) { if (!parent.add(sr)) { return sr; } } Future<?> f; try { if (delayTime <= 0) { ///executor是ScheduledExecutorService线程池对象 // 在构造函数中初始化,核心线程数1,所以订阅发生在新线程 f = executor.submit((Callable<Object>)sr); } else { f = executor.schedule((Callable<Object>)sr, delayTime, unit); } ///f是Future对象,包含结果 sr.setFuture(f); } catch (RejectedExecutionException ex) { if (parent != null) { parent.remove(sr); } RxJavaPlugins.onError(ex); } return sr;} 这里的关键就是线程池的相关代码,这里调用了 executor 的 submit() 方法,executor 是一个 ScheduledExecutorService 线程池对象,核心线程数为1,线程的创建由 ThreadFactory 子类 RxThreadFactory 来完成 1234567891011public Thread newThread(Runnable r) { StringBuilder nameBuilder = new StringBuilder(prefix).append('-').append(incrementAndGet()); String name = nameBuilder.toString(); ///nonBlocking默认为false Thread t = nonBlocking ? new RxCustomThread(r, name) : new Thread(r, name); ///prioryty为5 t.setPriority(priority); t.setDaemon(true); return t; } 可见我们指定的 IO 线程是一个后台线程。线程优先级的计算如下 12int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, Integer.getInteger(KEY_IO_PRIORITY, Thread.NORM_PRIORITY))); 具体来说就是5. 回到 NewThreadWorker 的 scheduleActual() 方法,里面调用了线程池的 submit() 方法,最终会在新创建的线程中执行之前的 SubscribeTask ,导致 SubscribeOnObserver 在新线程中订阅了上游的 ObservableMap。 所以上游的全部操作包括数据的发射,数据处理都在该线程中执行。 既然上游的 ObservableMap 被订阅了,那么自然会调用它的 scheduleActual() 方法 123456789public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> { @Override public void subscribeActual(Observer<? super U> t) { ///用MapObserver订阅上游ObservableCreate。 source.subscribe(new MapObserver<T, U>(t, function)); }} 只是简单地订阅了上游 Observable,而这次的 Observable 终于是事件流的源头 ObservableCreate了,意味着即将要开始发送事件。它的 subscribeActual() 方法如下 1234567891011121314protected void subscribeActual(Observer<? super T> observer) { ///创建一个能向下游observer发送事件并能自由取消的对象 CreateEmitter<T> parent = new CreateEmitter<T>(observer); ///调用MapObserver的onSubscribe方法 observer.onSubscribe(parent); try { ///调用了我们创建的ObservableOnSubscribe对象的 subscribe 方法 source.subscribe(parent); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); parent.onError(ex); }} 程序首先调用了下游 MapObserver 的 onSubscribe() 方法,那么 MapObserver.onSubscribe() -> SubscribeOnObserver.onSubscribe() 都会被调用,内部逻辑主要是将 Disposable 对象保存起来,在此不赘述了。 然后我们看到一句关键的代码 source.subscribe(parent),调用我们一开始创建的 ObservableOnSubscribe 对象的 subscribe() 方法,数据的发送就是在这里被执行的。那么就会使用 ObservableEmitter 对象的 onNext() 方法发送若干数据,以 onComplete() 结束,来完成数据的发送,并且是在 onSubscribe 操作符指定的线程。而 ObservableOnSubscribe 的 subscribe() 方法接收的 ObservableEmitter 参数实际上就是 ObservableCreate 对象里面的一个内部类 CreateEmitter。可以猜测到它一定是将数据转发到下游 MapObserver 的,那么我们看一下它的 onNext() 方法 12345678910111213static final class CreateEmitter<T> extends AtomicReference<Disposable>implements ObservableEmitter<T>, Disposable { @Override public void onNext(T t) { if (t == null) { onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources.")); return; } if (!isDisposed()) { observer.onNext(t); } } 不出所料 ObservableEmitter 没有做什么处理,调用 onNext() 方法发送给下游 MapObserver 12345678910111213141516171819202122232425262728static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> { @Override public void onNext(T t) { ///onError或者onComplete之后done为true if (done) { return; } ///默认sourceMode是NONE if (sourceMode != NONE) { actual.onNext(null); return; } ///下游Observer接受的类型 U v; ///这一步执行变换,将上游传过来的T类型,利用Function转换成下游需要的U类型 try { v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; } actual.onNext(v); }} MapObserver 的 onNext() 方法主要做两件事,数据转型和发送至下游。转型实际上是通过调用用户指定的 Function 对象的 apply() 方法完成的。然后调用下游的 onNext() 方法,也即是 SubscribeOnObserver 12345678static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable { ///直接调用下游观察者的对应方法 @Override public void onNext(T t) { actual.onNext(t); } } 那么我们来看一下下游 ObserveOnObserver 的 onNext() 方法,这也是线程切换的关键点之一 1234567891011121314151617181920212223242526272829303132public void onNext(T t) { ///执行过error / complete 会是true if (done) { return; } ///如果数据源类型不是异步的, 默认不是 if (sourceMode != QueueDisposable.ASYNC) { ///上游push过来的数据放入队列中 queue.offer(t); } ///开始进入对应Workder线程,在线程里将队列中的数据去除发送给下游Observer schedule(); } void schedule() { if (getAndIncrement() == 0) { ///worker是HandlerWorker,会执行该类的run方法,在MainLooper的Handler中执行 worker.schedule(this); } } @Override public void run() { //默认是false if (outputFused) { drainFused(); } else { //取出queue里的数据 发送 drainNormal(); } } ObserveOnObserver 并没有将数据直接发送给下游,而是先放入一个队列中,该队列的初始大小是128.然后再进行线程切换,在新线程中将数据取出再发送给下游。 schedule() 方法中调用了 Worker 对象的 sehedule() 方法,由于我们指定的是主线程,那么 Worker 对象的实例就是 HandlerWorker,其 schedule() 方法如下 12345678910111213141516171819202122232425public Disposable schedule(Runnable run, long delay, TimeUnit unit) { if (run == null) throw new NullPointerException("run == null"); if (unit == null) throw new NullPointerException("unit == null"); if (disposed) { return Disposables.disposed(); } run = RxJavaPlugins.onSchedule(run); ScheduledRunnable scheduled = new ScheduledRunnable(handler, run); Message message = Message.obtain(handler, scheduled); message.obj = this; // Used as token for batch disposal of this worker's runnables. handler.sendMessageDelayed(message, Math.max(0L, unit.toMillis(delay))); // Re-check disposed state for removing in case we were racing a call to dispose(). if (disposed) { handler.removeCallbacks(scheduled); return Disposables.disposed(); } return scheduled; } 关键的地方是 handler.sendMessageDelayed(),这里的 handler 实例是用 MainLooper 来构造的,所以传进去的 Runnable 将会在主线程中执行,也就是 ObserveOnObserver 中的 run() 方法将在主线程中执行。 run() 方法中又调用了 drainNormal() 方法,想必就是从队列中去除数据然后发送给下游了 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647void drainNormal() { int missed = 1; final SimpleQueue<T> q = queue; final Observer<? super T> a = actual; for (;;) { ///检查是否结束,是否没有数据要发送 if (checkTerminated(done, q.isEmpty(), a)) { return; } for (;;) { boolean d = done; T v; try { ///从队列中取出数据 v = q.poll(); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); s.dispose(); q.clear(); a.onError(ex); worker.dispose(); return; } boolean empty = v == null; if (checkTerminated(d, empty, a)) { return; } if (empty) { break; } ///发送给下游 a.onNext(v); } missed = addAndGet(-missed); if (missed == 0) { break; } } } 跟我们预料的一样,这里的 a 就是下游 Observer,也就是事件流最终点,那么我们的 Observer 就成功收到数据了。 将上面的流程进行简化可以用下面这样一个图表示,其中黑色表示订阅时所在线程,蓝色表示IO线程,红色表示主线程。]]></content>
<categories>
<category>源码分析</category>
</categories>
<tags>
<tag>源码分析</tag>
<tag>RxJava2</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Glide源码解析(二):缓存机制]]></title>
<url>%2F2017%2F05%2F08%2FGlide%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6%2F</url>
<content type="text"><![CDATA[本文是 Glide 源码分析系列的第二篇,主要通过分析源码总结 Glide 的缓存机制。 从加载流程揭开缓存机制的面纱首先回忆一下上一篇关于 Glide 加载流程源码分析的内容,我们从 Glide.with().load().into() 这个最简单最基本的用法入手,一步步深入源码,梳理出了完整的图片加载流程。由于当时分析重点在于整体流程的把握上,所以对于缓存相关的部分都是简单带过而没有进行深入分析。首先是为了避免文章篇幅过长,其次因为缓存它不是独立的部分,它埋藏在整个加载流程的各个环节中,所以对缓存机制的理解应该建立在对整体流程清晰的把握上。 而在本文中,我们将从整个加载流程入手,找出缓存相关的部分,进而还原出 Glide 缓存机制的整体面貌。然后再对各个部分进行详细分析。 那么我们开始来看看加载流程中被我们错过的缓存操作。 读取内存缓存我们在 GenericRequestBuilder 中构建了一个 GenericRequest 实例并交给 RequestTracker 去处理,执行它的 begin() 方法。在 GenericRequest 的 确定了 ImageView 的大小之后在 onSizeReady() 回调中调用了 Engine 的load() 进行加载。这时我们第一次遇到了缓存操作 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, EngineResource.ResourceListener { public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { ...... final String id = fetcher.getId(); EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder()); /// 从LruResourceCache中获取 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { ///GenericRequest cb.onResourceReady(cached); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } /// 从activeResources中获取 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { ///GenericRequest cb.onResourceReady(active); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } ...... } private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> active = null; WeakReference<EngineResource<?>> activeRef = activeResources.get(key); if (activeRef != null) { active = activeRef.get(); if (active != null) { active.acquire(); } else { activeResources.remove(key); } } return active; } ///从LruResourceCache中获取,若有则移除并放入activesource private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null) { cached.acquire(); activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue())); } return cached; } @SuppressWarnings("unchecked") private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key); final EngineResource result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource) cached; } else { result = new EngineResource(cached, true /*isCacheable*/); } return result; }} 程序首先调用 loadFromCache() 尝试从 MemoryCache 中获取,如果命中缓存则将缓存从 MemoryCache 中移除并放入 activeResources,然后返回。如果缓存失效则尝试从 activeResources 中获取,如果都失效再构建 EngineRunnable 从磁盘或者网络获取。 是否决定从 MemoryCache 和 activeResources 中获取的前提条件是 isMemoryCacheable 为 true。这个值从 GenericRequestBuilder 传过来,默认为true,也就是说 Glide 默认开启内存缓存。除非你主动调用了 skipMemoryCache() 使该加载请求跳过内存缓存。该方法就是通过将 isMemoryCacheable 置为 false 实现的。 这里出现了 MemoryCache 和 activeResources,它们一起构成的 Glide 中的内存缓存。它们的 Key,都是由 url、图片大小、decoder、encoder等变量组成。MemoryCache 用于保存最近使用过而当前不在使用的 EngineResource,这也是缓存命中是需要将缓存移除并添加到 activeResources 的原因。其内部使用 LinkedHashMap,当大小达到一个阈值时通过 LRU 算法来清除。在 Glide 中 MemoryCache 的默认实现是 LruResourceCache,在 GlideBuilder 中被初始化。activeResources 用于保存当前正在被使用的 EngineResource,是一个使用 Key 作为键, EngineResource 的弱引用为值的 HashMap。 读取磁盘缓存当 MemoryCache 和 activeResources 都失效时,程序才构建一个 EngineRunnable 并交给线程池执行。在 EngineRunnable 的 run() 方法中就调用了 decode() 尝试从磁盘或网络获取图片,这里,我们第二次遇到了缓存操作 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465class EngineRunnable implements Runnable, Prioritized { @Override public void run() { if (isCancelled) { return; } Exception exception = null; Resource<?> resource = null; try { ///得到了Resource<GlideDrawable>对象 resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } if (isCancelled) { if (resource != null) { resource.recycle(); } return; } if (resource == null) { /// 第一次走decode()尝试从磁盘获取,失败后会走这里 onLoadFailed(exception); } else { onLoadComplete(resource); } } private boolean isDecodingFromCache() { return stage == Stage.CACHE; } private Resource<?> decode() throws Exception { if (isDecodingFromCache()) { ///从磁盘缓存中decode图片,第一次会走这 return decodeFromCache(); } else { ///从源中decode图片,第二次会走这 return decodeFromSource(); } } private Resource<?> decodeFromCache() throws Exception { Resource<?> result = null; try { result = decodeJob.decodeResultFromCache(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Exception decoding result from cache: " + e); } } if (result == null) { result = decodeJob.decodeSourceFromCache(); } return result; }} 在 decode() 方法中,决定从源获取图片之前,先调用 decodeFromCache() 方法尝试从磁盘中获取图片。而 decodeFromCache() 方法中又先后调用 decodeResultFromCache() 获取处理图 和 decodeSourceFromCache() 获取原图。 那么我们去看看这两个方法 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455class DecodeJob<A, T, Z> { ///从磁盘decode转化过的resource,然后transcode public Resource<Z> decodeResultFromCache() throws Exception { if (!diskCacheStrategy.cacheResult()) { return null; } long startTime = LogTime.getLogTime(); ///Resource<GifBitmapWrapper> Resource<T> transformed = loadFromCache(resultKey); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Decoded transformed from cache", startTime); } startTime = LogTime.getLogTime(); ///Resource<GlideDrawable> Resource<Z> result = transcode(transformed); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transcoded transformed from cache", startTime); } return result; } public Resource<Z> decodeSourceFromCache() throws Exception { if (!diskCacheStrategy.cacheSource()) { return null; } long startTime = LogTime.getLogTime(); ///Resource<GifBitmapWrapper> 使用的是OriginalKey Resource<T> decoded = loadFromCache(resultKey.getOriginalKey()); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Decoded source from cache", startTime); } return transformEncodeAndTranscode(decoded); } private Resource<T> loadFromCache(Key key) throws IOException { File cacheFile = diskCacheProvider.getDiskCache().get(key); if (cacheFile == null) { return null; } Resource<T> result = null; try { ///FileToStreamDecoder,将file解码成Resource<GifBitmapWrapper>实例是GifBitmapWrapperResource result = loadProvider.getCacheDecoder().decode(cacheFile, width, height); } finally { if (result == null) { diskCacheProvider.getDiskCache().delete(key); } } return result; }} decodeResultFromCache() 方法和 decodeSourceFromCache() 方法内部都调用了 loadFromCache() 方法来获取磁盘缓存,只不过前者使用的参数是 resultKey 而后者使用的是 OriginalKey。这里的 loadProvider 是一个 LazyDiskCacheProvider 对象,里面封装了一个 InternalCacheDiskCacheFactory,当调用 getCacheDecoder() 时,方法内部将调用 InternalCacheDiskCacheFactory 的 build() 方法,最终返回一个 DiskLruCacheWrapper 对象。现在只要知道它是我们操作磁盘缓存的直接对象即可,内部原理将在文章后半部分进行分析。 它们得到的都是一个缓存文件,只不过前者写入的内容是变换后的图片,后者后者写入的内容是原始的图片。所以前者获取文件之后只需要进行解码和转码即可,而后者需要进行变换加上解码和转码。 decodeSourceFromCache() 方法中从磁盘缓存中获取原图之后调用了 transformEncodeAndTranscode() 方法进行变换和转码,这里也涉及到缓存的操作,这个会在后面讲到。 是否决定从磁盘缓存中获取取决于 DiskCacheStrategy,它是一个枚举类,用来表示一组缓存策略,cacheSource 属性表示是否缓存原图,cacheResult 属性表示是否缓存处理图。Glide 的默认的磁盘缓存是 RESULT,即只缓存处理图,也可以通过 diskCacheStrategy() 方法来指定。 123456789101112131415161718public enum DiskCacheStrategy { /** Caches with both {@link #SOURCE} and {@link #RESULT}. */ ALL(true, true), /** Saves no data to cache. */ NONE(false, false), /** Saves just the original data to cache. */ SOURCE(true, false), /** Saves the media item after all transformations to cache. */ RESULT(false, true); private final boolean cacheSource; private final boolean cacheResult; DiskCacheStrategy(boolean cacheSource, boolean cacheResult) { this.cacheSource = cacheSource; this.cacheResult = cacheResult; }} 往磁盘缓存写入原图当从磁盘缓存中既取不到原图也取不到处理图时,才会发起网络请求去获取。相应的逻辑在 DecodeJob 的 decodeFromSource() 方法中。 在 decodeFromSource() 方法中,程序首先调用了 decodeSource() 方法来从网络中获取 InputStream 以及解码成 Bitmap,然后调用 transformEncodeAndTranscode() 来进行图片的变换和转码。这两个步骤中都涉及到对缓存的操作。先来看看 decodeSource() 方法 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950private Resource<T> decodeSource() throws Exception { Resource<T> decoded = null; try { long startTime = LogTime.getLogTime(); ///ImageVideoFetcher,内部使用... 返回一个ImageVideoWrapper final A data = fetcher.loadData(priority); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Fetched data", startTime); } if (isCancelled) { return null; } ///返回 Resource<GifBitmapWrapper> decoded = decodeFromSourceData(data); } finally { fetcher.cleanup(); } return decoded; } private Resource<T> decodeFromSourceData(A data) throws IOException { final Resource<T> decoded; ///判断磁盘缓存策略中是否缓存source,缓存ImageVideoWrapper并decode(原始InputStream) if (diskCacheStrategy.cacheSource()) { decoded = cacheAndDecodeSourceData(data); } else { ...... } return decoded; } private Resource<T> cacheAndDecodeSourceData(A data) throws IOException { long startTime = LogTime.getLogTime(); ///loadProvider是FixedLoadProvider,里面封装了ImageVideoGifDrawableLoadProvider // 这里实际返回的ImageVideoWrapperEncoder SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data); ///diskCacheProvider是一个LazyDiskCacheProvider,返回DiskLruCacheWrapper实例 ///resultKey由Engine构造decodejob的时候传进来,由EngineKeyFactory.builidKey返回. diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Wrote source to cache", startTime); } startTime = LogTime.getLogTime(); Resource<T> result = loadFromCache(resultKey.getOriginalKey()); if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) { logWithTimeAndKey("Decoded source from cache", startTime); } return result; } decodeSource() 方法中 DateFetcher 的 loadData() 方法从网络中获取数据之后,调用了 decodeFromSourceData() 方法开始进行解码。在 decodeFromSourceData() 方法中,解码前首先判断当前磁盘缓存策略,如果 cacheSource 为 true,那么解码前还有写入磁盘缓存的操作,也就是 cacheAndDecodeSourceData() 方法。方法中获取了 DiskLruCacheWrapper 对象然后调用了它的 put() 方法来写入缓存,最终会将从服务器获取的 InputSteream 写入一个缓存文件中,缓存对应的 Key 是由 Url 生成的 OriginalKey。 往磁盘缓存写入处理图回到 decodeSource() 方法,将图片解码之后,接着就是调用 transformEncodeAndTranscode() 进行变换和转码,这里也涉及到缓存的操作 123456789101112131415161718192021222324252627282930313233private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) { long startTime = LogTime.getLogTime(); ///因为decodeSourceFromCache去取出的resource还没有经过transform,要先transform再缓存起来 Resource<T> transformed = transform(decoded); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transformed resource from source", startTime); } ///将transform后的resource写入缓存 bitmap或GifDrawable writeTransformedToCache(transformed); startTime = LogTime.getLogTime(); ///与decodeResultFromCache的第二步一样 Resource<Z> result = transcode(transformed); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transcoded transformed from source", startTime); } return result; } private void writeTransformedToCache(Resource<T> transformed) { if (transformed == null || !diskCacheStrategy.cacheResult()) { return; } long startTime = LogTime.getLogTime(); ///GifBitmapWrapperResourceEncoder SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed); ///resultKey是一个EngineKey diskCacheProvider.getDiskCache().put(resultKey, writer); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Wrote transformed from source to cache", startTime); } } transformEncodeAndTranscode() 方法中调用了 transform() 方法进行图片变换之后调用了 writeTransformedToCache() 方法来尝试将 Resource 写入磁盘缓存。具体是借助 GifBitmapWrapperResourceEncoder 来完成,有兴趣可以自行研究。 写入内存缓存按着图片的加载流程,经过变换和转码之后,会回到 EngineRunnable 的 run() 方法,然后回调 EngineJob 的 onResourceReady() 方法,通知图片获取已经完成。我们直接来到 handleResultOnMainThread() 方法,这时 EngineJob 正准备将 onResourceReady() 回调分发给 GenericRequest。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556private void handleResultOnMainThread() { if (isCancelled) { resource.recycle(); return; } else if (cbs.isEmpty()) { throw new IllegalStateException("Received a resource without any callbacks to notify"); } ///封装成一个EngineResource engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it // synchronously released by one of the callbacks. engineResource.acquire(); ///key是一个EngineKey。如果resource.isCacheable()为true,resource将添加到activeResources中,并在移除时加入memorycache listener.onEngineJobComplete(key, engineResource); ///这里的ResourceCallback就是GenericRequest,由GenericRequest调用EngineJob的load方法时传入 for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); ///回调GenericRequest cb.onResourceReady(engineResource); } } // Our request is complete, so we can release the resource. engineResource.release();}public void onEngineJobComplete(Key key, EngineResource<?> resource) { Util.assertMainThread(); // A null resource indicates that the load failed, usually due to an exception. if (resource != null) { ///监听engineresource的释放,从activeResources中移除,移除时放入MemoryCache中 resource.setResourceListener(key, this); if (resource.isCacheable()) { ///将resource放到activeResources中 activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue())); } } // TODO: should this check that the engine job is still current? jobs.remove(key);}@Overridepublic void onResourceReleased(Key cacheKey, EngineResource resource) { Util.assertMainThread(); activeResources.remove(cacheKey); if (resource.isCacheable()) { ///将resource转移到cache中 cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource); }} 注意 listener.onEngineJobComplete(key, engineResource) ,这里的 listener 正是 EngineJob 自身。在 EngineJob 的 onEngineJobComplete() 方法中给 EngineResource 设置了一个 ResourceListener,并且如果没有设置跳过内存缓存,将 EngineResource 放入 activeResources 中。那么 ResourceListener 做了什么呢? ResourceListener 接口中只有 onResourceReleased() 一个方法,当 EngineResource 需要被释放时会回调该方法来通知监听者。这里的监听者正是 EngineJob,在 EngineJob 的 onResourceReleased() 方法中,将 EngineResource 从 activeResources 中删除,并且如果没有设置跳过内存缓存,则放入 MemoryCache 中,否则回收 EngineResource。这讲导致 EngineResource 中持有的 Bitmap 回收到 BitmapPool 中,等待复用。 接着图片将显示到 Target 上,加载流程结束,其中涉及的缓存操作也分析完毕。 那么,我们以缓存作为重点将加载流程梳理一下,就得到这样一个流程图 现在应该对整个缓存的流程有一个整体的认识了,下面将逐一各部分的内部细节,将涉及内存缓存、磁盘缓存和 BitmapPool。 内存缓存Glide 的内存缓存并不像传统的那样仅仅使用 强引用+LRU 算法,还加入了 ActiveResource 这个概念。防止 LruResourceCache 回收了正在使用的资源。 LruResourceCache + BitmapPool 缓存的最大空间默认为应用可用最大内存*0.4,低配手机是应用可用最大内存*0.33. 引用计数内存缓存包括 LruResourceCache 和 ActiveResources 存储的对象都是 EngineResource 对象。它是 Resource 对象的封装,并且内部维护对 Resource 的引用计数。调用 acquire() 引用计数+1,调用 release() 引用计数-1.当调用 release() 后引用计数为0时,由 ResourceListener(目前只有 Engine)来将其从 ActiveResources 移除并决定放入 LruResourceCache 还是调用 Resouece 的 recycler() 方法来回收相关资源,例如将 Bitmap 放入 BitmapPool。 LruResourceCacheLruResourceCache 用于保存最近被使用但是当前不在使用的资源。它的最大容量与屏幕分辨率和 Bitmap 质量参数有关,默认是 宽度 Pixels*高度 Piexls*ARGB_8888图片的质量参数*2,这样至少足够缓存两个屏幕大小的图片了。当容量过大时,使用 LRU 算法来移除最近最少使用的缓存项。 LruResourceCache 实现了 MemoryCache 接口,提供了 get()、remove()、getCurrentSize()、setResourceRemovedListener() 等方法,要注意的是并没有提供 get() 方法,在 Glide 中读取 LruResourceCache 往往是为了放入 ActiveResources,所以用 remove() 就足够了。同时继承了 LruCache,缓存逻辑的实现主要来自于 LruCache。LruCache 内部使用 LinkedHashMap 来实现 LRU 算法。没有十分复杂的逻辑,就不详细分析了。 还有值得注意的是,Engine 通过 setResourceRemovedListener() 方法设置了 ResourceRemovedListener。当一个缓存项因为 LruResourceCache 容量过大使用 LRU 算法被移除(不包括调用 remove() 方法移除的情况)时,将接受到 onResourceRemoved() 回调。Engine 在 onResourceRemoved() 调用了 Resource 的 recycler() 方法,这意味着 Resource 持有的 Bitmap 将会放入到 BitmapPool 中。 总结一下,LruResourceCache 用于保存最近被使用但是当前不在使用的资源。其中缓存的来源只有一个:ActiveResources。当 ActiveResources 中的缓存不再被使用时,会被移除,放入 LruResourceCache 中。缓存的去向有两个:一个是缓存通过 remove() 被读取时,转移到 ActiveResources 中,第二个是最近使用较少被 LRU 算法移除,然后相关的 Bitmap 资源回收到 BitmapPool。 ActiveResourcesActiveResources 用于保存当前正在使用的资源,它其实就是一个裸露的 HashMap,对缓存的读取与写入就是 get() 和 put() 方法的调用。它使用 EngineKey 为键,使用 EngineResource 的弱引用为值。所以也没有容量大小可言。 虽说 ActiveResources 中的资源是当前正在使用的资源,Glide 也提供了负责清理 ActiveResources 弱可达资源的实现(可能是某些特殊场景下没有调用 release() 会导致 ActiveResources 中的资源实际上不在使用了?)。实现方法是构造 EngineResource 的弱引用 WeakReference 时传进 ReferenceQueue 来监听弱引用的回收。当系统检测到该 EngineResource 是弱可达,即不在被使用时,就将该弱引用放入 ReferenceQueue 中。如果 ReferenceQueue 不为空,就说明 EngineResource 该被回收了,可是在什么时候回收呢? Glide 在当前线程对应 Looper 所对应的 MessageQueue 中通过 addIdleHandler() 方法添加了一个 IdleHandler 实例 RefQueueIdleHandler,当 MessageQueue 空闲的时候就会回调 IdleHandler 的 queueIdle() 方法,在 queueIdle() 方法中清理 WeakReference 被回收的 activeResources 资源。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null) { cached.acquire(); activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue())); } return cached;}private static class ResourceWeakReference extends WeakReference<EngineResource<?>> { private final Key key; public ResourceWeakReference(Key key, EngineResource<?> r, ReferenceQueue<? super EngineResource<?>> q) { super(r, q); this.key = key; }}private ReferenceQueue<EngineResource<?>> getReferenceQueue() { if (resourceReferenceQueue == null) { resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>(); MessageQueue queue = Looper.myQueue(); queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue)); } return resourceReferenceQueue;}// Responsible for cleaning up the active resource map by remove weak references that have been cleared.private static class RefQueueIdleHandler implements MessageQueue.IdleHandler { private final Map<Key, WeakReference<EngineResource<?>>> activeResources; private final ReferenceQueue<EngineResource<?>> queue; public RefQueueIdleHandler(Map<Key, WeakReference<EngineResource<?>>> activeResources, ReferenceQueue<EngineResource<?>> queue) { this.activeResources = activeResources; this.queue = queue; } @Override public boolean queueIdle() { ResourceWeakReference ref = (ResourceWeakReference) queue.poll(); if (ref != null) { activeResources.remove(ref.key); } return true; }} 磁盘缓存除了内存缓存之外, Glide 还设计了磁盘缓存。磁盘缓存的默认路径在 /data/data/\/cache/image_manager_disk_cache,默认的大小为 250MB,默认的实现类是 DiskLruCacheWrapper。 Glide 的磁盘缓存默认只缓存处理图,不过可以根据需要通过设置 DiskCacheStrategy 来定制缓存策略。Glide 中不管是原图还是处理图,每张图片都与一个缓存文件对应,存取原图时使用 OriginalKey,内部封装了图片的 Url 和 Signature 信息。而存取处理图时使用 EngineKey 内部封装了图片的 Url 和 Signature 之外,还包括图片尺寸和图片处理流程中相关的 Decoder、Encoder、Transformation 所返回的 id,,通过这些信息就能唯一确定一张图片了。 DiskLruCacheWrapperDiskLruCacheWrapper 实现了 DiskCache 接口,提供的方法非常简洁,提供了 get()、put()、delete()、clear() 四个方法。DiskLruCacheWrapper 内部封装了 DiskLruCache 对象,缓存文件的管理由它来完成。 下面从 DiskLruCacheWrapper 的角度分析一下缓存的写入与读取。 缓存写入123456789101112131415161718192021222324252627282930313233343536373839404142434445464748public File get(Key key) { ///使用SHA-256算法生成唯一String String safeKey = safeKeyGenerator.getSafeKey(key); File result = null; try { //It is possible that the there will be a put in between these two gets. If so that shouldn't be a problem //because we will always put the same value at the same key so our input streams will still represent //the same data final DiskLruCache.Value value = getDiskCache().get(safeKey); if (value != null) { result = value.getFile(0); } } catch (IOException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Unable to get from disk cache", e); } } return result;}public String getSafeKey(Key key) { String safeKey; synchronized (loadIdToSafeHash) { safeKey = loadIdToSafeHash.get(key); } if (safeKey == null) { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); key.updateDiskCacheKey(messageDigest); safeKey = Util.sha256BytesToHex(messageDigest.digest()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } synchronized (loadIdToSafeHash) { loadIdToSafeHash.put(key, safeKey); } } return safeKey;}private synchronized DiskLruCache getDiskCache() throws IOException { if (diskLruCache == null) { diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize); } return diskLruCache;} put() 方法首先调用 getSafeKey() 方法将 Key 转化为一个唯一的字符串,原理是将 Key 中封装的信息(原图就是图片的 Url,处理图就是 Url、尺寸、Decoder等)通过 SHA-256 算法进行编码。缓存的存取都是使用这个字符串,同时这也是缓存文件的文件名。 getDiskCache() 方法返回了 DiskLruCache,方法内部调用了它的静态方法 open() 来创建或返回一个现有的 DiskLruCache 实例。 返回 DiskLruCache 实例之后,通过之前生成的字符串作为参数调用了它的 get() 方法,该方法 返回 DiskLruCache.Value 对象,然后通过 Value 对象来返回一个缓存文件。DiskLruCacheWrapper 就完成了,之后就由 ResourceDecoder 获取文件的 InputSteream 然后解码成 Bitmap 了。 put() 的逻辑非常简单,因为 DiskLruCache 已经封装了缓存实现的细节。DiskLruCache 的实现细节后面再详细分析。 缓存读取12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758public void put(Key key, Writer writer) { String safeKey = safeKeyGenerator.getSafeKey(key); writeLocker.acquire(key); try { DiskLruCache.Editor editor = getDiskCache().edit(safeKey); // Editor will be null if there are two concurrent puts. In the worst case we will just silently fail. if (editor != null) { try { File file = editor.getFile(0); if (writer.write(file)) { editor.commit(); } } finally { editor.abortUnlessCommitted(); } } } catch (IOException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Unable to put to disk cache", e); } } finally { writeLocker.release(key); }}class SourceWriter<DataType> implements DiskCache.Writer { private final Encoder<DataType> encoder; private final DataType data; public SourceWriter(Encoder<DataType> encoder, DataType data) { this.encoder = encoder; this.data = data; } @Override public boolean write(File file) { boolean success = false; OutputStream os = null; try { os = fileOpener.open(file); success = encoder.encode(data, os); } catch (FileNotFoundException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to find file to write to disk cache", e); } } finally { if (os != null) { try { os.close(); } catch (IOException e) { // Do nothing. } } } return success; }} put() 方法接收两个参数,Key 和 Writer,实际上接收的是 SourceWriter 实例,其内部封装了一个 Encoder。put() 方法内部也是先生成字符串 safeKey,然后通过 DiskLruCache 实例的 edit() 方法来获取一个 Editor 实例,进而获取一个缓存文件。然后调用 Writer 的 write() 方法,write() 方法内部将数据写入缓存文件的 OutputStream 中。数据写入完成后调用 Editor 的 commit() 方法来刷新数据。 看来 put() 和 get() 方法的逻辑都比较简单,都是通过 Key 生成字符串,然后通过字符串获取对应的缓存文件,然后使 ResourceDecoder 或者 Encoder 来读取或者写入数据。那么我们来看看 DiskLruCache 是怎么管理缓存文件的。 DiskLruCacheDiskLruCache 表示磁盘缓存,它管理了多个缓存文件,每个 Key 对应一个或多个缓存文件,运行时相关信息保存在一个 LinkedHashMap 中,LinkedHashMap 以 String 为键,以一个 Entry 对象为值。当容量过大时使用 LinkedHashMap 的 LRU 算法来移除 Entry。 Entry 用来表示一个缓存实体,封装了一些相关信息。我们看看它的构造函数 1234567891011121314151617181920private Entry(String key) { this.key = key; ///valueCount默认是1 this.lengths = new long[valueCount]; cleanFiles = new File[valueCount]; dirtyFiles = new File[valueCount]; // The names are repetitive so re-use the same builder to avoid allocations. StringBuilder fileBuilder = new StringBuilder(key).append('.'); int truncateTo = fileBuilder.length(); for (int i = 0; i < valueCount; i++) { fileBuilder.append(i); ///cleanFiles用key.1命名 cleanFiles[i] = new File(directory, fileBuilder.toString()); fileBuilder.append(".tmp"); ///dirtyFiles用key.1.tmp命名 dirtyFiles[i] = new File(directory, fileBuilder.toString()); fileBuilder.setLength(truncateTo); }} 可见 Entry 封装了 Key,对应的缓存文件和文件大小等信息。一个缓存实体对应两个缓存文件,cleanFile 文件中的数据时刻是干净可读的,dirtyFile 的作用是作为临时文件,当需要写入缓存时,返回的是 dirtyFile,等调用 commit() 方法时再更新数据。这样缓存的写入时也不会影响缓存的读取了。dirtyFile 用 key.1命名,dirtyFiles 用 key.1.tmp 命名,知道缓存 Key 就知道对应的缓存文件了。 缓存构建磁盘缓存与内存缓存不同,内存缓存在程序每次重新启动时都是空白的全新的状态,需要一个预热的过程,等缓存写入一定量的时候才能开始发挥作用,而磁盘缓存则不能受程序重新启动的影响,程序重新启动时,要能够自动恢复到上次运行的状态,每个 Key 对应哪个缓存文件、每个缓存的大小、哪些缓存文件的数据是干净的、哪些缓存文件的数据是不正常的写入需要删除、当前磁盘缓存的大小等等,都需要实时管理好。这就需要有一个文件实时记录下磁盘缓存的相关信息,以便能够随时恢复。DiskLruCache 就用到了这样一个文件,文件名为 journal。我们先来看看该文件的格式。 12345678910111213libcore.io.DiskLruCache11001CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832DIRTY 335c4c6028171cfddfbaae1a9c313c52CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934REMOVE 335c4c6028171cfddfbaae1a9c313c52DIRTY 1ab96a171faeeee38496d8b330771a7aCLEAN 1ab96a171faeeee38496d8b330771a7a 1600READ 335c4c6028171cfddfbaae1a9c313c52READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 这是典型的 journal 文件内容。文件的前5行是文件头 第一行 固定为 libcore.io.DiskLruCache 第二行 版本号,源码中定为1 第三行 应用版本号,由 DiskLruCache 的使用者指定 第四行 valueCount,每个 Key 对应几个文件,源码中定为1 第五行 空行 第6行开始记录的是磁盘缓存的操作记录,每一行的格式为操作类型+Key+可选数据项。数据项之间用空格分隔,如果操作类型是 CLEAN,那么可选数据项是数字,描述缓存文件的大小。下面介绍一下四种操作类型 DIRTY 表示一个 Entry 被创建或正在写入,一个合法的 DIRTY 操作后面必须接着 CLEAN 操作或者 REMOVE 操作,否则表示该文件只是临时文件,应该被删除。 CLEAN 表示一个 Entry 被成功写入,可以被读取。CLEAN 行后面应该接着 valueCount 个数字表示每个缓存文件的大小 READ 表示一个 Entry 被读取 REMOVE 表示一个 Entry 被删除 journal 文件记录了每一个对磁盘缓存的操作,通过读取这个文件,程序就能还原出磁盘缓存的状态了。下面来看看磁盘缓存具体的构建过程。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } if (valueCount <= 0) { throw new IllegalArgumentException("valueCount <= 0"); } // If a bkp file exists, use it instead. ///判断是否有journal备份文件,如果有则进一步判断有无journal源文件,有则删除备份文件,无则将备份文件改名为源文件 File backupFile = new File(directory, JOURNAL_FILE_BACKUP); if (backupFile.exists()) { File journalFile = new File(directory, JOURNAL_FILE); // If journal file also exists just delete backup file. if (journalFile.exists()) { backupFile.delete(); } else { renameTo(backupFile, journalFile, false); } } // Prefer to pick up where we left off. ///有journal源文件则从该文件中加载到新的DiskLruCache DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); if (cache.journalFile.exists()) { try { cache.readJournal(); cache.processJournal(); return cache; } catch (IOException journalIsCorrupt) { System.out .println("DiskLruCache " + directory + " is corrupt: " + journalIsCorrupt.getMessage() + ", removing"); cache.delete(); } } // Create a new empty cache. ///既没有备份文件也没有原journal文件,新建一个 directory.mkdirs(); cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); cache.rebuildJournal(); return cache;} DiskLruCache 在 open() 方法中构建。构建 DiskLruCache 需要指定目录、应用版本、每个 Key 的缓存文件个数、磁盘缓存的最大容量。 程序首先判断是否有 journal 备份文件(备份文件命名为 journal.bkp),如果有的话则进一步判断有无 journal 原文件,有则删除备份文件,无则将备份文件改名为原文件。 接下来再次判断 journal 文件是否存在 journal 文件存在 如果 journal 文件存在则调用 readJournal() 和 processJournal() 方法。先来看 readJournal() 123456789101112131415161718192021222324252627282930313233343536373839404142private void readJournal() throws IOException { StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII); try { ///校验文件头 String magic = reader.readLine(); String version = reader.readLine(); String appVersionString = reader.readLine(); String valueCountString = reader.readLine(); String blank = reader.readLine(); if (!MAGIC.equals(magic) || !VERSION_1.equals(version) || !Integer.toString(appVersion).equals(appVersionString) || !Integer.toString(valueCount).equals(valueCountString) || !"".equals(blank)) { throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); } int lineCount = 0; while (true) { try { ///处理一行 readJournalLine(reader.readLine()); lineCount++; } catch (EOFException endOfJournal) { break; } } redundantOpCount = lineCount - lruEntries.size(); // If we ended on a truncated line, rebuild the journal before appending to it. if (reader.hasUnterminatedLine()) { rebuildJournal(); } else { journalWriter = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(journalFile, true), Util.US_ASCII)); } } finally { Util.closeQuietly(reader); }} 首先校验文件头,然后循环调用 readJournalLine() 方法处理每一行 1234567891011121314151617181920212223242526272829303132333435363738394041private void readJournalLine(String line) throws IOException { int firstSpace = line.indexOf(' '); if (firstSpace == -1) { throw new IOException("unexpected journal line: " + line); } ///从行中读取key 格式 REMOVE|CLEAN|DIRTY key 文件大小(知道了key也就知道了key对应的cleanFile和dirtyFile) int keyBegin = firstSpace + 1; int secondSpace = line.indexOf(' ', keyBegin); final String key; if (secondSpace == -1) { key = line.substring(keyBegin); ///处理 remove if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) { lruEntries.remove(key); return; } } else { key = line.substring(keyBegin, secondSpace); } Entry entry = lruEntries.get(key); if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } ///如果是clean,那么readable就为true if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) { String[] parts = line.substring(secondSpace + 1).split(" "); entry.readable = true; entry.currentEditor = null; entry.setLengths(parts); } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) { entry.currentEditor = new Editor(entry); } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) { // This work was already done by calling lruEntries.get(). } else { throw new IOException("unexpected journal line: " + line); }} 先取出行中的 Key,如果是 REMOVE 操作,则将 Entry 从 LinkedHashMap 中移除。一个 Entry 表示一个缓存实体,封装了 Key、对应的缓存文件、文件大小 等信息。 如果 Key 对应的 Entry 不存在,则创建一个,然后放入 LinkedHashMap 中。最后根据操作类型 CLEAN 还是 DIRTY,进行一些属性初始化。如果 Entry 的 currentEditor 属性不为空,表示当前正在写入。 readJournalLine() 主要就是读取 journal 文件按照操作记录来还原了 LinkedHashMap。但是仔细分析可以发现它还没处理好那些单独出现的 DIRTY 记录,这些 Entry 应在被删除。那么我们接下来看看 processJournal() 方法。 123456789101112131415161718private void processJournal() throws IOException { deleteIfExists(journalFileTmp); for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) { Entry entry = i.next(); if (entry.currentEditor == null) { for (int t = 0; t < valueCount; t++) { size += entry.lengths[t]; } } else { entry.currentEditor = null; for (int t = 0; t < valueCount; t++) { deleteIfExists(entry.getCleanFile(t)); deleteIfExists(entry.getDirtyFile(t)); } i.remove(); } }} processJournal() 果然对这部分 Entry 做了清理,判断依据是如果是单独出现的 DIRTY 记录,那么该 Entry 的 currentEditor 必定是不为空的。同时 processJournal() 也统计了当前磁盘磁盘缓存的大小。 那么 journal 文件存在的情况就分析完了。 journal 文件不存在 如果文件不存在,则创建目录,新建 DiskLryCache ,调用 rebuildJournal() 方法生成 journal 文件。 1234567891011121314151617181920212223242526272829303132333435363738private synchronized void rebuildJournal() throws IOException { if (journalWriter != null) { journalWriter.close(); } Writer writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII)); try { writer.write(MAGIC); writer.write("\n"); writer.write(VERSION_1); writer.write("\n"); writer.write(Integer.toString(appVersion)); writer.write("\n"); writer.write(Integer.toString(valueCount)); writer.write("\n"); writer.write("\n"); for (Entry entry : lruEntries.values()) { if (entry.currentEditor != null) { writer.write(DIRTY + ' ' + entry.key + '\n'); } else { writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); } } } finally { writer.close(); } if (journalFile.exists()) { renameTo(journalFile, journalFileBackup, true); } renameTo(journalFileTmp, journalFile, false); journalFileBackup.delete(); journalWriter = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));} 首先创建 journal.tmp 文件,写入文件头,然后将临时文件重新命名为 journal 原文件。 那么 DiskLruCache 磁盘缓存的构建就分析完毕了,主要是当操作日志文件 journal 文件存在时,通过读取文件来还原 LinkHashMap,同时统计当前磁盘缓存的大小。 缓存写入需要写入缓存时,通常先调用 edit() 方法获取一个 Editor 对象,通过该对象的 getFile() 方法获取一个文件以供写入缓存,写入成功后 调用 Editor 对象的 commit() 方法来提交写入的数据。 1234567891011121314151617181920212223242526272829public Editor edit(String key) throws IOException { return edit(key, ANY_SEQUENCE_NUMBER);}private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { checkNotClosed(); Entry entry = lruEntries.get(key); if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { return null; // Value is stale. } if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } else if (entry.currentEditor != null) { return null; // Another edit is in progress. } Editor editor = new Editor(entry); entry.currentEditor = editor; // Flush the journal before creating files to prevent file leaks. journalWriter.append(DIRTY); journalWriter.append(' '); journalWriter.append(key); journalWriter.append('\n'); journalWriter.flush(); return editor;} edit() 方法首先通过 key 在 LinkedHashMap 中获取 Entry,如果 Entry 为空则创建一个放入 LinkedHashMap 中。然后创建一个 Editor 对象并设置到 Entry 的 currentEditor 属性中。最后在 journal 新增一条 DIRTY 记录,表示该 key 的缓存文件正在被写入。 接下来看看 Editor 的 getFile() 方法 123456789101112131415public File getFile(int index) throws IOException { synchronized (DiskLruCache.this) { if (entry.currentEditor != this) { throw new IllegalStateException(); } if (!entry.readable) { written[index] = true; } File dirtyFile = entry.getDirtyFile(index); if (!directory.exists()) { directory.mkdirs(); } return dirtyFile; } } getFile() 方法返回 Entry 对应的 dirtyFile。 向 Editor 返回的文件写入缓存数据后,需要调用 commit() 方法提交数据,否则 dirtyFile 中的数据将被视为非法写入而被删除。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 public void commit() throws IOException { // The object using this Editor must catch and handle any errors // during the write. If there is an error and they call commit // anyway, we will assume whatever they managed to write was valid. // Normally they should call abort. completeEdit(this, true); committed = true; } private synchronized void completeEdit(Editor editor, boolean success) throws IOException { Entry entry = editor.entry; if (entry.currentEditor != editor) { throw new IllegalStateException(); } // If this edit is creating the entry for the first time, every index must have a value. ///如果这个记录以前有值,则readable为TRUE if (success && !entry.readable) { for (int i = 0; i < valueCount; i++) { if (!editor.written[i]) { editor.abort(); throw new IllegalStateException("Newly created entry didn't create value for index " + i); } if (!entry.getDirtyFile(i).exists()) { editor.abort(); return; } } } //将dirty重命名为clean for (int i = 0; i < valueCount; i++) { File dirty = entry.getDirtyFile(i); if (success) { if (dirty.exists()) { File clean = entry.getCleanFile(i); dirty.renameTo(clean); long oldLength = entry.lengths[i]; long newLength = clean.length(); entry.lengths[i] = newLength; size = size - oldLength + newLength; } } else { deleteIfExists(dirty); } } redundantOpCount++; entry.currentEditor = null; ///在journal文件中添加一条记录 if (entry.readable | success) { entry.readable = true; journalWriter.append(CLEAN); journalWriter.append(' '); journalWriter.append(entry.key); journalWriter.append(entry.getLengths()); journalWriter.append('\n'); if (success) { entry.sequenceNumber = nextSequenceNumber++; } } else { lruEntries.remove(entry.key); journalWriter.append(REMOVE); journalWriter.append(' '); journalWriter.append(entry.key); journalWriter.append('\n'); } journalWriter.flush(); if (size > maxSize || journalRebuildRequired()) { executorService.submit(cleanupCallable); }} commit() 方法内部又调用了 completeEdit() 方法。completeEdit() 方法主要将 dirtyFile 重命名为 cleanFile,重新计算缓存大小,然后在 journal 文件中增加一条 CLEAN 记录。 redundantOpCount 变量用来记录磁盘缓存的操作次数,当操作次数大于某个值时,就会根据 LinkedHashMap 重新创建 journal 文件,以防止 journal 文件过大。 缓存读取读取缓存时调用 get() 方法即可,它返回一个 Value 对象,表示 Entry 的一个快照。 1234567891011121314151617181920212223242526272829public synchronized Value get(String key) throws IOException { checkNotClosed(); Entry entry = lruEntries.get(key); if (entry == null) { return null; } if (!entry.readable) { return null; } for (File file : entry.cleanFiles) { // A file must have been deleted manually! if (!file.exists()) { return null; } } redundantOpCount++; journalWriter.append(READ); journalWriter.append(' '); journalWriter.append(key); journalWriter.append('\n'); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return new Value(key, entry.sequenceNumber, entry.cleanFiles, entry.lengths);} 主要是用过 key 获取 Entry 的 cleanFile,封装为 Value 对象返回,同时在 journal 文件中新增一条 READ 记录。 BitmapPool虽然有了图片内存缓存程序在一般情况都都能工作得很好,不会出现大的问题,但是内存缓存的容量总是有限度的,不能无限制地增长,所以程序还是会周期性的申请和释放内存。特别是在一个长列表快速滑动时,会造成大量的图片申请和释放,导致 GC 频繁,进而产生卡顿。 应用运行时图片内存往往占相当大的一部分,如果这部分内存能够尽量的复用,就能够显著地减少内存的频繁申请和释放了。基于这个考虑,Glide 针对 Bitmap 设计了复用方案,这就是 BitmapPool。 Glide 构建了一个 BitmapPool,图片的 Bitmap 的申请和释放都需要通过它来处理。需要加载新的图片时,先从 BitmapPool 中查找有没有相应大小或者稍大一点的 Bitmap,有则直接使用,没有再创建新的 Bimap。一个长列表中的图片往往是大小相同的,所以这个复用率还是相当可观的。 BitmapPool 的最大容量与屏幕分辨率有关,默认是 宽度 Pixels*高度 Piexls*ARGB_8888图片的质量参数*4,这样至少足够缓存四个屏幕大小的图片。容量到达阈值时,使用 LRU 算法从最近最少使用的图片尺寸中移除图片。 复用策略BitmapPool 使用策略模式来封装不同的复用策略,策略接口是 LruPoolStrategy,定义了 put()、get()、getSize() 等方法。Glide 中有两种复用策略 AttributeStrategy 复用的图片需要图片的尺寸和 Bitmap.Config 完全一致 SizeConfigStrategy 复用要求相对宽松,复用的图片需要 Bitmap.Config 一致,但复用图片的所需内存比原图小即可。确保 Bitmap.Config 一致后,如果有内存大小一致的图片则直接复用,没有则选取内存稍大一点的图片。需要 KITKAT 以上版本 Glide 在 KITKAT 以上系统中采用 SizeConfigStrategy,否则采用 AttributeStrategy。显然采取 SizeConfigStrategy 的复用率更高。之所以有这两者的区分,跟 Bitmap 的 reconfigure() 方法有关,BitmapPool 能够复用旧图片的内存得益于这个方法,这个方法是在 KITKAT(API 19) 增加的。该方法的注释如下 123456789101112131415161718192021222324252627282930313233343536373839404142/** * <p>Modifies the bitmap to have a specified width, height, and {@link * Config}, without affecting the underlying allocation backing the bitmap. * Bitmap pixel data is not re-initialized for the new configuration.</p> * * <p>This method can be used to avoid allocating a new bitmap, instead * reusing an existing bitmap's allocation for a new configuration of equal * or lesser size. If the Bitmap's allocation isn't large enough to support * the new configuration, an IllegalArgumentException will be thrown and the * bitmap will not be modified.</p> * * <p>The result of {@link #getByteCount()} will reflect the new configuration, * while {@link #getAllocationByteCount()} will reflect that of the initial * configuration.</p> * * <p>WARNING: This method should NOT be called on a bitmap currently used * by the view system. It does not make guarantees about how the underlying * pixel buffer is remapped to the new config, just that the allocation is * reused. Additionally, the view system does not account for bitmap * properties being modifying during use, e.g. while attached to * drawables.</p> * * @see #setWidth(int) * @see #setHeight(int) * @see #setConfig(Config) */public void reconfigure(int width, int height, Config config) { checkRecycled("Can't call reconfigure() on a recycled bitmap"); if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); } if (!isMutable()) { throw new IllegalStateException("only mutable bitmaps may be reconfigured"); } if (mBuffer == null) { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length); mWidth = width; mHeight = height;} 简单来说,reconfigure() 方法能够在不重新初始化 Bitmap 和不影响底层内存分配的前提下修改 Bitmap 的尺寸和类型。使用该方法能够复用旧 Bitmap 的内存从而避免给新 Bitmap 分配内存。通过复用生成的 Bitmap,新的内存大小可以通过 getByteCount() 方法获得。 由于 Bitmap 是复用的,所以它所映射的底层内存中还是原始图片的数据,所以 BitmapPool 将 Bitmap 返回给使用者前,还需要使用 Bitmap 的 eraseColor(Color.TRANSPARENT) 来擦除旧的数据。 LRU 算法实现BitmapPool 并没有使用 LruCache 来实现 LRU 功能,其内部使用 LinkedHashMap。而是自定义了一个数据结构 GroupedLinkedMap,其内部使用 HashMap,不同之处是其每个键对应的值是 LinkedEntry 对象,LinkedEntry 对象内部持有一个 ArrryList 来保存一组图片,并且持有 next 和 prev 引用指向其他 LinkedEntry,以构成一个双向链表,通过 makeTail() 和 makeHead() 方法来改变链表头尾的位置,从而实现 LRU 功能,其 LRU 算法针对的是对某尺寸的一组图片,而非某张图片。这样就能通过 Key 访问一组图片了。(其实本人觉得LruCache也可以实现,存的值类型是Arraylis 就好了) Glide 中的缓存机制算是介绍完了,本文先是从 Glide 的图片加载流程入手,还原出 Glide 缓存的整体设计。然后从 内存缓存、磁盘缓存、BitmapPool 三个方面分别介绍了各自的一些实现细节。由于本人水平有限,对 Glide 缓存机制的理解深度可能有些欠缺,如有发现文章中欠妥的地方欢迎指正。 感谢阅读。]]></content>
<categories>
<category>源码分析</category>
</categories>
<tags>
<tag>Glide</tag>
<tag>源码分析</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Glide源码解析(一):加载流程]]></title>
<url>%2F2017%2F04%2F27%2FGlide%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%2F</url>
<content type="text"><![CDATA[对于 Glide 相信大家都不陌生,它是 Google 员工 bumptech 开源的一款图片加载框架,因为其简单易用的 API ,强大的功能,优秀的内存管理被 Google 官方所推荐并且被广泛运用在 Google 的开源项目中。目前 Glide 、Picasso 和 Fresco 都是比较主流图片加载框架。 它们的使用场景基本都是重合的,而本人对 Glide 更为熟悉,所以选取 Glide 来进行深入学习,这系列文章作为学习的总结。选取的 Glide 版本是比较新的稳定版 3.7.0 。 本篇是源码解析的开篇,主要分析 Glide 加载图片的基本流程。在后续的专题中再对缓存机制、生命周期监听等细节进行详细分析。 预热为了预防直接扎进源码的海洋带来的各种不适,做一下热身还是很有必要的。我们先抛开源码,从感性角度理解 Glide 成功显示出一张图片,需要经过哪些过程,由什么角色完成,这些角色有机结合起来就是整个基本流程了。 数据源。即对数据来源的描述,要加载图片,当然需要告诉 Glide 从哪里获取了。它可以是 URL ,资源 ID 或者是一个文件。Glide 中称之为 Model。 从数据源中获取原始数据。一般是 InputStream ,Glide 中称之为 Data。负责从数据源中获取原始数据的角色称为 ModelLoader。 对原始数据解码。例如将 InputStream 解码为 Bitmap 或者 GifDrawable。解码后的资源称为 Resource。完成解码的角色称为 ResourceDecoder。 转码。Glide 除了能加载静图外,还能加载动图 Gif 。但是解码后静图类型 Bitmap 与 GifDrawable 并不统一。为了逻辑方便处理,Glide 会将 Bitmap 转码成 GlideBitmapDrawable。这样类型就能统一了,都是 GlideDrawable。负责转码的角色称为 Transcoder。 变换。根据图片设置的 ScaleType,做 FitCenter、CenterCrop 等转换。 在 Glide 中用 ResourceTranscoder 表示。 将图片显示到目标如 ImageView 上,Glide 将显示目标封装为 Target。 到这里已经从概念上梳理清楚 Glide 图片加载的大概流程了,这是抹去了大量细节的描述,但是记住这个流程在阅读源码时就能知道当前所在环节而不轻易迷失方向。 流程分析Glide 最基本的用法就是三步:with()再load()最后into()。下面从with()开始分析。 withwith()方法是 Glide 中的一组静态方法,有多个类型不同的重载方法: 1234567891011121314151617181920212223242526//RequestManager负责管理请求,具体是通过 Activity、Fragment的生命周期或网络连接的改变来智能地 start/stop/restart 请求。public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context);}public static RequestManager with(Activity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity);}public static RequestManager with(FragmentActivity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public static RequestManager with(android.app.Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment);}public static RequestManager with(Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment);} 以上是with()的全部重载方法,with()方法可接受的参数类型很多,包括 Context、Activity、FragmentActivity和Fragment,这样就可以灵活的根据当前上下文进行选择。同时不同的参数会对图片加载的生命周期产生影响。这些重载方法的内部实现都比较简单,先调用RequestManagerRetriever类的静态get()方法,该方法返回RequestManagerRetriever的单例实现。然后调用该实例的get()重载方法,最终返回一个RequestManager。 RequestManager负责管理请求,具体是通过 Activity、Fragment的生命周期或网络连接的改变来智能地 start/stop/restart 请求。 下面看看 get()方法的内部逻辑: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154public class RequestManagerRetriever implements Handler.Callback { /** The singleton instance of RequestManagerRetriever. */ private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever(); /** The top application level RequestManager. */ private volatile RequestManager applicationManager; /** * Retrieves and returns the RequestManagerRetriever singleton. */ public static RequestManagerRetriever get() { return INSTANCE; } private RequestManager getApplicationManager(Context context) { // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or activity. // However, in this case since the manager attached to the application will not receive lifecycle // events, we must force the manager to start resumed using ApplicationLifecycle. applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle(), new EmptyRequestManagerTreeNode()); } } } return applicationManager; } public RequestManager get(Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } public RequestManager get(FragmentActivity activity) { //如果当前不是运行在主线程 if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } } public RequestManager get(Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread()) { return get(fragment.getActivity().getApplicationContext()); } else { FragmentManager fm = fragment.getChildFragmentManager(); return supportFragmentGet(fragment.getActivity(), fm); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private static void assertNotDestroyed(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) { throw new IllegalArgumentException("You cannot start a load for a destroyed activity"); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public RequestManager get(android.app.Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return get(fragment.getActivity().getApplicationContext()); } else { android.app.FragmentManager fm = fragment.getChildFragmentManager(); return fragmentGet(fragment.getActivity(), fm); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm) { RequestManagerFragment current = getRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; } SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) { SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag( FRAGMENT_TAG); if (current == null) { current = pendingSupportRequestManagerFragments.get(fm); if (current == null) { current = new SupportRequestManagerFragment(); pendingSupportRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); ///在主线程中将刚添加的SupportRequestManagerFragment从pending中移除 handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } RequestManager supportFragmentGet(Context context, FragmentManager fm) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }} get() 也有多个重载方法,不过实际上就是两种情况:Application类型和非Application类型。 当传入的参数是 Application 类型时,就调用 getApplicationManager() 方法获取一个 RequestManager 对象,这是最简单的一种情况,因为 Application 的生命周期也就是应用程序的生命周期,RequestManager 不需要特地去管理 Request 的生命周期。 当传入的参数是非 Application 类型时,先用当前的 Activity 或 Fragment 获取 FragmentManager。然后调用重载的 fragmentGet() 方法来获取 ResuqstManager。 fragmentGet() 方法的内部逻辑并不复杂,首先是调用 getRequestManagerFragment() 方法创建一个 RequestManagerFragment,然后创建一个 RequestManager 并且将两者绑定,最后返回 RequestManager 实例。 为什么要创建 RequestManagerFragment 呢?它和 RequestManager 是什么关系?其实是为了监听 Activity 的生命周期事件来达到管理图片加载的生命周期。一个 RequestManagerFragment 和一个 RequestManager 对应。RequestManagerFragment 是一个无界面的 Fragment ,通过 FragmentManager 添加到当前 Activity 或 Fragment 下,通过这样一个隐藏的 Fragment 就能知道当前 Activity 的生命周期了。而 RequestManager 在 RequestManagerFragment 中注册了回调接口。这样就能通过监听 Activity 或 Fragment 的 onStart 、onStop 、onDestory 回调来发起、暂停、取消加载请求。这是 Glide 中的巧妙设计之一。 with() 部分的源码就到此结束,没有复杂的逻辑,主要是为了获取一个能够监听生命周期的 RequestManager 对象。可见漫漫长路才开始了一小步。 load经过 with() 方法,我们获得了一个 RequestManager 对象。接下来我们看看 load() 方法。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849/** * Returns a request builder to load the given {@link File}. * * @see #fromFile() * @see #load(Object) * * @param file The File containing the image */ public DrawableTypeRequest<File> load(File file) { return (DrawableTypeRequest<File>) fromFile().load(file); } /** * Returns a request builder to load the given resource id. * * @see #fromResource() * @see #load(Object) * * @param resourceId the id of the resource containing the image */ public DrawableTypeRequest<Integer> load(Integer resourceId) { return (DrawableTypeRequest<Integer>) fromResource().load(resourceId); } /** * Returns a request builder to load the given {@link java.lang.String}. * signature. * * @see #fromString() * @see #load(Object) * * @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}. */ public DrawableTypeRequest<String> load(String string) { return (DrawableTypeRequest<String>) fromString().load(string); } /** * Returns a request builder to load the given {@link Uri}. * * @see #fromUri() * @see #load(Object) * * @param uri The Uri representing the image. Must be of a type handled by * {@link com.bumptech.glide.load.model.UriLoader}. */ public DrawableTypeRequest<Uri> load(Uri uri) { return (DrawableTypeRequest<Uri>) fromUri().load(uri); } load() 也有多个重载方法,能接受文件、资源ID、字符串和 Uri 作为输入进而加载图片。这里我就选取最常见的用 URL 字符串输入来分析。只有简短的一行代码,链式调用了fromString() 方法和 load() 方法。先来看看 fromString() 方法 1234567891011121314151617181920public DrawableTypeRequest<String> fromString() { return loadGeneric(String.class);}private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) { ///对于 string,int,integer,file,uri ,glide对每种modelclass都注册了将其转化为InputStream和ParcelFileDescriptor的ModelLoaderFactory ///从注册的ModelLoaderFactory中查找string -> stream 的 modelloader 得到StreamStringLoader ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context); ///从注册的ModelLoaderFactory查找 string -> filedescriptor 的modelloader 得到FileDescriptorStringLoader ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context); if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) { throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for" + " which there is a registered ModelLoader, if you are using a custom model, you must first call" + " Glide#register with a ModelLoaderFactory for your custom model class"); } return optionsApplier.apply( new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,glide, requestTracker, lifecycle, optionsApplier));} fromString() 方法内部调用了 loadGeneric() 方法,loadGeneric() 方法返回了一个 DrawableTypeRequest 对象。 DrawableTypeRequest 的继承关系如下 可见它负责创建加载请求,同时也代表一个加载 Drawable 的请求。现在回到上段代码,看看它是如何创建出来的。 第一步,先是调用了 Glide 的静态方法 buildStreamModelLoader() 和 buildFileDescriptorModelLoader() 来获得两个 ModelLoader。回想一下预热部分的内容,ModelLoader 是负责从数据源中获取原始数据的,这里分别获得了从 String 表示的 URL 中取得 InputStream 和 ParcelFileDescriptor 的 ModelLoader。 实际上,Glide 在初始化的时候,对于每种类型的输入:String、int、Integer、File、Uri,都注册了能够将它们转化为 InputStream 和 ParcelFileDescriptor 的 ModelLoader,保存在HashMap中。这两个 buildxxxModelLoader() 方法实际上就是从 HashMap 中获取对应的 ModelLoader。在当前情景下,获取到的是 StreamStringLoader 和 FileDescriptorStringLoader。 第二步,用刚刚获取的 StreamStringLoader 和 FileDescriptorStringLoader 作为构造参数来创建 DrawableTypeRequest。还有其他一些参数,这里暂且先不关注。 回到 RequestManager 的 load() 方法,接下来执行 fromString() 返回的 DrawableTypeRequest 的 load() 方法。DrawableTypeRequest 没有 load() 方法,因此实际上执行的是其父类 DrawableRequestBuilder 的 load() 方法。 123456789101112131415===DrawableRequestBuilder.java===@Overridepublic DrawableRequestBuilder<ModelType> load(ModelType model) { super.load(model); return this;}===GenericRequestBuilder.java===public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) { this.model = model; isModelSet = true; return this;} 只是做了简单的赋值操作,这里就不多做解释了。 第二步 load() 至此也结束了,目前我们得到了一个 DrawableTypeRequest 对象。第三步 into() 将围绕这个对象展开。 into接下来接着看 DrawableTypeRequest 的 into() 方法。 1234@Overridepublic Target<GlideDrawable> into(ImageView view) { return super.into(view);} 真正的逻辑在其父类 GenericRequestBuilder 中。 1234567891011121314151617181920212223242526272829303132333435/** * Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into the view, and frees * any resources Glide may have previously loaded into the view so they may be reused. * * @see Glide#clear(android.view.View) * * @param view The view to cancel previous loads for and load the new resource into. * @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}. */public Target<TranscodeType> into(ImageView view) { Util.assertMainThread(); if (view == null) { throw new IllegalArgumentException("You must pass in a non null View"); } if (!isTransformationSet && view.getScaleType() != null) { switch (view.getScaleType()) { case CENTER_CROP: //导致transformation中保存一个类型为centerCrop的transformation,并且isTransformationSet设为true applyCenterCrop(); break; case FIT_CENTER: case FIT_START: case FIT_END: applyFitCenter(); break; //$CASES-OMITTED$ default: // Do nothing. } } ///transcodeClass用来标记imageview的使用bitmap还是drawable还是glidedrawable。返回不同实例的ImageViewTarget return into(glide.buildImageViewTarget(view, transcodeClass));} 首先 Glide 根据 ImageView 的 ScaleType 来对图片进行相应的变换。这里选取 CENTER_CROP 的场景作为例子进行分析。来看看 applyCenterCrop() 方法。 applyCenterCrop() 的实现在具体的子类中,在当前场景下就是 DrawableTypeRequest。 1234567891011121314151617181920212223242526272829===DrawableTypeRequest.java===@Override void applyCenterCrop() { centerCrop(); } public DrawableRequestBuilder<ModelType> centerCrop() { return transform(glide.getDrawableCenterCrop()); } public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) { super.transform(transformation); return this; } ===GenericRequestBuilder.java=== public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform( Transformation<ResourceType>... transformations) { isTransformationSet = true; if (transformations.length == 1) { transformation = transformations[0]; } else { transformation = new MultiTransformation<ResourceType>(transformations); } return this; } 嵌套比较多,我抽取出了上面的相关方法。 在 DrawableTypeRequest 的 centerCrop() 方法中调用了 Glide 的 glide.getDrawableCenterCrop() 获取一个 Transformation 实例。预热部分提到,Transformation 负责对图片进行 CenterCrop、FitCenter 等转化。此处会获取到一个 GifBitmapWrapperTransformation,其内部封装了对 Bitmap 执行转化的 BitmapTransformation 和一个对 GifDrawable 执行变换的 GifDrawableTransformation。 经过层层嵌套,最终会执行父类 GenericRequestBuilder 的 transform() 方法,该方法将 isTransformationSet 置为 true,并且将 Transformation 实例保存到 transformation 变量中。可见这里只是做了相关的赋值操作,真正的图片变换将在后面合适的时机进行。 回到 GenericRequestBuilder 的 into() 方法,记录下该图片需要进行的转化之后,调用了 Glide 的 buildImageViewTarget() 来构建一个 ImageViewTarget,然后使用 ImageViewTarget 作为参数执行 into() 方法。 Target 是一个接口,Glide 将可以接收图片的目标封装成一个 Target 对象,并且 Target 包含 onLoadStarted、onResourceReady、onLoadCleared、onLoadFailed 等方法来显示不同的内容。 我们接着看看 buildImageViewTarget() 方法 12345678910111213141516171819202122232425 ===Glide.java=== <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) { return imageViewTargetFactory.buildTarget(imageView, transcodedClass); } === ImageViewTargetFactory.java=== public class ImageViewTargetFactory { //这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法, //那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。 @SuppressWarnings("unchecked") public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) { if (GlideDrawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new GlideDrawableImageViewTarget(view); } else if (Bitmap.class.equals(clazz)) { return (Target<Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); } }} 返回的 Target 具体实例取决于 transcodedClass 参数,该参数由 DrawableTypeRequest 传入,具体来说就是 GlideDrawable.class。而如果你在 Glide.with().load() 之后加上了 asBitmap(),将得到一个 BitmapTypeRequest,那么 transcodedClass 参数将是 Bitmap.class。如果加上了 asGif() ,将得到一个 GifTypeRequest,那么 transcodedClass 参数将是 GifDrawable。实际上他们都是 GenericRequestBuilder 的子类,关系如下: 简单来说,默认情况下 buildImageViewTarget 将返回一个 GlideDrawableImageViewTarget 实例,除非你调用了 asBitmap(),那么将返回一个 BitmapImageViewTarget 实例。在当前情景下,我们获取到的是前者。 回到 GenericRequestBuilder 的 into() 方法,刚刚我们构建了一个 GlideDrawableImageViewTarget 对象,接下来它将作为参数来执行重载的 into() 方法。 12345678910111213141516171819202122232425//清除target绑定的旧request,新建一个request并绑定到target。将request传递到requestTracker的runRequest方法中public <Y extends Target<TranscodeType>> Y into(Y target) { Util.assertMainThread(); if (target == null) { throw new IllegalArgumentException("You must pass in a non null Target"); } if (!isModelSet) { throw new IllegalArgumentException("You must first set a model (try #load())"); } Request previous = target.getRequest(); if (previous != null) { previous.clear();///回收资源,设置状态,设置placeholder requestTracker.removeRequest(previous);///意味着就request不再受生命周期回调影响 previous.recycle();///成员属性置null,回收到REQUEST_POOL,以供复用 } Request request = buildRequest(target); target.setRequest(request); lifecycle.addListener(target); requestTracker.runRequest(request); return target;} 这段代码主要是构建了一个 Request 对象然后执行。首先 target.getRequest() 取出与 target 对象绑定的 Request 对象。Glide 通过 View 的 setTag() 方法将特定的 View 和其对应的图片加载请求绑定在一起。这样做的好处是能够容易地判断 Target 所封装的 View 是否被复用,复用时先取消之前的请求,避免了不必要的请求,也能防止图片错位。如果没有之前绑定的请求,说明该 View 没有被复用,那么调用 buildRequest() 创建一个 Request 对象,然后通过 requestTracker.runRequest 交给 RequestTracker 去执行。 下面先看下 Request 是如何构建出来的 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950private Request buildRequest(Target<TranscodeType> target) { if (priority == null) { priority = Priority.NORMAL; } return buildRequestRecursive(target, null);}private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) { if (thumbnailRequestBuilder != null) { if (isThumbnailBuilt) { throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, " + "consider using clone() on the request(s) passed to thumbnail()"); } // Recursive case: contains a potentially recursive thumbnail request builder. if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) { thumbnailRequestBuilder.animationFactory = animationFactory; } if (thumbnailRequestBuilder.priority == null) { thumbnailRequestBuilder.priority = getThumbnailPriority(); } if (Util.isValidDimensions(overrideWidth, overrideHeight) && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth, thumbnailRequestBuilder.overrideHeight)) { thumbnailRequestBuilder.override(overrideWidth, overrideHeight); } ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); // Guard against infinite recursion. isThumbnailBuilt = true; // Recursively generate thumbnail requests. Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse. ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // Base case: no thumbnail. ///parentCoordinator是null return obtainRequest(target, sizeMultiplier, priority, parentCoordinator); }} buildRequest() 方法内部调用了 buildRequestRecursive() 方法。buildRequestRecursive() 方法中有三个分支,前两个分支与略缩图有关,当前情景下会进入到第三个分支,仅仅是调用了 obtainRequest() 方法。 123456789101112131415161718192021222324252627private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain( loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy);} obtainRequest() 方法内部又去调用了 GenericRequest 静态的 obtain() 方法来获取 Request。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain( LoadProvider<A, T, Z, R> loadProvider, A model, Key signature, Context context, Priority priority, Target<R> target, float sizeMultiplier, Drawable placeholderDrawable, int placeholderResourceId, Drawable errorDrawable, int errorResourceId, Drawable fallbackDrawable, int fallbackResourceId, RequestListener<? super A, R> requestListener, RequestCoordinator requestCoordinator, Engine engine, Transformation<Z> transformation, Class<R> transcodeClass, boolean isMemoryCacheable, GlideAnimationFactory<R> animationFactory, int overrideWidth, int overrideHeight, DiskCacheStrategy diskCacheStrategy) { @SuppressWarnings("unchecked") GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll(); if (request == null) { request = new GenericRequest<A, T, Z, R>(); } request.init(loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderResourceId, errorDrawable, errorResourceId, fallbackDrawable, fallbackResourceId, requestListener, requestCoordinator, engine, transformation, transcodeClass, isMemoryCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); return request;} 如果没有可复用的 Request,那么将会创建一个 GenericRequest 实例,它是 Request 接口的实现。然后调用了该实例的 init() 方法,里面主要是做一些赋值操作,就不展开了。 现在我们拥有了一个 GenericRequest 实例,接下来它将交给 RequestTracker 的 runRequest() 方法执行。 12345678public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); }} RequestTracker 调用了 Request 实例的 begin() 方法 1234567891011121314151617181920212223242526public void begin() { startTime = LogTime.getLogTime(); if (model == null) { onException(null); return; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { ///如果使用了override() API为图片指定了一个固定的宽高 onSizeReady(overrideWidth, overrideHeight); } else { //没有指定 //内部使用Determiner先尝试获取view.getWidth/view.getHight,如果两者都为0,那么检查layoutparam是否为空,如果也为空,就在view的ViewTreeObserver上添加OnPreDrawListener,在回调方法中再次去检查。 // 在计算完之后,它也会调用onSizeReady()方法 target.getSize(this); } ///设置占位图 if (!isComplete() && !isFailed() && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } 上面这段代码主要尝试去确定当前请求的图片大小,同时给 Target 设置占位图。 当前图片请求的大小是否已经指定取决于是否使用了 override() 方法,如果已经指定则调用 onSizeReady() 做进一步操作,如果还没有指定,则调用 getSize() 方法来确定 ImageView 的实际大小。getSize() 方法内部使用 SizeDeterminer 辅助类,依次从 view.getWidth/view.getHight、layoutparam 获取,如果还不能确定,说明该 View 还没有 measure 完毕。那么就在该 View 的ViewTreeObserver 添加 OnPreDrawListener,当整个 view tree measure 完毕即将 draw 时会收到 onPreDraw() 方法回调,这时再重新确定 View 的大小。确定好大小后会调用 onSizeReady() 方法。 同时,会调用 getPlaceholderDrawable() 方法获取占位图 然后调用 Target 的 onLoadStarted() 方法来设置占位图,我们来看一下内部细节 123456789101112131415===GenericRequest.java===private Drawable getPlaceholderDrawable() { if (placeholderDrawable == null && placeholderResourceId > 0) { placeholderDrawable = context.getResources().getDrawable(placeholderResourceId); } return placeholderDrawable;}===ImageViewTarget.java===@Overridepublic void onLoadStarted(Drawable placeholder) { view.setImageDrawable(placeholder);} 最终会调用 ImageView 对象的 setImageDrawable() 方法,这就是通过 placeholder() 设置占位图的原理。上面 begin() 代码中的onException(null) 也是类似,看官们可以自行研究。 上面刚才说到确定图片大小之后会调用 onSizeReady() 方法,下面我们来看看它的内部逻辑 12345678910111213141516171819202122232425262728293031323334public void onSizeReady(int width, int height) { if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; width = Math.round(sizeMultiplier * width); height = Math.round(sizeMultiplier * height); ///loadProvider是一个FixedLoadProvider,返回ImageVideoModelLoader ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); ///返回ImageVideoFetcher,其封装了StreamStringLoader和FileDescriptorStringlLoader的fetcher final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); if (dataFetcher == null) { onException(new Exception("Failed to load model: \'" + model + "\'")); return; } ///GifBitmapWrapperDrawableTranscoder ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadedFromMemoryCache = true; loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this); loadedFromMemoryCache = resource != null; if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); }} 这段代码主要从 loadProvider 获取了 ModelLoader 和 ResourceTranscoder,然后传进 Engine 的 load() 方法中。 要读懂这段代码首先要明白 loadProvider 是什么东西?从何而来?经过层层回溯会发现,loadProvider 是从 DrawableTypeRequest 的构造函数传入的,我们回头看一下 DrawableTypeRequest 的构造函数 1234567891011DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader, ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide, RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) { super(context, modelClass, buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class, GlideDrawable.class, null), glide, requestTracker, lifecycle); this.streamModelLoader = streamModelLoader; this.fileDescriptorModelLoader = fileDescriptorModelLoader; this.optionsApplier = optionsApplier; } DrawableTypeRequest 的构造函数中 调用 buildProvider 创建了一个 FixedLoadProvider,并将它作为参数调用了父类的构造函数,我们来看一下 buildProvider() 12345678910111213141516171819202122232425///A代表Model类型:String Z代表Resource类型:GifBitmapWrapper R代表转码后类型:GlideDrawableprivate static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, ModelLoader<A, InputStream> streamModelLoader, ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass, Class<R> transcodedClass, ResourceTranscoder<Z, R> transcoder) { if (streamModelLoader == null && fileDescriptorModelLoader == null) { return null; } if (transcoder == null) { ///Glide 为 GifBitmapWrapper -> GlideDrawable 和 Bitmap -> GlideBitmapDrawable 都注册了ResourceTranscoder ///获取负责将 GifBitmapWrapper 转码成 GlideDrawable 的 ResourceTranscoder,这里获取到GifBitmapWrapperDrawableTranscoder transcoder = glide.buildTranscoder(resourceClass, transcodedClass); } /// DataLoadProvider 用于图片编解码,此处获取 ImageVideoWrapper 与 GifBitmapWrapper 编解码的 DataLoadProvider // 得到的实例是ImageVideoGifDrawableLoadProvider DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class, resourceClass); ///封装了 StreamStringLoader 和 FileDescriptorStringLoader ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader, fileDescriptorModelLoader); ///这个也就是 onSizeReady() 方法中的 loadProvider return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);} 这段代码已经附上了比较详细的注释。首先说明一下,DataLoadProvider 是一个接口,接口声明如下 123456789101112131415161718192021222324252627282930/** * A load provider that provides the necessary encoders and decoders to decode a specific type of resource from a * specific type of data. * * @param <T> The type of data the resource will be decoded from. * @param <Z> The type of resource that will be decoded. */public interface DataLoadProvider<T, Z> { /** * Returns the {@link com.bumptech.glide.load.ResourceDecoder} to use to decode the resource from the disk cache. */ ResourceDecoder<File, Z> getCacheDecoder(); /** * Returns the {@link com.bumptech.glide.load.ResourceDecoder} to use to decode the resource from the original data. */ ResourceDecoder<T, Z> getSourceDecoder(); /** * Returns the {@link com.bumptech.glide.load.Encoder} to use to write the original data to the disk cache. */ Encoder<T> getSourceEncoder(); /** * Returns the {@link com.bumptech.glide.load.ResourceEncoder} to use to write the decoded and transformed resource * to the disk cache. */ ResourceEncoder<Z> getEncoder();} 一个 DataLoadProvider 实例负责提供 Data 和 Resource 之间的编解码,解码表示从 Date 转化为 Resource 的过程,编码表示将 Date 或者 Resource 持久化为本地文件的过程,以实现磁盘缓存。所以 DataLoadProvider 提供了四个方法: getCacheDecoder getSourceDecoder getSourceEncoder getEncoder 返回类型 ResourceDecoder ResourceDecoder Encoder ResourceEncoder 转化前 File Data Data Resource 转化后 Resource Resource File File 这样一个 DataLoadProvider 实例就包办 Resource 的解码和缓存了。 Glide 提供了丰富的 DataLoadProvider 实例来实现各种类型之间的编解码 这里还要提一个接口 LoadProvider,它继承了 DataLoadProvider 接口,并新增了两个方法 12345678910111213public interface LoadProvider<A, T, Z, R> extends DataLoadProvider<T, Z> { /** * Returns the {@link com.bumptech.glide.load.model.ModelLoader} to convert from the given model to a data type. */ ModelLoader<A, T> getModelLoader(); /** * Returns the {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} to convert from the decoded * and transformed resource into the transcoded resource. */ ResourceTranscoder<Z, R> getTranscoder();} LoadProvider 接口新增了获取 ModelLoader 和 ResourceTranscoder 的两个方法。我们当前讨论的主角 FixedLoadProvider 就是 LoadProvider 的实例,它是多个 DataLoadProvider 实例的顶层封装 ,它与大部分的 DataLoadProvider 实例有着紧密的联系,它们之间的依赖关系如下 所以说,FixLoadProvider 提供了图片原始数据的获取、解码、编码、转码的一条龙服务! 回到 buildProvider() 那段代码,buildProvider() 方法中获取了 GifBitmapWrapperDrawableTranscoder 、ImageVideoGifDrawableLoadProvider 和 ImageVideoModelLoader,然后作为参数构建了一个 FixLoadProvider,这个也就是 onSizeReady() 方法中的 loadProvider 实例。 了解了 FixLoadProvider 之后,我们就可以回过头重新看看 onSizeReady() 方法了,这里重新贴一下 12345678910111213141516171819202122232425262728293031323334public void onSizeReady(int width, int height) { if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; width = Math.round(sizeMultiplier * width); height = Math.round(sizeMultiplier * height); ///loadProvider是一个FixedLoadProvider,返回ImageVideoModelLoader ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); ///返回ImageVideoFetcher,其封装了StreamStringLoader和FileDescriptorStringlLoader的fetcher final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); if (dataFetcher == null) { onException(new Exception("Failed to load model: \'" + model + "\'")); return; } ///GifBitmapWrapperDrawableTranscoder ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadedFromMemoryCache = true; loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this); loadedFromMemoryCache = resource != null; if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); }} 这里从 loadProvider 获取了刚刚提到的 ImageVideoModelLoader 和 GifBitmapWrapperDrawableTranscoder,还有从 ImageVideoModelLoader 中获取一个 DataFetcher 实例。getResourceFetcher() 是 ModelLoader 接口的唯一一个方法,返回一个真正执行原始数据获取的一个 DataFetcher 实例。最后 onSizeReady() 将一系列值传进 Engin 的 load() 方法中。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162/** * Responsible for starting loads and managing active and cached resources. */public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, EngineResource.ResourceListener { public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); final String id = fetcher.getId(); EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder()); EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } ///EngineJob的主要作用就是用来开启线程的,为后面的异步加载图片做准备 EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key, engineJob); ///将GenericRequest的回调接口交给enginejob engineJob.addCallback(cb); ///实际上就是让EngineRunnable的run()方法在子线程当中执行 engineJob.start(runnable); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }} loadFromCache() 和 loadFromActiveResources() 与内存缓存有关,不是本文的重点,我们关注最下面这段就可以了。这里分别创建了 EngineJob 和 DecodeJob,并用来构建 EngineRunnable。 EngineJob 负责统一管理加载请求的 ResourceCallback,EngineJob 本身也实现了 ResourceCallback 接口,当加载请求完成时 EngineRunnable 回调 EngineJob 的 onResourceReady() 方法,EngineJob 在分发给所有的监听者。 DecodeJob 的工作特别繁重,负责了 Resource 的所有解码工作,包括从 Data 解码和从缓存解码,同时承担了解码之后的转换和转码工作。 最后把 EngineRunnable 交给 Engine 的 start() 方法执行。 1234public void start(EngineRunnable engineRunnable) { this.engineRunnable = engineRunnable; future = diskCacheService.submit(engineRunnable); } 这里交给了线程池去处理,那么 EngineRunnable 的 run() 方法将在子线程中运行。 1234567891011121314151617181920212223242526272829303132333435class EngineRunnable implements Runnable, Prioritized { @Override public void run() { if (isCancelled) { return; } Exception exception = null; Resource<?> resource = null; try { ///最后会得到Resource<GlideDrawable>实例 resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } if (isCancelled) { if (resource != null) { resource.recycle(); } return; } if (resource == null) { /// 第一次走decode()尝试从磁盘获取,失败后会走这里。重新进入decode onLoadFailed(exception); } else { onLoadComplete(resource); } }} 这段代码的重点是 decode() 方法,它将返回一个 Resource 实例对象,那么我们看看它的内部逻辑 123456789private Resource<?> decode() throws Exception { if (isDecodingFromCache()) { ///从磁盘缓存中decode图片,第一次会走这 return decodeFromCache(); } else { ///从源中decode图片,第二次会走这 return decodeFromSource(); } } 第一次进入该方法时 isDecodingFromCache() 返回 true,Glide 会尝试从磁盘缓存解码图片。本文关注的重点是图片加载的基本流程,怎么从无到有加载一张图片,我们来看下 decodeFromSource() 方法 123private Resource<?> decodeFromSource() throws Exception { return decodeJob.decodeFromSource();} 这里交给 DecodeJob 去处理 123456public Resource<Z> decodeFromSource() throws Exception { ///返回Resource<GifBitmapWrapper> Resource<T> decoded = decodeSource(); ///返回了Resource<GlideDrawable>,统一了静态图和动图类型 return transformEncodeAndTranscode(decoded);} decodeFromSource() 方法的工作分为两部分:从数据源获取图片,返回封装类 Resource;转化和转码。虽然看似简单,但是每一步都包含大量的逻辑。先看看 decodeSource() 12345678910111213141516171819private Resource<T> decodeSource() throws Exception { Resource<T> decoded = null; try { long startTime = LogTime.getLogTime(); ///ImageVideoFetcher,返回一个ImageVideoWrapper final A data = fetcher.loadData(priority); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Fetched data", startTime); } if (isCancelled) { return null; } ///返回 Resource<GifBitmapWrapper> decoded = decodeFromSourceData(data); } finally { fetcher.cleanup(); } return decoded;} 这里首先使用 DataFetcher 的 loadData() 获取图片的原始数据,InputStream 或者是 ParcelFileDescriptor。然后调用 decodeFromSourceData()进行解码。这里的 DataFetcher 实例其实就是 FixLoaderProvider 提供的 ModelLoader 所提供的 DataFetcher 实例,实际上是 ImageVideoFetcher 类型,我们看看它的 loadData() 方法 123456789101112131415161718192021222324252627282930313233343536373839404142434445static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> { private final DataFetcher<InputStream> streamFetcher; private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher; public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher, DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) { this.streamFetcher = streamFetcher; this.fileDescriptorFetcher = fileDescriptorFetcher; } @SuppressWarnings("resource") // @see ModelLoader.loadData @Override public ImageVideoWrapper loadData(Priority priority) throws Exception { InputStream is = null; if (streamFetcher != null) { try { ///HttpUrlFetcher is = streamFetcher.loadData(priority); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e); } if (fileDescriptorFetcher == null) { throw e; } } } ParcelFileDescriptor fileDescriptor = null; if (fileDescriptorFetcher != null) { try { fileDescriptor = fileDescriptorFetcher.loadData(priority); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception fetching ParcelFileDescriptor", e); } if (is == null) { throw e; } } } ///用来封装服务器返回的InputStream return new ImageVideoWrapper(is, fileDescriptor); } } ImageVideoFetcher 内部的 streamFetcher 实际上是一个 HttpUrlFetcher 对象,调用了它的 loadData() 获取 InputStream 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768public class HttpUrlFetcher implements DataFetcher<InputStream> { @Override public InputStream loadData(Priority priority) throws Exception { return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders()); } private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException { if (redirects >= MAXIMUM_REDIRECTS) { throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!"); } else { // Comparing the URLs using .equals performs additional network I/O and is generally broken. // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html. try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new IOException("In re-direct loop"); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(2500); urlConnection.setReadTimeout(2500); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (statusCode / 100 == 2) { return getStreamForSuccessfulRequest(urlConnection); } else if (statusCode / 100 == 3) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new IOException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else { if (statusCode == -1) { throw new IOException("Unable to retrieve response code from HttpUrlConnection."); } throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage()); } } private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws IOException { if (TextUtils.isEmpty(urlConnection.getContentEncoding())) { int contentLength = urlConnection.getContentLength(); stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength); } else { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding()); } stream = urlConnection.getInputStream(); } return stream; }} loadData() 方法内部又去调用了 loadDataWithRedirects() 方法,loadDataWithRedirects() 方法内部使用 UrlConnection 获取了从服务器返回的 InputStream。至此,Glide 根据 URL 的字符串向服务器发起请求并成功收到响应,万里长征才算是刚刚完成一半。 回到 loadData() 方法,收到服务器返回的 InputStream 之后,用它来构建一个封装类 ImageVideoWrapper 并返回。继续向上返回到 decodeSource() 方法,接下来调用 decodeFromSourceData() 进行解码 1234567891011121314151617private Resource<T> decodeFromSourceData(A data) throws IOException { final Resource<T> decoded; ///判断磁盘缓存策略中是否缓存 Source,缓存 ImageVideoWrapper 并 decode if (diskCacheStrategy.cacheSource()) { decoded = cacheAndDecodeSourceData(data); } else { ///loadProvider就是刚才在onSizeReady()方法中得到的FixedLoadProvider, // 而getSourceDecoder()得到的则是一个GifBitmapWrapperResourceDecoder对象, // 得到Resource<GifBitmapWrapper> long startTime = LogTime.getLogTime(); decoded = loadProvider.getSourceDecoder().decode(data, width, height); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Decoded from source", startTime); } } return decoded;} 我们来看不进行缓存的分支,loadProvider.getSourceDecoder() 获取一个 SourceDecoder,我们分析过,会得到一个 GifBitmapWrapperResourceDecoder 对象,解码工作交给它来执行,我们看看它的 decode() 方法 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException { ByteArrayPool pool = ByteArrayPool.get(); byte[] tempBytes = pool.getBytes(); GifBitmapWrapper wrapper = null; try { wrapper = decode(source, width, height, tempBytes); } finally { pool.releaseBytes(tempBytes); } return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;}private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException { final GifBitmapWrapper result; if (source.getStream() != null) { ///准备从服务器返回的流当中读取数据 result = decodeStream(source, width, height, bytes); } else { result = decodeBitmapWrapper(source, width, height); } return result;}///decodeStream()方法中会先从流中读取2个字节的数据,来判断这张图是GIF图还是普通的静图,// 如果是GIF图就调用decodeGifWrapper()方法来进行解码,如果是普通的静图就用调用decodeBitmapWrapper()方法来进行解码private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException { InputStream bis = streamFactory.build(source.getStream(), bytes); bis.mark(MARK_LIMIT_BYTES); ImageHeaderParser.ImageType type = parser.parse(bis); bis.reset(); GifBitmapWrapper result = null; if (type == ImageHeaderParser.ImageType.GIF) { result = decodeGifWrapper(bis, width, height); } // Decoding the gif may fail even if the type matches. if (result == null) { // We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to // pass in a new source containing the buffered stream rather than the original stream. ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor()); result = decodeBitmapWrapper(forBitmapDecoder, width, height); } return result;}private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException { GifBitmapWrapper result = null; ///这个bitmapDecoder是一个ImageVideoBitmapDecoder对象.将InputStream或者ParcelFileDescriptor转换成bitmap Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height); if (bitmapResource != null) { result = new GifBitmapWrapper(bitmapResource, null); } return result;}private GifBitmapWrapper decodeGifWrapper(InputStream bis, int width, int height) throws IOException { GifBitmapWrapper result = null; ///GifResourceDecoder Resource<GifDrawable> gifResource = gifDecoder.decode(bis, width, height); if (gifResource != null) { GifDrawable drawable = gifResource.get(); // We can more efficiently hold Bitmaps in memory, so for static GIFs, try to return Bitmaps // instead. Returning a Bitmap incurs the cost of allocating the GifDrawable as well as the normal // Bitmap allocation, but since we can encode the Bitmap out as a JPEG, future decodes will be // efficient. if (drawable.getFrameCount() > 1) { result = new GifBitmapWrapper(null /*bitmapResource*/, gifResource); } else { Resource<Bitmap> bitmapResource = new BitmapResource(drawable.getFirstFrame(), bitmapPool); result = new GifBitmapWrapper(bitmapResource, null /*gifResource*/); } } return result;} decode() 方法内部调用了重载的 decode() 方法,其内部调用了 decodeStream() 将 InputStream 解码成图片。 decodeStream() 方法中会从流中读取2个字节的数据来判断这张图是 Gif 还是普通的静图,如果是 Gif 图则调用 decodeGifWrapper() 方法来进行解码,如果是普通的静图就用调用 decodeBitmapWrapper() 方法来进行解码。这里选取比较常见的静图解码来分析。 decodeBitmapWrapper() 方法内部调用 bitmapDecoder 对象的 decode() 方法,然后封装成一个GifBitmapWrapper。bitmapDecoder 实际上是一个 ImageVideoBitmapDecoder 对象,来看一下它的 decode() 方法。 12345678910111213141516171819202122232425262728public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> { @SuppressWarnings("resource") // @see ResourceDecoder.decode @Override public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException { Resource<Bitmap> result = null; InputStream is = source.getStream(); if (is != null) { try { ///streamDecode是一个StreamBitmapDecoder对象 result = streamDecoder.decode(is, width, height); } catch (IOException e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e); } } } if (result == null) { ParcelFileDescriptor fileDescriptor = source.getFileDescriptor(); if (fileDescriptor != null) { result = fileDescriptorDecoder.decode(fileDescriptor, width, height); } } return result; }} ImageVideoBitmapDecoder 的 decode() 方法内部又将解码工作交给了 StreamBitmapDecoder 对象 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889===StreamBitmapDecoder.java===public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> { @Override public Resource<Bitmap> decode(InputStream source, int width, int height) { Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat); return BitmapResource.obtain(bitmap, bitmapPool); }}===Downsampler.java===public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) { final ByteArrayPool byteArrayPool = ByteArrayPool.get(); final byte[] bytesForOptions = byteArrayPool.getBytes(); final byte[] bytesForStream = byteArrayPool.getBytes(); final BitmapFactory.Options options = getDefaultOptions(); // Use to fix the mark limit to avoid allocating buffers that fit entire images. RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream( is, bytesForStream); // Use to retrieve exceptions thrown while reading. // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine // if a Bitmap is partially decoded, consider removing. ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream); // Use to read data. // Ensures that we can always reset after reading an image header so that we can still attempt to decode the // full image even when the header decode fails and/or overflows our read buffer. See #283. MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream); try { exceptionStream.mark(MARK_POSITION); int orientation = 0; try { orientation = new ImageHeaderParser(exceptionStream).getOrientation(); } catch (IOException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Cannot determine the image orientation from header", e); } } finally { try { exceptionStream.reset(); } catch (IOException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Cannot reset the input stream", e); } } } options.inTempStorage = bytesForOptions; ///编码过程中通过设置采样率缩放图片,降低内存占用,提高加载性能。 final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options); final int inWidth = inDimens[0]; final int inHeight = inDimens[1]; final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation); final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight); final Bitmap downsampled = downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize, decodeFormat); // BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch // and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps, // we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here. final Exception streamException = exceptionStream.getException(); if (streamException != null) { throw new RuntimeException(streamException); } Bitmap rotated = null; if (downsampled != null) { rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation); if (!downsampled.equals(rotated) && !pool.put(downsampled)) { downsampled.recycle(); } } return rotated; } finally { byteArrayPool.releaseBytes(bytesForOptions); byteArrayPool.releaseBytes(bytesForStream); exceptionStream.release(); releaseOptions(options); } } 经过层层跋涉,可以看到最终的解码工作就在 Downsampler 的 decode() 方法这里了。里面包含很多的逻辑,包括对图片的压缩,甚至还有旋转、圆角等逻辑处理。总之,decode() 最终返回了一个 Bitmap 对象。然后向上层层返回,在 StreamBitmapDecoder 的 decode() 方法中 Bitmap 被封装成 Resource\ 对象。在 GifBitmapWrapperResourceDecoder 的 decodeBitmapWrapper() 方法中Resource\ 进而被封装成 GifBitmapWrapper 对象。最后在 GifBitmapWrapperResourceDecoder 的 decode() 方法中封装成 Resource\ 对象。 有点混乱,这里用图表整理一下 方法 返回值 封装内容 GifBitmapWrapperResourceDecoder.decode() Resource\ GifBitmapWrapper GifBitmapWrapperResourceDecoder.decodeBitmapWrapper() GifBitmapWrapper Resource\和Resource\ StreamBitmapDecoder.decode() Resource\ Bitmap Downsampler.decode() Bitmap 可以看到,不管加载的是静图还是Gif动图,都能通过返回 Resource\ 对象来表示 最后经过千辛万苦,返回到了 DecodeJob 的 decodeFromSource() 方法中,得到 Resource\ 对象,解码工作完成。 123456public Resource<Z> decodeFromSource() throws Exception { ///返回Resource<GifBitmapWrapper> Resource<T> decoded = decodeSource(); ///返回了Resource<GlideDrawable>,统一了静态图和动图类型 return transformEncodeAndTranscode(decoded); } 接下来就是转化和转码的工作了,来看下 transformEncodeAndTranscode() 方法 123456789101112131415161718private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) { long startTime = LogTime.getLogTime(); Resource<T> transformed = transform(decoded); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transformed resource from source", startTime); } ///将transform后的resource写入缓存 writeTransformedToCache(transformed); startTime = LogTime.getLogTime(); ///返回了Resource<GlideDrawable> Resource<Z> result = transcode(transformed); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transcoded transformed from source", startTime); } return result; } 同样写入缓存的部分略过,transformEncodeAndTranscode() 的主要工作包括两部分,调用 transform() 方法进行图片的变换,然后调用 transcode() 方法进行转码。先来看下 transform() 方法 12345678910111213private Resource<T> transform(Resource<T> decoded) { if (decoded == null) { return null; } ///GenericRequestBuilder.into中根据imageview的scaletype保存了transformation, // 比如是drawable的centercrop那么transformation就是GifBitmapWrapperTransformation(内部封装类型为CenterCrop的BitmapTransformation对象) Resource<T> transformed = transformation.transform(decoded, width, height); if (!decoded.equals(transformed)) { decoded.recycle(); } return transformed;} 方法内部调用了 transformation 对象的 transform 来执行图片变换。还记得 GenericRequestBuilder 的 into() 方法么?当时就判断了 ImageView 的 ScaleType,将对应的 Transformation 实例保存在 transformation,这里的 transformation 对象实际上就是由 GenericRequestBuilder 传过来的。 假设当前 ImageView 的 ScaleType 是 CenterCrop,那么得到 transformation 对象就是 GifBitmapWrapperTransformation,其内部封装了用于 Bitmap 变换 的 Transformation 实例 CenterCrop 对象,还有用于 GifDrawable 变换的 Transformation 实例 GifDrawableTransformation 对象。 那么我们看看 GifBitmapWrapperTransformation 的 transform() 方法 123456789101112131415161718192021222324252627282930313233public class GifBitmapWrapperTransformation implements Transformation<GifBitmapWrapper> { public GifBitmapWrapperTransformation(BitmapPool bitmapPool, Transformation<Bitmap> bitmapTransformation) { this(bitmapTransformation, new GifDrawableTransformation(bitmapTransformation, bitmapPool)); } GifBitmapWrapperTransformation(Transformation<Bitmap> bitmapTransformation, Transformation<GifDrawable> gifDataTransformation) { this.bitmapTransformation = bitmapTransformation; this.gifDataTransformation = gifDataTransformation; } @Override public Resource<GifBitmapWrapper> transform(Resource<GifBitmapWrapper> resource, int outWidth, int outHeight) { Resource<Bitmap> bitmapResource = resource.get().getBitmapResource(); Resource<GifDrawable> gifResource = resource.get().getGifResource(); if (bitmapResource != null && bitmapTransformation != null) { ///FitCenter 或 CenterCrop Resource<Bitmap> transformed = bitmapTransformation.transform(bitmapResource, outWidth, outHeight); if (!bitmapResource.equals(transformed)) { GifBitmapWrapper gifBitmap = new GifBitmapWrapper(transformed, resource.get().getGifResource()); return new GifBitmapWrapperResource(gifBitmap); } } else if (gifResource != null && gifDataTransformation != null) { Resource<GifDrawable> transformed = gifDataTransformation.transform(gifResource, outWidth, outHeight); if (!gifResource.equals(transformed)) { GifBitmapWrapper gifBitmap = new GifBitmapWrapper(resource.get().getBitmapResource(), transformed); return new GifBitmapWrapperResource(gifBitmap); } } return resource; }} GifBitmapWrapperTransformation 是一个 Transformation\,它变换的对象是 GifBitmapWrapper,自然也就需要执行 Bitmap 或者 GifDrawable 的变换。首先判断 GifBitmapWrapper 中封装的是静图 Bitmap 还是 动图 Gif,然后再交给对应的 Transformation 实例做变换。如果 ImageView 的 ScaleType 是 CenterCrop,那么此处的 bitmapTransformation 就是 CenterCrop 对象,那么我们来看看 CenterCrop 的 transform() 方法 12345678910111213141516public class CenterCrop extends BitmapTransformation { // Bitmap doesn't implement equals, so == and .equals are equivalent here. @SuppressWarnings("PMD.CompareObjectsWithEquals") @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { ///从 BitmapPool 中获取可复用的 Bitmap final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null ? toTransform.getConfig() : Bitmap.Config.ARGB_8888); Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) { toReuse.recycle(); } return transformed; }} transform() 方法最终调用了 TransformationUtils 的静态方法 centerCrop() 去处理 1234567891011121314151617181920212223242526272829303132333435public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) { if (toCrop == null) { return null; } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) { return toCrop; } // From ImageView/Bitmap.createScaledBitmap. final float scale; float dx = 0, dy = 0; Matrix m = new Matrix(); if (toCrop.getWidth() * height > width * toCrop.getHeight()) { scale = (float) height / (float) toCrop.getHeight(); dx = (width - toCrop.getWidth() * scale) * 0.5f; } else { scale = (float) width / (float) toCrop.getWidth(); dy = (height - toCrop.getHeight() * scale) * 0.5f; } m.setScale(scale, scale); m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); final Bitmap result; if (recycled != null) { result = recycled; } else { result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop)); } // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given. TransformationUtils.setAlpha(toCrop, result); Canvas canvas = new Canvas(result); Paint paint = new Paint(PAINT_FLAGS); canvas.drawBitmap(toCrop, m, paint); return result; } centerCrop() 方法就是根据给定的宽和高来缩放 toCrop Bitmap,使得 bitmap 的其中一边与给定宽高相等,而另一边较长,然后进行截取。那么经过 centerCrop() 方法处理,我们就得到了一个经过变换的 Bitmap。一直向上返回,一直回到 transformEncodeAndTranscode() 方法 123456789101112131415161718private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) { long startTime = LogTime.getLogTime(); Resource<T> transformed = transform(decoded); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transformed resource from source", startTime); } ///将transform后的resource写入缓存 writeTransformedToCache(transformed); startTime = LogTime.getLogTime(); ///返回了Resource<GlideDrawable> Resource<Z> result = transcode(transformed); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transcoded transformed from source", startTime); } return result; } transformEncodeAndTranscode() 方法的工作已经完成一半,目前我们得到的还是 Resource\ 对象,只不过 其中封装的 Resource\ 已经根据 ScaleType 进行了变换。 然后转码的功过就是在 transcode()方法内部了,要注意的是它将返回的是 Resource\ 对象 1234567private Resource<Z> transcode(Resource<T> transformed) { if (transformed == null) { return null; } ///GifBitmapWrapperDrawableTranscoder return transcoder.transcode(transformed); } 方法内部又调用了 transcoder 的 transcode() 方法。这里的 transcoder 是什么呢?让我们追本溯源,发现答案就在 DrawableRequestBuilder 的构造函数。 当时在 DrawableRequestBuilder 的构造函数中创建了一个 FixedLoadProvider 对象,创建 FixedLoadProvider 对象的时候,在它的构造函数里传了一个 GifBitmapWrapperDrawableTranscoder 对象,后来这个 GifBitmapWrapperDrawableTranscoder 不远千里,途经 GenericRequest、Engine 来到 DecodeJob。所以 transcoder 就是 GifBitmapWrapperDrawableTranscoder 对象。 那么我们看看它的 transcode 方法 1234567891011121314151617181920212223242526///GifBitmapWrapperDrawableTranscoder的核心作用就是用来转码的。// 因为GifBitmapWrapper是无法直接显示到ImageView上面的,只有Bitmap或者Drawable才能显示到ImageView上。// 因此,这里的transcode()方法先从Resource<GifBitmapWrapper>中取出GifBitmapWrapper对象,然后再从GifBitmapWrapper中取出Resource<Bitmap>对象。public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> { ///转码之后,因为不管是静图的Resource<GlideBitmapDrawable>对象, // 还是动图的Resource<GifDrawable>对象,它们都是属于父类Resource<GlideDrawable>对象的 @SuppressWarnings("unchecked") @Override public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) { GifBitmapWrapper gifBitmap = toTranscode.get(); Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource(); final Resource<? extends GlideDrawable> result; if (bitmapResource != null) { ///说明加载的是bitmap,需要再做一次转码,将Bitmap转换成Drawable对象.因为要保证静图和动图的类型一致性,不然逻辑上是不好处理的。 ///使用GlideBitmapDrawableTranscoder将bitmap转化成Resource<GlideBitmapDrawable> result = bitmapDrawableResourceTranscoder.transcode(bitmapResource); } else { ///说明此时加载的是GIF图.因为Glide用于加载GIF图片是使用的GifDrawable这个类,它本身就是一个Drawable对象了 result = gifBitmap.getGifResource(); } // This is unchecked but always safe, anything that extends a Drawable can be safely cast to a Drawable. return (Resource<GlideDrawable>) result; }} 转码的目的是为了统一静图和动图 Gif 的类型,是它们都属于 Drawable 类型,以便统一处理逻辑。Glide 中 Gif 动图用 GifDrawable 表示,本身就是 Drawable 的子类,所以不需要转码。而静图 Bitmap 则需要转码为 Drawable。 程序先从 Resource\ 取出 GifBitmapWrapper,然后在 GifBitmapWrapper 中取出 Resource\。对于 Bitmap 的转码,在 transcode() 方法中交给了 bitmapDrawableResourceTranscoder 处理,bitmapDrawableResourceTranscoder 是一个 GlideBitmapDrawableTranscoder 对象,下面看看它的 transcode() 方法 12345678public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> { @Override public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) { GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get()); return new GlideBitmapDrawableResource(drawable, bitmapPool); }} 这里 创建了一个 GlideBitmapDrawable,然后把 Bitmap 封装到里面,然后对 GlideBitmapDrawable 再一次封装,返回一个 Resource\ 对象。 现在不管是静图 Resource\ 还是动图 Resource\ ,它们都属于父类Resource\对象。 那么现在转码的工作也已经完成了,只剩下将图片显示出来的工作了。从 DecodeJob 的 transformEncodeAndTranscode() 方法开始向上返回,一直返回到 EngineRunnable 的 run() 方法,我们重新看一下代码 1234567891011121314151617181920212223242526272829303132@Override public void run() { if (isCancelled) { return; } Exception exception = null; Resource<?> resource = null; try { ///得到了Resource<GlideDrawable>对象 resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } if (isCancelled) { if (resource != null) { resource.recycle(); } return; } if (resource == null) { /// 第一次走decode()尝试从磁盘获取,失败后会走这里。重新进入decode onLoadFailed(exception); } else { onLoadComplete(resource); } } 上次我们从 decode() 方法开始追溯,经过向服务发送请求,接收 InputStream,将 InputStream 解码成 Bitmap 或者 GifDrawable,然后经过 ScaleType 变换,转码 的一系列复杂逻辑,现在终于从 decode() 方法出来了,真是恍如隔世,现在我们拥有了 Resource\对象,准备将图片显示出来。 显然接下来就是调用 onLoadComplete() 方法了 1234private void onLoadComplete(Resource resource) { ///manager就是EngineJob对象 manager.onResourceReady(resource); } 只有简单的一行代码,这个 manager 实际上是 EngineJob 对象,上次提到它是负责统一管理加载请求的 onResourceReady() 回调的,那么我们来看一下 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869class EngineJob implements EngineRunnable.EngineRunnableManager { private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback()); public void addCallback(ResourceCallback cb) { Util.assertMainThread(); if (hasResource) { cb.onResourceReady(engineResource); } else if (hasException) { cb.onException(exception); } else { cbs.add(cb); } } @Override public void onResourceReady(final Resource<?> resource) { this.resource = resource; ///使用Handler发出了一条MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就会收到这条消息。 // 从这里开始,所有的逻辑又回到主线程当中进行了 MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget(); } private void handleResultOnMainThread() { if (isCancelled) { resource.recycle(); return; } else if (cbs.isEmpty()) { throw new IllegalStateException("Received a resource without any callbacks to notify"); } ///封装成一个EngineResource engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it // synchronously released by one of the callbacks. engineResource.acquire(); ///key是一个EngineKey。如果resource.isCacheable()为true,resource将添加到activeResources中,并在移除时加入到memorycache listener.onEngineJobComplete(key, engineResource); ///这里的ResourceCallback就是GenericRequest,由GenericRequest调用EngineJob的load方法时传入 for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); ///回调GenericRequest cb.onResourceReady(engineResource); } } // Our request is complete, so we can release the resource. engineResource.release(); } private static class MainThreadCallback implements Handler.Callback { @Override public boolean handleMessage(Message message) { if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) { EngineJob job = (EngineJob) message.obj; if (MSG_COMPLETE == message.what) { job.handleResultOnMainThread(); } else { job.handleExceptionOnMainThread(); } return true; } return false; } }} 这里贴上了相关的代码。在 onResourceReady() 方法中 使用 MAIN_THREAD_HANDLER 发送了一条 MSG_COMPLETE 消息。MAIN_THREAD_HANDLER 是用 Looper.getMainLooper() 构建的一个 Handler 对象,那么 MainThreadCallback 的 handleMessage() 将会在主线程中运行。这一意味着即将要更新 UI 了。 handleResultOnMainThread() 方法中通过循环,遍历了所有 ResourceCallback 的 onResourceReady() 方法。这些 ResourceCallback 都是谁呢?看看谁调用过 EngineJob 的 addCallback() 方法就知道了。其实我们都已经见过了 12345678910111213141516171819202122232425262728293031public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { ...... EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } ///EngineJob的主要作用就是用来开启线程的,为后面的异步加载图片做准备 EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key, engineJob); ///将GenericRequest的回调接口交给enginejob engineJob.addCallback(cb); ///实际上就是让EngineRunnable的run()方法在子线程当中执行了 engineJob.start(runnable); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); } 在 Engine 的 load() 方法中,在运行 EngingRunnale 之前,使用 cb 参数调用了 EngineJob 的 addCallback() 方法,以便图片加载完毕之后收到回调。而 cb 就是 GenericRequest 调用 Engine 的 load() 方法时传过来的它自己本身,因为 GenericRequest 本身就实现了 ResourceCallback 接口。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253@SuppressWarnings("unchecked")@Overridepublic void onResourceReady(Resource<?> resource) { if (resource == null) { onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass + " inside, but instead got null.")); return; } ///resource是一个EngineResource,获取到GlideBitmapDrawable 或者是 GifDrawable Object received = resource.get(); if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) { releaseResource(resource); onException(new Exception("Expected to receive an object of " + transcodeClass + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}" + " inside Resource{" + resource + "}." + (received != null ? "" : " " + "To indicate failure return a null Resource object, " + "rather than a Resource object containing null data.") )); return; } if (!canSetResource()) { releaseResource(resource); // We can't set the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } onResourceReady(resource, (R) received);}private void onResourceReady(Resource<?> resource, R result) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache, isFirstResource)) { GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource); ///target就是调用into时构建的GlideDrawableImageViewTarget target.onResourceReady(result, animation); } notifyLoadSuccess(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: " + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache); }} 这里有 GenericRequest 中有两个 onResourceReady() 方法,第一个就是 ResourceCallback 接口的方法。该方法中首先调用了 EngineResource 的 get() 方法获取它所封装的 GlideBitmapDrawable 或者是 GifDrawable,然后调用了另一个 onResourceReady() 方法。 在另一个方法中调用了 target 的 onResourceReady() 方法,target 实际上就是我们调用第三步 into() 方法时传的 GlideDrawableImageViewTarget 对象。我们看看它的源码 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> { private static final float SQUARE_RATIO_MARGIN = 0.05f; private int maxLoopCount; private GlideDrawable resource; public GlideDrawableImageViewTarget(ImageView view) { this(view, GlideDrawable.LOOP_FOREVER); } public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) { super(view); this.maxLoopCount = maxLoopCount; } @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) { if (!resource.isAnimated()) { float viewRatio = view.getWidth() / (float) view.getHeight(); float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight(); if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) { resource = new SquaringDrawable(resource, view.getWidth()); } } super.onResourceReady(resource, animation); this.resource = resource; resource.setLoopCount(maxLoopCount); resource.start(); } /** * Sets the drawable on the view using * {@link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}. * * @param resource The {@link android.graphics.drawable.Drawable} to display in the view. */ @Override protected void setResource(GlideDrawable resource) { view.setImageDrawable(resource); } @Override public void onStart() { if (resource != null) { resource.start(); } } @Override public void onStop() { if (resource != null) { resource.stop(); } }} GlideDrawableImageViewTarget 的 onResourceReady() 方法中调用了父类的 onResourceReady() 方法 1234567public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) { if (glideAnimation == null || !glideAnimation.animate(resource, this)) { setResource(resource); }}protected abstract void setResource(Z resource); 父类的 onResourceReady() 方法调用了子类的 setResource() 实现,回头看一下,GlideDrawableImageViewTarget 的 setResource() 实现 调用了 view.setImageDrawable() 方法,这个 view 就是所封装的 ImageView 对象。图片也就成功显示出来了。 那么,Glide 加载基本流程的源码分析就到此结束。我已经尽力将它描述清楚,希望各位有所收获。 感谢坚持看到这里的你们。]]></content>
<categories>
<category>源码分析</category>
</categories>
<tags>
<tag>Glide</tag>
<tag>源码分析</tag>
</tags>
</entry>
<entry>
<title><![CDATA[听听,一款优雅的开源音乐播放器]]></title>
<url>%2F2017%2F04%2F15%2F%E5%90%AC%E5%90%AC%EF%BC%8C%E4%B8%80%E6%AC%BE%E4%BC%98%E9%9B%85%E7%9A%84%E5%BC%80%E6%BA%90%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8%2F</url>
<content type="text"><![CDATA[ListenerMusicPlayer一款优雅的遵循 Material Design 的开源音乐播放器,UI参考 腾讯轻听 音乐播放器,使用 Lastfm Api 与 酷狗歌词Api。项目架构采用 mvp-clean,基于 Retrofit2 + Dagger2 + RxJava + RxBus + Glide。 github地址:ListenerMusicPlayer 效果图 部分截图 gif演示 模块分析 我的歌曲:展示本地所有的音乐文件,由歌曲、歌手、专辑三个标签页分类展示,数据取自系统媒体库。为了防止媒体库数据与本地不同步,应用启动时会主动刷新媒体库。 正在播放:该模块体现为固定在每个页面下方的播放控制区,展开后可查看全部操作,包括标记喜欢、打开播放列表及查看歌词。歌词API由酷狗提供。 文件夹:展示本地所有包含音乐文件个文件夹 我的歌单:展示用户创建的歌单 我喜欢:展示用户标记为喜欢的歌曲 最近播放:展示用户最近播放的歌曲 最近添加:展示最近添加的歌曲 播放排行:综合歌曲过去每周的播放历史和当前周播放次数得出歌曲排行 本地搜索:根据关键词搜索相关的歌曲、专辑和歌手 设置:该模块包括切换日夜间模式和更换出题 主要技术点梳理那么,从本项目中你能学到哪些知识呢? 高仿轻听UI,最为酷炫的正在播放模块也得到了高度还原 使用 app-theme-engine 轻松实现日夜间切换和主题更换 使用独立进程负责播放,进程间AIDL通信 mvp-clean架构的使用 透明状态栏使用与版本适配 使用RxBus进行组件间通信 自定义View如LyricView、PlayPauseView的实现 通过自定义Behavior实现自定义FloatingActionButton进出动画 使用ScrimUtil实现更加平滑的单色渐变效果 使用RxJava对数据进行过滤和转化 Thanks 参考项目 : Timber 、 轻听 图片来源 : Material design icon 、 腾讯轻听App Api : LastFM 、 酷狗音乐 开源库 : RxJava 、 Retrofit 、 Glide 、 AndroidSlidingUpPanel等等 Statement感谢轻听提供参考,轻听是一款十分良心的音乐播放器,本人使用了其中的部分素材,如构成侵权请及时通知我修改或删除。部分数据来自于干LastFM和酷狗歌词Api,一切数据解释权都归LastFM和酷狗所有。 End 注意:此开源项目仅做学习交流使用,如果你觉得不错,对你有帮助,欢迎点个fork,star,follow,也可以帮忙分享给你更多的朋友,这是给我们最大的动力与支持。 Contact Me Github: github.com/hefuyicoder Email: [email protected]]]></content>
<categories>
<category>开源项目</category>
</categories>
<tags>
<tag>开源项目</tag>
<tag>music player</tag>
</tags>
</entry>
</search>