史上最细Glide源码解读(一) : 主流程分析

前言

本篇作为史上最细Glide源码解读系列第一篇 , 主要对主流程进行分析
以后还会有若干篇对Glide 中 运用的设计模式 / 线程池 /图片优化/ 解码/转码/缓存 等细节的文章发布

使用

假设调用以下代码进行图片加载

Glide.with(activity).load("https/http开头的图片链接").into(imageView)

先来一张思维导图 glide 思维图.png 流程分析开始 , 前排请系好安全带

with 方法

with 方法调用如下

  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
  
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever();
  }
  
  public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

with 方法主要执行以下几歩

    1. 初始化Glide
    1. 调用RequestManagerRetriever.get 构建RequestManager 并返回 RequestManager
    1. 将request 和 Activity 生命周期绑定

初始化Glide

Glide 全局单例 ,构造函数如下

  Glide( //... 参数省略//) {
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;
    this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;

    final Resources resources = context.getResources();

    registry = new Registry();
      
    //拼接到 model loader表
    registry
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
   
    //拼接到 decoder 表  
    registry
          .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            InputStream.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
     
    //... 省略大部分registry.append(xxx)代码

    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptionsFactory,
            defaultTransitionOptions,
            defaultRequestListeners,
            engine,
            isLoggingRequestOriginsEnabled,
            logLevel);
  }

registry 是一张注册表 , glide 所有操作需要的类都在这张注册表里面 , 建议背熟此表哈哈哈

比如 append(String.class, InputStream.class, new StringLoader.StreamFactory()) , **输入为url String , 输出为InputStream 数据 , 操作类为StringLoader , StringLoader 作用将图片的 # **load(url)方法**输入的url 字符串转换为 InputStream **

append(Registry.BUCKET_BITMAP_DRAWABLE, InputStream.class, BitmapDrawable.class,new BitmapDrawableDecoder ) , 输入为 InputStream , 输出为BitmapDrawable , BitmapDrawableDecoder 作用将InputStream 解码转换为 BitmapDrawable

registry 注册表有很多 , 作用在注释中

  //model 加载表 , 将model 转换为可供解码的数据如Stream  
  private final ModelLoaderRegistry modelLoaderRegistry;

  //编码表 , 将InputStream / ByteBuffer 编码成File , 缓存在磁盘中
  private final EncoderRegistry encoderRegistry;

  //资源解码表 , 将Stream等数据解码为可供ImageView显示的GifDrawable / Bitmap / BitmapDrawable 
  private final ResourceDecoderRegistry decoderRegistry;

  //资源编码表 , 将GifDrawable / Bitmap / BitmapDrawable 编码成File , 缓存在磁盘中
  private final ResourceEncoderRegistry resourceEncoderRegistry;
  
 //流回卷表, 用于将流回退重读
  private final DataRewinderRegistry dataRewinderRegistry;

  //转码表 ,  用于将Bitmap / GifDrawable 转码为 BitmapDrawable / byte[].class
  private final TranscoderRegistry transcoderRegistry;
 
 //图片头解析表 , 用于解析图片头
  private final ImageHeaderParserRegistry imageHeaderParserRegistry;

上面这几张表 , glide 大部分功能都会用到 , 非常复杂 , 以后会出一篇文章详细解答 , 继续关注主流程

RequestManagerRetriever#get

RequestManagerRetriever.get 方法中将 request 和 Activity 生命周期绑定 , 通过Fragment .commitAllowingStateLoss 方法 , 使用透明的Fragment 监听Activity 生命周期 , onStart 启动 图片加载请求 , onStop暂停图片加载请求 , Fragment # onStop 最终会调用到 requestTracker#pauseRequests

  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {  
      if (request.isRunning()) {
        //status == Status.RUNNING || status == Status.WAITING_FOR_SIZE 
        request.pause();
        pendingRequests.add(request);
      }
    }
  }

如果正在请求 , request#pause 方法会把此次请求的数据clear掉 , 但是并不会清理已缓存在磁盘中的数据

 // SingleRequest#clear
public void clear() {
    Resource<R> toRelease = null;
    synchronized (requestLock) {
      if (status == Status.CLEARED) {
        return;
      }
      cancel();
      if (resource != null) {
        toRelease = resource;
        resource = null;
      }
      if (canNotifyCleared()) {
        target.onLoadCleared(getPlaceholderDrawable());
      }
      status = Status.CLEARED;
    }
    if (toRelease != null) {
      engine.release(toRelease);
    }
  }

总结下with 方法 , 初始化 glide 把所需的类放在表中保存起来 , 监听Activity 生命周期用于控制开始和取消请求

load 方法

load 方法从RequestManager 开始 ,

//RequestManager#load 
public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
}
public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
}

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    }

构建一个RequestBuilder 并返回 , RequestBuilder 的泛型TranscodeType 为 Drawable RequestBuilder 构造方法内会应用默认的请求配置和请求监听

protected RequestBuilder(
    @NonNull Glide glide,
    RequestManager requestManager,
    Class<TranscodeType> transcodeClass,
    Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();
    //应用默认的请求监听
    initRequestListeners(requestManager.getDefaultRequestListeners());
    //应用默认的请求配置
    apply(requestManager.getDefaultRequestOptions());
}

requestManager 有setDefaultRequestOptions , addDefaultRequestListener 可以设置请求配置和请求监听 总结下load 方法 , 构建RequestBuilder 然后应用请求配置和监听

into 方法

经过with , load 两个方法 , 图片请求相关的配置都已经处理了 , 接下来就是构建 Request 去请求图片的资源了 RequestBuilder#into 方法

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
        switch (view.getScaleType()) {
            case CENTER_CROP:
                requestOptions = requestOptions.clone().optionalCenterCrop();
                break;
            case CENTER_INSIDE:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                requestOptions = requestOptions.clone().optionalFitCenter();
                break;
            case FIT_XY:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case CENTER:
            case MATRIX:
            default:
                // Do nothing.
        }
    }
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

根据ImageView 的 ScaleType 设置图片 Transform , 因为transcodeClass 为 Drawable 所以glideContext.buildImageViewTarget返回的为 DrawableImageViewTarget , 然后传入图片处理成功之后的在主线程运行的线程池 继续更进into方法 , 会构建出 Request , 然后将 Request 放入到 RequestManager 变量targetTracker中 , 接着调用 request.begin() 方法 , RequestManager 中变量保存的 Request 使用的是WeakHashMap , 防止 Request 持有ImageView 引用造成内存泄漏 继续查看Request#begin 方法 , 会先获取到图片的Width 和 Height , 在图片的Width 和 Height有效的情况 , 会调用 engine.load 方法 , engine是Glide 中的变量 , 全局单例

public void begin() {
    synchronized (requestLock) {
        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            target.getSize(this);
        }    
    }
}

public void onSizeReady(int width, int height) {
    synchronized (requestLock) {
        status = Status.RUNNING;   
        loadStatus =
            engine.load(
            glideContext,
            model,
            requestOptions.getSignature(),
            this.width,
            this.height,
            requestOptions.getResourceClass(),
            transcodeClass,
            priority,
            // ... 省略相关配置//
            this,
            callbackExecutor);
    }
  }

engine#load 方法 , 主要分两歩

    1. 先从内存缓存中判断是否有缓存 , 如果有则从内存缓存中获取直接返回
    1. 内存缓存中没有缓存 , 就构建engineJob 和 decodeJob , decodeJob 负责用于去下载和解码源数据 , engineJob 负责开始/取消decodeJob , 图片加载相关的回调

默认情况下Glide启用RESOURCE 和 DATA 的 缓存 , engineJob 会将decodeJob 放入 **diskCacheExecutor线程池中执行 , **DecodeJob #run 会执行runWrapped , runWrapped 才是关键方法

DecodeJob #runWrapped

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;
    }
}
private Stage getNextStage(Stage current) {
    switch (current) {
        case INITIALIZE:
            return diskCacheStrategy.decodeCachedResource()
                ? Stage.RESOURCE_CACHE
                //递归获取Stage
                : getNextStage(Stage.RESOURCE_CACHE);
        case RESOURCE_CACHE:
            return diskCacheStrategy.decodeCachedData()
                ? Stage.DATA_CACHE
                : getNextStage(Stage.DATA_CACHE);
        case DATA_CACHE:
            return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
        case SOURCE:
        case FINISHED:
            return Stage.FINISHED;
    }
}
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;
    }
  }

runWrapped 分三步

    1. 先从RESOURCE_CACHE 中去找 , 这个缓存拿到的图片已经解码过可以直接给ImageView使用, 对应的是 ResourceCacheGenerator这个类
    1. 如果 RESOURCE_CACHE 缓存中没有 , 则从 DATA_CACHE 中查找 , DATA_CACHE 缓存中缓存的是未经解码的源数据 , 需要解码才能给ImageView使用 , 对应的是DataCacheGenerator这个类
    1. 如果RESOURCE_CACHE 和 DATA_CACHE 中都没有找到 , 则从源(服务器或者本地)去找到图片数据 , 对应的是 SourceGenerator这个类

因为首次请求没有缓存所以会调用 SourceGenerator #startNext , 此方法会从 ModelLoader 注册表中获取 ModelLoader 加载Model 对应的源数据

SourceGenerator #startNext

startNext 方法作用是拿到图片的源数据 , 如果可以缓存就把源数据缓存

public boolean startNext() {
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
        loadData = helper.getLoadData().get(loadDataListIndex++);
        if (loadData != null
            && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
                || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
            started = true;
            startNextLoad(loadData);
        }
    }
    return started;
}

List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
        isLoadDataSet = true;
        loadData.clear();
        List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
        for (int i = 0, size = modelLoaders.size(); i < size; i++) {
            ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
            LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
            if (current != null) {
                loadData.add(current);
            }
        }
    }
    return loadData;
  }

上面方法代码有点长 , glideContext.getRegistry().getModelLoaders(model) 获取所有能处理此 model的 ModelLoaders , 然后把modelLoader 遍历构建成 List<LoadData<?>> 返回 model 为String , 所以 ModelLoaders为在modelLoader 表注册的 StringLoader 和 DataUrlLoader image.png 但因为传入的model 为http / https 开头 , DataUrlLoader会被过滤掉 , 就只剩下**StringLoader **了

public final class DataUrlLoader<Model, Data> implements ModelLoader<Model, Data> {
    
    private static final String DATA_SCHEME_IMAGE = "data:image";
    
    @Override
    public boolean handles(@NonNull Model model) {
        return model.toString().startsWith(DATA_SCHEME_IMAGE);
    }
    
  }

ModelLoaderRegistry#getModelLoaders( A model) 中会根据 ModelLoader的 handles 方法来过滤

public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) { 
    for (int i = 0; i < size; i++) {
        ModelLoader<A, ?> loader = modelLoaders.get(i);
        //loader.handles 返回true 才会添加到 modelLoaders中
        if (loader.handles(model)) {
            filteredLoaders.add(loader);
        }  
    }
    return filteredLoaders;
}

StringLoader 又会丢给StringLoader 中的 uriLoader 去处理 ,** StringLoader #buildLoadData 会根据model 进行筛选 **, 如果是 '/' 开头的本地图片会返回 null , 因为model 为 http/https 开头 ,详细代码如下

public LoadData<Data> buildLoadData(
    @NonNull String model, int width, int height, @NonNull Options options) {
    Uri uri = parseUri(model);
    //非http/https 的返回null
    if (uri == null || !uriLoader.handles(uri)) {
        return null;
    }
    return uriLoader.buildLoadData(uri, width, height, options);
}
//model 为http/https 开头
private static Uri parseUri(String model) {
    Uri uri;
    if (TextUtils.isEmpty(model)) {
        return null;
    } else if (model.charAt(0) == '/') {
        uri = toFileUri(model);
    } else {
        uri = Uri.parse(model);
        String scheme = uri.getScheme();
        if (scheme == null) {
            uri = toFileUri(model);
        }
    }
    return uri;
  }

经过重重的筛选就只有StreamFactory#build() 的 StringLoader 可以供 model 使用

public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
    }

这样就可以去通过StringLoader加载图片了 ? 还没完 , 还会通过 Uri.class 和 InputStream.class 去找到与之匹配的modelLoader image.png 会找到HttpUriLoader , HttpUriLoader 又会通过 GlideUrl.class, InputStream.class 找到与之匹配的HttpGlideUrlLoader, 是不是有点俄罗斯套娃的感觉 , 一层套一层

public class HttpUriLoader implements ModelLoader<Uri, InputStream> {
    private static final Set<String> SCHEMES =
        Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
    
    public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
        
        @NonNull
        @Override
        public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
            return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
        }
    }
}

image.png HttpGlideUrlLoader 应该没有套了吧 ? 继续点进HttpGlideUrlLoader 查看

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
    
    /** The default factory for {@link HttpGlideUrlLoader}s. */
    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        
        @Override
        public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
            return new HttpGlideUrlLoader(modelCache);
        }
        
    }
}

果不其然 , HttpGlideUrlLoader 的Factory#build 直接返回HttpGlideUrlLoader , 停止套娃了 经过重重筛选 ,HttpGlideUrlLoader .Factory 生成的 HttpGlideUrlLoader , 才是model 需要的loader , HttpGlideUrlLoader 会通过buildLoadData 方法生成 LoadData , 去服务器下载图片的源文件就是通过LoadData完成 接下来看看HttpGlideUrlLoader #buildLoadData 方法

HttpGlideUrlLoader #buildLoadData

public LoadData<InputStream> buildLoadData(
    @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

会new 一个 LoadData , fetcher 为 HttpUrlFetcher 回到 **SourceGenerator#startNextLoad **中

private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        // ...省略回调 //);
  }

继续更进HttpUrlFetcher # loadData

@Override
public void loadData(
    @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    try {
        InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
        callback.onDataReady(result);
    }
}

private InputStream loadDataWithRedirects(
    URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
    //...省略代码 //
    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
        urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.connect();
    stream = urlConnection.getInputStream();
    //取消直接返回null
    if (isCancelled) {
        return null;
    }
    return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
}


interface HttpUrlConnectionFactory {
    HttpURLConnection build(URL url) throws IOException;
}

private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
    
    @Override
    public HttpURLConnection build(URL url) throws IOException {
        return (HttpURLConnection) url.openConnection();
    }
  }

历经百般阻挠 , 终于看到 从服务器下载图片相关的代码 , glide 通过系统提供的 HttpURLConnection去服务器下载图片的源文件
图片源数据下载好之后 , 会通过DataCallback 回调到SourceGenerator#onDataReadyInternal中

SourceGenerator#onDataReadyInternal

处理图片的源数据

void onDataReadyInternal(LoadData<?> loadData, 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);
    }
  }

data为InputStream , 默认缓存是支持DATA_CACHE , 所以会走进if 条件内, dataToCache = data , cb 为 DecodeJob, 最终会通知EngineJob 重新将 DecodeJob丢进线程池中

//SourceGenerator#onDataReadyInternal
void onDataReadyInternal(LoadData<?> loadData, Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
        cb.reschedule();
    }
}
//DecodeJob#reschedule
public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
}
//EngineJob#reschedule
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#runWrapped

此时 runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; 会直接执行runGenerators() 方法 , currentGenerator 还是原来的SourceGenerator , 所以会再执行SourceGenerator#startNext 方法

class SourceGenerator implements DataFetcherGenerator, DataFetcherGenerator.FetcherReadyCallback {
    
    private DataCacheGenerator sourceCacheGenerator;
    private Object dataToCache;
    
    @Override
    public boolean startNext() {
        if (dataToCache != null) {
            Object data = dataToCache;
            dataToCache = null;
            cacheData(data);
        }
        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
            return true;
        }
        return started;
    }
    
    
    private void cacheData(Object dataToCache) {
        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);
        } 
        sourceCacheGenerator =
            new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
    }
}

此时 dataToCache 不为 null , 会通过DiskCache 将源数据缓存在文件 中 , encoder为 StreamEncoder , 将图片缓存在文件中然后会构建出 DataCacheGenerator 接着执行DataCacheGenerator#startNext 方法

class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {
    
    @Override
    public boolean startNext() {
        while (modelLoaders == null || !hasNextModelLoader()) {
            
            cacheFile = helper.getDiskCache().get(originalKey);
            
            while (!started && hasNextModelLoader()) {
                ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
                
                if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                    started = true;
                    loadData.fetcher.loadData(helper.getPriority(), this);
                }
            }
            return started;
        }    
        
}

DataCacheGenerator#startNext 和 SourceGenerator 也是差不多的思路 , 先拿到刚刚缓存在磁盘中的cacheFile , 然后找到源File对应的 FileLoader , 通过FileLoader 的loadData.fetcher去拿到缓存在File中图片Stream数据 , 这里贴点关键代码

public class FileLoader<Data> implements ModelLoader<File, Data> {
    private static final String TAG = "FileLoader";
    
    @Override
    public LoadData<Data> buildLoadData(
        @NonNull File model, int width, int height, @NonNull Options options) {
        return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
    }
    
    private static final class FileFetcher<Data> implements DataFetcher<Data> {
        
        @Override
        public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
            try {
                data = opener.open(file);
            } 
            callback.onDataReady(data);
        }
    }
    
    public static class StreamFactory extends Factory<InputStream> {
        public StreamFactory() {
            super(
                new FileOpener<InputStream>() {
                    @Override
                    public InputStream open(File file) throws FileNotFoundException {
                        return new FileInputStream(file);
                    }
                });
        }
    }
}

拿到了图片的 FileInputStream数据之后 , 会回调到DecodeJob#onDataFetcherReady 方法中

DecodeJob#onDataFetcherReady

这一步之前已经将图片的源数据缓存在DATA_CACHE 中了 , 现在是将源数据从DATA_CACHE 中拿出来并且转换为Stream了 这里判断了下是不是和 runGenerators 的线程一致 , 不一致则重新调度下, 目的是让其运行在对应的线程池中

private void runGenerators() {
    currentThread = Thread.currentThread();
}  
@Override
public void onDataFetcherReady(
    Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    
    if (Thread.currentThread() != currentThread) {
        runReason = RunReason.DECODE_DATA;
        callback.reschedule(this);
    } else {
        try {
            decodeFromRetrievedData();
        } finally {
            GlideTrace.endSection();
        }
    }
  }

最终都会调到decodeFromRetrievedData 方法

DecodeJob#decodeFromRetrievedData

主要有3步

    1. 将Stream 解码成 Resource
    1. 将 Resource 缓存在 RESOURCE_CACHE 中
    1. 如果resource == null , 会再次调用 runGenerators

基于第三步 , 先提出个问题

如果resource == null , 执行runGenerators 又会调用 DataCacheGenerator#startNext 方法 , startNext 方法最终又会调到 decodeFromRetrievedData , ( decodeFromRetrievedData -> DataCacheGenerator#startNext -> 又回调到 decodeFromRetrievedData)这样不是死循环了 ?

private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
        //1. 解码  
        resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } 
    if (resource != null) {
        //2. 回调将图片显示 , 然后缓存在 RESOURCE_CACHE 中
        notifyEncodeAndRelease(resource, currentDataSource);
    } else {
        //3. 再次调用 runGenerators
        runGenerators();
    }
  }

解答第三歩提出死循环问题 , 因为在DataCacheGenerator#startNext中有处理

public boolean startNext() {
    // 通过第三步runGenerators(); 
    //再次调用进来 modelLoaders 肯定不为null ,跳过这个循环
    while (modelLoaders == null || !hasNextModelLoader()) {
        
    }
    boolean started = false;
    while (!started && hasNextModelLoader()) {
        ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
    }
    return started;
}

private boolean hasNextModelLoader() {
    return modelLoaderIndex < modelLoaders.size();
  }

在第二个while循环中 ,每次循环一次 modelLoaderIndex++ , 而hasNextModelLoader() 方法如果modelLoaderIndex>=modelLoaders 会返回false , 直接跳过循环 return false 如果 DataCacheGenerator#startNext 返回 false , 再看看 runGenerators

private void runGenerators() {
    
    boolean isStarted = false;
    while (!isCancelled
           && currentGenerator != null
           && !(isStarted = currentGenerator.startNext())) {
        stage = getNextStage(stage);
    }
    
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
        notifyFailed();
    }
    
  }

此时stage = getNextStage(stage) = FINISHED , 会调用 notifyFailed , 所以当解码之后resource == null 情况下, 会调用 notifyFailed 通知图片加载失败 , 并不会死循环执行

继续分析第二步 , Stream 解码成 Resource

最终会调用 decodeFromData() -> decodeFromFetcher() 方法 , 在这个方法中会通过 decodeHelper.getLoadPath((Class) data.getClass())去拿到 LoadPath, 看来又是通过注册表中去找的

<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
    return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
  }

先看看 dataClass, resourceClass, transcodeClass 是什么?

  1. dataClass是从DataCacheGenerator 通过 FileLoader 回调过来的 InputStream , 所以 dataClass = InputStream.class
  2. resourceClass 从 BaseRequestOptions 设置, 如果不设置默认为 Object.class
  3. transcodeClass 是 class RequestBuilder 的泛型TranscodeType , 由as方法指定 , 此处为Drawable.class

image.pngLoadPath<Data, TResource, Transcode> 功能由 List<DecodePath<Data, TResource, Transcode>> 实现 , DecodePath<Data, TResource, Transcode> 表示 从Data->TResource->Transcode 的一条解码路径 , 生成代码如下

private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
    @NonNull Class<Data> dataClass,
    @NonNull Class<TResource> resourceClass,
    @NonNull Class<Transcode> transcodeClass) {
    
    List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
    //1. 根据dataClass, resourceClass找到所有registeredResourceClasses
    List<Class<TResource>> registeredResourceClasses =
        decoderRegistry.getResourceClasses(dataClass, resourceClass);
    
    for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
        //2.  根据registeredResourceClass找到所有registeredTranscodeClasses
        List<Class<Transcode>> registeredTranscodeClasses =
            transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
        
        for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
            //3. 根据registeredResourceClass找到所有decoders
            List<ResourceDecoder<Data, TResource>> decoders =
                decoderRegistry.getDecoders(dataClass, registeredResourceClass);
             //4. 根据registeredTranscodeClass找到所有transcoder
            ResourceTranscoder<TResource, Transcode> transcoder =
                transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
             //5. 构建DecodePath
            DecodePath<Data, TResource, Transcode> path =
                new DecodePath<>(
                dataClass,
                registeredResourceClass,
                registeredTranscodeClass,
                decoders,
                transcoder,
                throwableListPool);
            decodePaths.add(path);
        }
    }
    return decodePaths;
}

我把上面代码分成5小步 , 下面详细看下这5歩

  1. 根据dataClass, resourceClass找到所有registeredResourceClasses

image.png 可以就只有看到Bitmap.class 满足条件 , 因为GifDrawable.class 的dataclass为ByteBuffer 不是InputStream的子类 , 所以registeredResourceClasses只有一个数据 Bitmap.class

  1. 根据registeredResourceClass找到所有registeredTranscodeClasses

通过getTranscodeClasses方法 去获取**registeredTranscodeClasses **

   List<Class<Transcode>> registeredTranscodeClasses =
          transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);

public synchronized <Z, R> List<Class<R>> getTranscodeClasses(
      @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) {
    List<Class<R>> transcodeClasses = new ArrayList<>();
    for (Entry<?, ?> entry : transcoders) {
      if (entry.handles(resourceClass, transcodeClass)) {
        transcodeClasses.add(transcodeClass);
      }
    }
    return transcodeClasses;
  }

transcodeClass 为 Drawable.class **所以registeredTranscodeClasses 也和registeredResourceClasses 一样只有一个数据 ** Drawable.class

  1. 根据registeredResourceClass找到所有decoders
  List<ResourceDecoder<Data, TResource>> decoders =
            decoderRegistry.getDecoders(dataClass, registeredResourceClass);

dataClass为InputStream.class , registeredResourceClass 为Bitmap.class 符合条件的就只有streamBitmapDecoder , streamBitmapDecoder又会根据 Android 版本构建 , 本文以StreamBitmapDecoder来分析

 ResourceDecoder<InputStream, Bitmap> streamBitmapDecoder;
    if (isImageDecoderEnabledForBitmaps && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
      streamBitmapDecoder = new InputStreamBitmapImageDecoderResourceDecoder();
      byteBufferBitmapDecoder = new ByteBufferBitmapImageDecoderResourceDecoder();
    } else {
      byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
      streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
    }
  1. 根据registeredTranscodeClass找到所有transcoder
 ResourceTranscoder<TResource, Transcode> transcoder =
            transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);

转码表中根据registeredResourceClass =Bitmap.class 和 registeredTranscodeClass =Drawable.class 去获取 transcoder image.png 符合条件就只有 BitmapDrawableTranscoder

总结

decodePaths集合中 只有一个DecodePath实例对象 , decoders 只有一个streamBitmapDecoder 实例对象 , transcoder为 BitmapDrawableTranscoder

   DecodePath<Data, TResource, Transcode> path =
            new DecodePath<>(
                dataClass,
                registeredResourceClass,
                registeredTranscodeClass,
                decoders,
                transcoder,
                throwableListPool);
        decodePaths.add(path);

从 InputStram ->Bitmap -> Drawable这条先找匹配的解码器后找匹配的转码器的路径 的LoadPath 就分析完了 ,解码和转码的细节以后再抽一篇文章分析 , 继续关注主流程

继续回到DecodeJob#decodeFromRetrievedData方法

  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } 
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } 
  }

异常情况就先不去分析了 , 假设解码和转码成功下 , 此时Resource 不为空了, 并且泛型R 和 LoadPath<Data, ?, R> 的泛型R 一样为BitmapDrawable, 继续进入notifyEncodeAndRelease

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
  
    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }
    //显示图片
    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
          //缓存
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } 
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  }

以上代码分两步

  1. 回调ImageView 显示图片

图片显示回调链 , DecodeJob#notifyComplete() -> EngineJob#onResourceReady()-> ....->主线程池运行SingleRequest#onResourceReady 开始启动DecodeJob 之前 , Engine#waitForExistingOrStartNewJob 中给EngineJob添加Callback , cb 为SingleRequest , callbackExecutor为在主线程运行的线程池

 
private <R> LoadStatus waitForExistingOrStartNewJob(
      ResourceCallback cb,
      Executor callbackExecutor,
  ) {
       engineJob.addCallback(cb, callbackExecutor);
    }

synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
    cbs.add(cb, callbackExecutor);
  }

void add(ResourceCallback cb, Executor executor) {
      callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
    }

转码成功后 , EngineJob中处理回调

  void notifyCallbacksOfResult() {

    for (final ResourceCallbackAndExecutor entry : callbacksAndExecutors) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }
 
private class CallResourceReady implements Runnable {

    private final ResourceCallback cb;

    @Override
    public void run() {
     callCallbackOnResourceReady(cb);
    }
  }
  
void callCallbackOnResourceReady(ResourceCallback cb) {
   cb.onResourceReady(engineResource, dataSource); 
}

显示图片

SingleRequest最终会调用DrawableImageViewTarget#setResource

  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
  1. 把 Resource缓存在磁盘中

onResourceDecoded 会在解码和转码成功后回调 , 默认 isResourceCacheable = true , 支持ResourceCache , 调用deferredEncodeManager.init() 给 toEncode 赋值

  <Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) { 
if //省略代码
    (diskCacheStrategy.isResourceCacheable(
        isFromAlternateCacheKey, dataSource, encodeStrategy)) {
      // 支持ResourceCache 给toEncode 赋值
      deferredEncodeManager.init(key, encoder, lockedResult); 
    }
  }

缓存图片

回调图片显示之后 , 判断如果toEncode !=null , 表示有图片要缓存, 则调用 encode 使用DiskCache 把Resource 缓存起来

    void encode(DiskCacheProvider diskCacheProvider, Options options) {
      try {
        diskCacheProvider
            .getDiskCache()
            .put(key, new DataCacheWriter<>(encoder, toEncode, options));
      }
    }

总结

通过请求参数构建图片Request , 然后把Request放入到RequestManager 管理 , 请求的生命周期控制通过Fragment 实现 图片的宽高准备好之后调用Request#begin方法启动Engine开始请求

Engine 负责构建出EngineJob 和DecodeJob ,并且负责连接EngineJob 和DecodeJob

EngineJob负责管理(开始/取消) DecodeJob , 以及将图片请求的结果回调给SingleRequest/ImageView

DecodeJob负责获取图片源数据/解码/转码 , 主要功能由内部变量 decodeHelper 实现

再次贴出文章开始放的图 glide 思维图.png

猜你喜欢

转载自juejin.im/post/7120601725311926286