本文首发于 vivo互联网技术 微信公众号                  链接:                https://mp.weixin.qq.com/s/cPLkefpEb3w12-uoiqzTig作者:连凌能                Android上图片加载的存流程解决方案有多种,但是存流程官方认可的是Glide。Glide提供简洁易用的存流程api,整个框架也方便扩展,存流程比如可以替换网络请求库,存流程同时也提供了完备的存流程缓存机制,应用层不需要自己去管理图片的存流程缓存与获取,框架会分成内存缓存,存流程文件缓存和远程缓存。存流程本文不会从简单的存流程使用着手,会把重点放在缓存机制的存流程分析上。 一、存流程综述 开始之前,存流程关于Glide缓存请先思考几个问题: Glide有几级缓存?存流程 Glide内存缓存之间是什么关系? Glide本地文件IO和网络请求是一个线程吗?如果不是,怎么实现线程切换?存流程 Glide网络请求回来后数据直接返回给用户还是先存再返回? 加载开始入口从Engine.load()开始,先看下对这个方法的注释, 会先检查(Active Resources),如果有就直接返回,Active Resources没有被引用的资源会放入Memory Cache,如果Active Resources没有,IT技术网会往下走。 检查Memory Cache中是否有需要的资源,如果有就返回,Memory Cache中没有就继续往下走。 检查当前在运行中的job中是否有改资源的下载,有就在现有的job中直接添加callback返回,不重复下载,当然前提是计算得到的key是一致的,如果还是没有,就会构造一个新的job开始新的工作。 1                2                3                4                5                6                7                8                9                10                11                12                                    * Starts a load for the given arguments.                    *                    * <p>Must be called on the main thread.                    *                    * <p>The flow for any request is as follows:                    * <ul>                    *   <li>Check the current set of actively used resources, return the active resource if                    *   present, and move any newly inactive resources into the memory cache.</li>                    *   <li>Check the memory cache and provide the cached resource if present.</li>                    *   <li>Check the current set of in progress loads and add the cb to the in progress load if                    *   one is present.</li>                    *   <li>Start a new load.</li>                                ok, find the source code. 二、内存缓存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                52                53                54                55                56                57                58                59                60                61                62                63                64                65                66                67                68                69                70                71                72                73                74                75                76                77                78                79                80                81                82                83                84                                    public <R> LoadStatus load(                    GlideContext glideContext,                    Object model,                    Key signature,                    int width,                    int height,                    Class<?> resourceClass,                    Class<R> transcodeClass,                    Priority priority,                    DiskCacheStrategy diskCacheStrategy,                    Map<Class<?>, Transformation<?>> transformations,                    boolean isTransformationRequired,                    boolean isScaleOnlyOrNoTransform,                    Options options,                    boolean isMemoryCacheable,                    boolean useUnlimitedSourceExecutorPool,                    boolean useAnimationPool,                    boolean onlyRetrieveFromCache,                    ResourceCallback cb) {                    Util.assertMainThread();                    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;                    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,                    resourceClass, transcodeClass, options);                    // focus 1                    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);                    if (active != null) {                    cb.onResourceReady(active, DataSource.MEMORY_CACHE);                    if (VERBOSE_IS_LOGGABLE) {                    logWithTimeAndKey("Loaded resource from active resources", startTime, key);                    }                    return null;                    }                    // focus 2                    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);                    if (cached != null) {                    cb.onResourceReady(cached, DataSource.MEMORY_CACHE);                    if (VERBOSE_IS_LOGGABLE) {                    logWithTimeAndKey("Loaded resource from cache", startTime, key);                    }                    return null;                    }                    // focus 3                    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);                    if (current != null) {                    current.addCallback(cb);                    if (VERBOSE_IS_LOGGABLE) {                    logWithTimeAndKey("Added to existing load", startTime, key);                    }                    return new LoadStatus(cb, current);                    }                    EngineJob<R> engineJob =                    engineJobFactory.build(                    key,                    isMemoryCacheable,                    useUnlimitedSourceExecutorPool,                    useAnimationPool,                    onlyRetrieveFromCache);                    DecodeJob<R> decodeJob =                    decodeJobFactory.build(                    glideContext,                    model,                    key,                    signature,                    width,                    height,                    resourceClass,                    transcodeClass,                    priority,                    diskCacheStrategy,                    transformations,                    isTransformationRequired,                    isScaleOnlyOrNoTransform,                    onlyRetrieveFromCache,                    options,                    engineJob);                    jobs.put(key, engineJob);                    engineJob.addCallback(cb);                    // focus 4                    engineJob.start(decodeJob);                    if (VERBOSE_IS_LOGGABLE) {                    logWithTimeAndKey("Started new load", startTime, key);                    }                    return new LoadStatus(cb, engineJob);                    }                                先看到 focus 1,这一步会从 ActiveResources 中加载资源,首先判断是否使用内存缓存,否的话返回null;否则到 ActiveResources 中取数据: 1                2                3                4                5                6                7                8                9                10                11                12                13                                    // Engine.java                    @Nullable                    private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {                    if (!isMemoryCacheable) {                    return null;                    }                    EngineResource<?> active = activeResources.get(key);                    if (active != null) {                    active.acquire();                    }                    return active;                    }                                接下来看下ActiveResources, 其实是用过弱引用保存使用过的资源。 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                                    final class ActiveResources {                    ...                    private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() {                    @Override                    public boolean handleMessage(Message msg) {                    if (msg.what == MSG_CLEAN_REF) {                    cleanupActiveReference((ResourceWeakReference) msg.obj);                    return true;                    }                    return false;                    }                    });                    @VisibleForTesting                    final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();                    ...                    }                                成功取到数据后回调类型也是内存缓存: 1                2                3                4                5                                    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);                    if (cached != null) {                    cb.onResourceReady(cached, DataSource.MEMORY_CACHE);                    return null;                    }                                接着回到Engine.load()中继续看到focus 2,如果在cache中找到就是云服务器remove掉,然后返回EngineResource,其中需要EngineResource进行acquire一下,这个后面再看,然后会把资源移到ActiveResources中,也就是上面提到的缓存: 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                                    // Engine.java                    private final MemoryCache cache;                    private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {                    if (!isMemoryCacheable) {                    return null;                    }                    EngineResource<?> cached = getEngineResourceFromCache(key);                    if (cached != null) {                    cached.acquire();                    activeResources.activate(key, cached);                    }                    return cached;                    }                    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 weve cached an EngineResource (the typical case).                    result = (EngineResource<?>) cached;                    } else {                    result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);                    }                    return result;                    }                                其中cache是MemoryCache接口的实现,如果没设置,默认在build的时候是LruResourceCache, 也就是熟悉的LRU Cache:                 1                2                3                4                                    // GlideBuilder.java                    if (memoryCache == null) {                    memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());                    }                                再看下EngineResource,主要是对资源增加了引用计数的功能: 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                52                53                54                55                56                57                58                59                60                61                62                63                64                65                66                67                68                69                70                71                72                73                74                75                76                77                78                79                80                81                82                83                84                                    // EngineResource.java                    private final boolean isCacheable;                    private final boolean isRecyclable;                    private ResourceListener listener;                    private Key key;                    private int acquired;                    private boolean isRecycled;                    private final Resource<Z> resource;                    interface ResourceListener {                    void onResourceReleased(Key key, EngineResource<?> resource);                    }                    EngineResource(Resource<Z> toWrap, boolean isCacheable, boolean isRecyclable) {                    resource = Preconditions.checkNotNull(toWrap);                    this.isCacheable = isCacheable;                    this.isRecyclable = isRecyclable;                    }                    void setResourceListener(Key key, ResourceListener listener) {                    this.key = key;                    this.listener = listener;                    }                    Resource<Z> getResource() {                    return resource;                    }                    boolean isCacheable() {                    return isCacheable;                    }                    @NonNull                    @Override                    public Class<Z> getResourceClass() {                    return resource.getResourceClass();                    }                    @NonNull                    @Override                    public Z get() {                    return resource.get();                    }                    @Override                    public int getSize() {                    return resource.getSize();                    }                    @Override                    public void recycle() {                    if (acquired > 0) {                    throw new IllegalStateException("Cannot recycle a resource while it is still acquired");                    }                    if (isRecycled) {                    throw new IllegalStateException("Cannot recycle a resource that has already been recycled");                    }                    isRecycled = true;                    if (isRecyclable) {                    resource.recycle();                    }                    }                    void acquire() {                    if (isRecycled) {                    throw new IllegalStateException("Cannot acquire a recycled resource");                    }                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {                    throw new IllegalThreadStateException("Must call acquire on the main thread");                    }                    ++acquired;                    }                    void release() {                    if (acquired <= 0) {                    throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");                    }                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {                    throw new IllegalThreadStateException("Must call release on the main thread");                    }                    if (--acquired == 0) {                    listener.onResourceReleased(key, this);                    }                    }                                在release后会判断引用计数是否为0,如果是0就会回调onResourceReleased,在这里就是Engine,然后会把资源从ActiveResources中移除,资源默认是可缓存的,因此会把资源放到LruCache中。 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                                    // Engine.java                    @Override                    public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {                    Util.assertMainThread();                    activeResources.deactivate(cacheKey);                    if (resource.isCacheable()) {                    cache.put(cacheKey, resource);                    } else {                    resourceRecycler.recycle(resource);                    }                    }                    // ActiveResources.java                    void activate(Key key, EngineResource<?> resource) {                    ResourceWeakReference toPut =                    new ResourceWeakReference(                    key,                    resource,                    getReferenceQueue(),                    isActiveResourceRetentionAllowed);                    ResourceWeakReference removed = activeEngineResources.put(key, toPut);                    if (removed != null) {                    removed.reset();                    }                    }                    void deactivate(Key key) {                    ResourceWeakReference removed = activeEngineResources.remove(key);                    if (removed != null) {                    removed.reset();                    }                    }                                如果是回收呢,看看上面的EngineResource,如果引用计数为0并且还没与回收,就会调用真正的服务器租用Resource.recycle(),看其中的一个BitmapResource是怎么回收的,就是放到Bitmap池中,也是用的LRU Cache,这个和今天的主题不相关,就不继续往下拓展。 1                2                3                4                5                                    // BitmapResource.java                    @Override                    public void recycle() {                    bitmapPool.put(bitmap);                    }                                思路再拉到Engine.load()的流程中,接下来该看focus 3,这里再贴一下代码,如果job已经在运行了,那么直接添加一个回调后返回LoadStatus,这个可以允许用户取消任务: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                20                21                22                23                24                                    // Engine.java                    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);                    if (current != null) {                    current.addCallback(cb);                    if (VERBOSE_IS_LOGGABLE) {                    logWithTimeAndKey("Added to existing load", startTime, key);                    }                    return new LoadStatus(cb, current);                    }                    // LoadStatus                    public static class LoadStatus {                    private final EngineJob<?> engineJob;                    private final ResourceCallback cb;                    LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {                    this.cb = cb;                    this.engineJob = engineJob;                    }                    public void cancel() {                    engineJob.removeCallback(cb);                    }                    }                                接着往下看到focus 4, 到这里就需要创建后台任务去拉取磁盘文件或者发起网络请求。 三、磁盘缓存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                                    // Engine.java                    EngineJob<R> engineJob =                    engineJobFactory.build(                    key,                    isMemoryCacheable,                    useUnlimitedSourceExecutorPool,                    useAnimationPool,                    onlyRetrieveFromCache);                    DecodeJob<R> decodeJob =                    decodeJobFactory.build(                    glideContext,                    model,                    key,                    signature,                    width,                    height,                    resourceClass,                    transcodeClass,                    priority,                    diskCacheStrategy,                    transformations,                    isTransformationRequired,                    isScaleOnlyOrNoTransform,                    onlyRetrieveFromCache,                    options,                    engineJob);                    jobs.put(key, engineJob);                    engineJob.addCallback(cb);                    engineJob.start(decodeJob);                    return new LoadStatus(cb, engineJob);                                先构造两个job,一个是EngineJob,另外一个DecodeJob,其中DecodeJob会根据需要解码的资源来源分成下面几个阶段: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                                    // DecodeJob.java                    /**                    * Where were trying to decode data from.                    */                    private enum Stage {                    /** The initial stage. */                    INITIALIZE,                    /** Decode from a cached resource. */                    RESOURCE_CACHE,                    /** Decode from cached source data. */                    DATA_CACHE,                    /** Decode from retrieved source. */                    SOURCE,                    /** Encoding transformed resources after a successful load. */                    ENCODE,                    /** No more viable stages. */                    FINISHED,                    }                                在构造DecodeJob时会把状态置为INITIALIZE。 构造完两个 Job 后会调用 EngineJob.start(DecodeJob),首先会调用getNextStage来确定下一个阶段,这里面跟DiskCacheStrategy这个传入的磁盘缓存策略有关。 磁盘策略有下面几种: **ALL: **缓存原始数据和转换后的数据 **NONE: **不缓存 **DATA: **原始数据,未经过解码或者转换 **RESOURCE: **缓存经过解码的数据 **AUTOMATIC(默认):**根据`EncodeStrategy`和`DataSource`等条件自动选择合适的缓存方 默认的AUTOMATIC方式是允许解码缓存的RESOURCE: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                20                21                                    public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {                    @Override                    public boolean isDataCacheable(DataSource dataSource) {                    return dataSource == DataSource.REMOTE;                    }                    @Override                    public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,                    EncodeStrategy encodeStrategy) {                    return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)                    || dataSource == DataSource.LOCAL)                    && encodeStrategy == EncodeStrategy.TRANSFORMED;                    }                    @Override                    public boolean decodeCachedResource() {                    return true;                    }                    @Override                    public boolean decodeCachedData() {                    return true;                    }                    };                                所以在 getNextStage 会先返回Stage.RESOURCE_CACHE,然后在start中会返回diskCacheExecutor,然后开始执行DecodeJob: 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                                    // EngineJob.java                    public void start(DecodeJob<R> decodeJob) {                    this.decodeJob = decodeJob;                    GlideExecutor executor = decodeJob.willDecodeFromCache()                    ? diskCacheExecutor                    : getActiveSourceExecutor();                    executor.execute(decodeJob);                    }                    // DecodeJob.java                    boolean willDecodeFromCache() {                    Stage firstStage = getNextStage(Stage.INITIALIZE);                    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;                    }                    private Stage getNextStage(Stage current) {                    switch (current) {                    case INITIALIZE:                    return diskCacheStrategy.decodeCachedResource()                    ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);                    case RESOURCE_CACHE:                    return diskCacheStrategy.decodeCachedData()                    ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);                    case DATA_CACHE:                    // Skip loading from source if the user opted to only retrieve the resource from cache.                    return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;                    case SOURCE:                    case FINISHED:                    return Stage.FINISHED;                    default:                    throw new IllegalArgumentException("Unrecognized stage: " + current);                    }                    }                                DecodeJob会回调run()开始执行, run()中调用runWrapped执行工作,这里runReason还是RunReason.INITIALIZE ,根据前面的分析指导这里会获得一个ResourceCacheGenerator,然后调用runGenerators: 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                                    // DecodeJob.java                     private void runWrapped() {                    switch (runReason) {                    case INITIALIZE:                    stage = getNextStage(Stage.INITIALIZE);                    currentGenerator = getNextGenerator();                    runGenerators();                    break;                    case SWITCH_TO_SOURCE_SERVICE:                    runGenerators();                    break;                    case DECODE_DATA:                    decodeFromRetrievedData();                    break;                    default:                    throw new IllegalStateException("Unrecognized run reason: " + runReason);                    }                    }                    private DataFetcherGenerator getNextGenerator() {                    switch (stage) {                    case RESOURCE_CACHE:                    return new ResourceCacheGenerator(decodeHelper, this);                    case DATA_CACHE:                    return new DataCacheGenerator(decodeHelper, this);                    case SOURCE:                    return new SourceGenerator(decodeHelper, this);                    case FINISHED:                    return null;                    default:                    throw new IllegalStateException("Unrecognized stage: " + stage);                    }                    }                                在 runGenerators 中,会调用 startNext,目前currentGenerator是ResourceCacheGenerator, 那么就是调用它的startNext方法: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                                    // DecodeJob.java                     private void runGenerators() {                    currentThread = Thread.currentThread();                    startFetchTime = LogTime.getLogTime();                    boolean isStarted = false;                    while (!isCancelled && currentGenerator != null                    && !(isStarted = currentGenerator.startNext())) {                    stage = getNextStage(stage);                    currentGenerator = getNextGenerator();                    if (stage == Stage.SOURCE) {                    reschedule();                    return;                    }                    }                    // Weve run out of stages and generators, give up.                    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {                    notifyFailed();                    }                    }                                看下ResourceCacheGenerator.startNext(), 这里面就是重点逻辑了,首先从Registry中获取支持资源类型的ModelLoader(其中ModelLoader是在构造Glide的时候传进去), 然后从ModelLoader中构造LoadData,接着就能拿到DataFetcher,(关于ModelLoader/LoadData/DataFetcher之间的关系不在本次范围内,后面有机会再另写)通过它的loadData方法加载数据: 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                52                53                54                                    @Override                    public boolean startNext() {                    List<Key> sourceIds = helper.getCacheKeys();                    if (sourceIds.isEmpty()) {                    return false;                    }                    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();                    if (resourceClasses.isEmpty()) {                    if (File.class.equals(helper.getTranscodeClass())) {                    return false;                    }                    }                    while (modelLoaders == null || !hasNextModelLoader()) {                    resourceClassIndex++;                    if (resourceClassIndex >= resourceClasses.size()) {                    sourceIdIndex++;                    if (sourceIdIndex >= sourceIds.size()) {                    return false;                    }                    resourceClassIndex = 0;                    }                    Key sourceId = sourceIds.get(sourceIdIndex);                    Class<?> resourceClass = resourceClasses.get(resourceClassIndex);                    Transformation<?> transformation = helper.getTransformation(resourceClass);                    currentKey =                    new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops                    helper.getArrayPool(),                    sourceId,                    helper.getSignature(),                    helper.getWidth(),                    helper.getHeight(),                    transformation,                    resourceClass,                    helper.getOptions());                    cacheFile = helper.getDiskCache().get(currentKey);                    if (cacheFile != null) {                    sourceKey = sourceId;                    modelLoaders = helper.getModelLoaders(cacheFile);                    modelLoaderIndex = 0;                    }                    }                    loadData = null;                    boolean started = false;                    while (!started && hasNextModelLoader()) {                    ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);                    loadData = modelLoader.buildLoadData(cacheFile,                    helper.getWidth(), helper.getHeight(), helper.getOptions());                    if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {                    started = true;                    loadData.fetcher.loadData(helper.getPriority(), this);                    }                    }                    return started;                    }                                如果在Resource中找不到需要的资源,那么startNext就会返回false,在runGenerators中就会进入循环体内: 接着会重复上面执行getNextStage,由于现在Stage已经是RESOURCE_CACHE,所以接下来会返回DataCacheGenerator,执行逻辑和上面的ResourceCacheGenerator是一样的,如果还是没有找到需要的,进入循环体内。 此时getNextStage会根据用于是否设置只从磁盘中获取资源,如果是就会通知失败,回调onLoadFailed;如果不是就设置当前Stage为Stage.SOURCE,接着往下走。 状态就会进入循环内部的if条件逻辑里面,调用reschedule。 在reschedule把runReason设置成SWITCH_TO_SOURCE_SERVICE,然后通过callback回调。 DecodeJob中的callback是EngineJob传递过来的,所以现在返回到EngineJob。 在EngineJob中通过getActiveSourceExecutor切换到网络线程池中,执行DecodeJob,下面就准备开始发起网络请求。 四、网络缓存在Stage.SOURCE阶段,通过getNextGenerator返回的是SourceGenerator,所以目前的currentGenerator就是它。 流程还是一样的,SourceGenerator还是调用startNext方法,获取到对应的DataFetcher,这里其实是HttpUrlFetcher,发起网络请求。 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                20                21                22                23                24                                    // DecodeJob.java                     private void runGenerators() {                    ...                    while (!isCancelled && currentGenerator != null                    && !(isStarted = currentGenerator.startNext())) {                    stage = getNextStage(stage);                    currentGenerator = getNextGenerator();                    if (stage == Stage.SOURCE) {                    reschedule();                    return;                    }                    }                    ...                    }                    @Override                    public void reschedule() {                    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;                    callback.reschedule(this);                    }                    // EngineJob.java                    @Override                    public void reschedule(DecodeJob<?> job) {                    getActiveSourceExecutor().execute(job);                    }                                先缓一缓,本文其实到了上面已经可以结束了,Glide涉及到的五级缓存都已经涉及到了,是真的就可以结束了吗?不是的,网络请求回来和缓存还有关系吗?接着看到HttpUrlFetcher,下载成功后回调onDataReady,其中callback是SourceGenerator: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                20                21                22                23                24                                    // HttpUrlFetcher.java                    @Override                    public void loadData(@NonNull Priority priority,                    @NonNull DataCallback<? super InputStream> callback) {                    long startTime = LogTime.getLogTime();                    try {                    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());                    callback.onDataReady(result);                    } catch (IOException e) {                    if (Log.isLoggable(TAG, Log.DEBUG)) {                    Log.d(TAG, "Failed to load data for url", e);                    }                    callback.onLoadFailed(e);                    } finally {                    if (Log.isLoggable(TAG, Log.VERBOSE)) {                    Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));                    }                    }                    }                    // EngineJob.java                    @Override                    public void reschedule(DecodeJob<?> job) {                    getActiveSourceExecutor().execute(job);                    }                                正常情况会进入if判断逻辑里面,赋值dataToCache,然后回调cb.reschedule,而cb就是DecodeJob构造SourceGenerator的时候传入,cb是DecodeJob。 1                2                3                4                5                6                7                8                9                10                11                12                                    // SourceGenerator.java                    @Override                    public void onDataReady(Object data) {                    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();                    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {                    dataToCache = data;                    cb.reschedule();                    } else {                    cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,                    loadData.fetcher.getDataSource(), originalKey);                    }                    }                                DecodeJob在reschedule回调EngineJob,最后还是回到SourceGenerator中的startNext()逻辑。 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                20                                    // DecodeJob.java                    private DataFetcherGenerator getNextGenerator() {                    switch (stage) {                    case RESOURCE_CACHE:                    return new ResourceCacheGenerator(decodeHelper, this);                    case DATA_CACHE:                    return new DataCacheGenerator(decodeHelper, this);                    case SOURCE:                    return new SourceGenerator(decodeHelper, this);                    case FINISHED:                    return null;                    default:                    throw new IllegalStateException("Unrecognized stage: " + stage);                    }                    }                    @Override                    public void reschedule() {                    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;                    callback.reschedule(this);                    }                                和第一次进来的逻辑不一样,现在dataToCache != null,进入第一个if逻辑。 在逻辑里面调用cacheData,逻辑很明显,保持数据到本地,然后会构造一个DataCacheGenerator。 而DataCacheGenerator前面已经分析过了,就是用来加载本地原始数据的,这回会加载成功,返回true。 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                                    // SourceGenerator.java                    @Override                    public boolean startNext() {                    if (dataToCache != null) {                    Object data = dataToCache;                    dataToCache = null;                    cacheData(data);                    }                    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {                    return true;                    }                    ...                    }                    private void cacheData(Object dataToCache) {                    long startTime = LogTime.getLogTime();                    try {                    Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);                    DataCacheWriter<Object> writer =                    new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());                    originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());                    helper.getDiskCache().put(originalKey, writer);                    } finally {                    loadData.fetcher.cleanup();                    }                    sourceCacheGenerator =                    new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);                    }                                接下来就是一系列的回调了: DataCacheGenerator的startNext逻辑里面会给DataFetcher传递自身作为callback,在加载本地数据成功后回调onDataReady。 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                18                19                                    // DataCacheGenerator                    @Override                    public boolean startNext() {                    ...                    loadData = null;                    boolean started = false;                    while (!started && hasNextModelLoader()) {                    ...                    if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {                    started = true;                    loadData.fetcher.loadData(helper.getPriority(), this);                    }                    }                    return started;                    }                    @Override                    public void onDataReady(Object data) {                    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);                    }                                而cb现在是SourceGenerator传递过来,SourceGenerator再回调它自己的cb,是DecodeJob在构造它的时候传过来。 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                                    // SourceGenerator.java                    @Override                    public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,                    DataSource dataSource, Key attemptedKey) {                    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);                    }                    // DecodeJob.java                    @Override                    public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,                    DataSource dataSource, Key attemptedKey) {                    this.currentSourceKey = sourceKey;                    this.currentData = data;                    this.currentFetcher = fetcher;                    this.currentDataSource = dataSource;                    this.currentAttemptingKey = attemptedKey;                    if (Thread.currentThread() != currentThread) {                    runReason = RunReason.DECODE_DATA;                    callback.reschedule(this);                    } else {                    try {                    decodeFromRetrievedData();                    } finally {                    GlideTrace.endSection();                    }                    }                    }                                在上面SourceGenerator把DecodeJob切换到ActiveSourceExecutor线程中执行,还记得一开始DecodeJob是在哪启动的吗?在EngineJob中启动,然后是把DecodeJob放到diskCacheExecutor中执行。 1                2                3                4                5                6                7                8                                    // EngineJob.java                    public void start(DecodeJob<R> decodeJob) {                    this.decodeJob = decodeJob;                    GlideExecutor executor = decodeJob.willDecodeFromCache()                    ? diskCacheExecutor                    : getActiveSourceExecutor();                    executor.execute(decodeJob);                    }                                所以上面在DecodeJob的onDataFetcherReady会走到第一个if逻辑里面,然后赋值runReason = RunReason.DECODE_DATA,再一次回调Engine.reschedule,将工作线程切换到ActiveSourceExecutor。 1                2                3                4                5                6                7                8                9                                    // Engine.java                    @Override                    public void reschedule(DecodeJob<?> job) {                    // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself                    // up.                    getActiveSourceExecutor().execute(job);                    }                    //                                然后还是走到DecodeJob, 现在会进入DECODE_DATA分支,在这里面会调用ResourceDecoder把数据解码: 1                2                3                4                5                6                7                8                9                10                11                12                13                14                15                16                17                                    private void runWrapped() {                    switch (runReason) {                    case INITIALIZE:                    stage = getNextStage(Stage.INITIALIZE);                    currentGenerator = getNextGenerator();                    runGenerators();                    break;                    case SWITCH_TO_SOURCE_SERVICE:                    runGenerators();                    break;                    case DECODE_DATA:                    decodeFromRetrievedData();                    break;                    default:                    throw new IllegalStateException("Unrecognized run reason: " + runReason);                    }                    }                                解码成功后调用notifyComplete(result, dataSource); 1                2                3                4                                    private void notifyComplete(Resource<R> resource, DataSource dataSource) {                    setNotifiedOrThrow();                    callback.onResourceReady(resource, dataSource);                    }                                五、总结现在回答一下开头的几个问题。 1、有几级缓存?五级,分别是什么?活动资源 (Active Resources) 内存缓存 (Memory Cache) 资源类型(Resource Disk Cache) 原始数据 (Data Disk Cache) 网络缓存 2、Glide内存缓存之间是什么关系?专门画了一幅图表明这个关系,言简意赅。  
 3、Glide本地文件IO和网络请求是一个线程吗?明显不是,本地IO通过diskCacheExecutor,而网络IO通过ActiveSourceExecutor 4、Glide网络请求回来后数据直接返回给用户还是先存再返回?不是直接返回给用户,会在SourceGenerator中构造一个DataCacheGenerator来取数据。  |