Glide源码解析(1)—图片加载流程

Glide加载图片主流程分析

Glide源码地址:https://github.com/bumptech/glide

Glide版本:3.7.0

使用Glide的时候主要三步代码,如下所示:

Glide.with(this).load("url").into(iv_glide);

Glide.with(this)

获取一个RequestManager对象

public static RequestManager with(Activity activity) {
    // 饿汉式 单例模式创建
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    // 获取一个RequestManager对象
    return retriever.get(activity);
}

RequestManager

RequestManager类的注释是:A class for managing and starting requests for Glide,意思是一个为Glide管理和开启请求的类。

load方法:

public DrawableTypeRequest<String> load(String string) {
    return (DrawableTypeRequest<String>) fromString().load(string);
}

fromString方法:

public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}

loadGeneric方法,就是负责创建DrawableTypeRequest对象,泛型是String类型。

最终调用的是DrawableTypeRequest类的load方法。

DrawableTypeRequest

load方法:

@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
    super.load(model);
    return this;
}

调用了父类GenericRequestBuilder的load方法:

GenericRequestBuilder类load方法:

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
    this.model = model;
    isModelSet = true;
    return this;
}

这里其实就是将参数传递进来,整个流程相当于只是配置了一下加载的参数。

into(view)流程

点进去into方法,进入DrawableRequestBuilder类的into方法,这里调用了父类的into方法。

@Override
public Target<GlideDrawable> into(ImageView view) {
    return super.into(view);
}

进入父类GenericRequestBuilder类的into方法:

public Target<TranscodeType> into(ImageView view) {
    Util.assertMainThread(); // 保证在主线程执行
   	// 省略部分代码
   	......
    return into(glide.buildImageViewTarget(view, transcodeClass));
}

这里首先调用了buildImageViewTarget方法:

<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}

这里通过一个工厂创建Target,ImageViewTargetFactory在glide创建的时候就创建了。

 private final ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();

这里顺便看一下ImageViewTargetFactory工厂:

public class ImageViewTargetFactory {

    @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)");
        }
    }
}

这里会根据条件分别创建GlideDrawableImageViewTargetBitmapImageViewTargetDrawableImageViewTarget

回到into方法,

    public <Y extends Target<TranscodeType>> Y into(Y target) {
        // 判断当前请求状态
        Request previous = target.getRequest();
		// 清除之前的请求状态
        if (previous != null) {
            previous.clear();
            requestTracker.removeRequest(previous);
            previous.recycle();
        }
		
        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        // 运行请求
        requestTracker.runRequest(request);

        return target;
    }

这里最后主要调用了requestTracker.runRequest(request);方法。

进入RequestTracker类的runRequest方法:

public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
        request.begin();
    } else {
        pendingRequests.add(request);
    }
}

RequestTracker类有个Set集合存储请求,有个List集合存储等待请求,这个后续研究。

进入request.begin();方法,会进入Request 的接口类,找到其实现类GenericRequest类:

    @Override
    public void begin() {
        ......
        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            // 确定好图片大小之后,开始处理
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            target.getSize(this);
        }

        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
            // 加载占位图,后续分析
            target.onLoadStarted(getPlaceholderDrawable());
        }
       ......
    }

这里主要做了两件事:1、确定好图片大小之后,开始处理;2、加载占位图,这个后续分析。

进入onSizeReady方法:

    @Override
    public void onSizeReady(int width, int height) {
        ......
        // 判断是否是等待图片大小处理
        if (status != Status.WAITING_FOR_SIZE) {
            return;
        }
        // 把状态改为运行
        status = Status.RUNNING;
		
        width = Math.round(sizeMultiplier * width);
        height = Math.round(sizeMultiplier * height);

        ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
        // 获取处理数据的DataFetcher
        final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
		......
        // 获取解码器
        ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
       	......
        loadedFromMemoryCache = true;
        // 开始加载数据
        loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
                priority, isMemoryCacheable, diskCacheStrategy, this);
        loadedFromMemoryCache = resource != null;
        ......
    }

从上面注释得知,前面处理了一些逻辑之后,最终调用的是engine.load方法,

进入engine.load方法:

  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);
   		......
		// 从活动内存缓存中获取,即正在使用的图片
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
     	......
        EngineJob current = jobs.get(key);
     	......
        // 创建工作引擎
        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);
        engineJob.addCallback(cb);
        // 开始执行线程
        engineJob.start(runnable);
		......
        return new LoadStatus(cb, engineJob);
    }

这段代码主要执行了:

  • 判断是否主线程
  • 创建唯一标识,缓存用
  • 从内存缓存中获取,不正在使用的内存缓存
  • 从活动内存缓存中获取,即正在使用的图片
  • 创建工作引擎
  • 创建解码器
  • 创建执行线程
  • 开始执行线程

其中缓存加载机制,这里暂不分析,主要分析开始执行线程步骤。

进入EngineJob类的engineJob.start(runnable);方法

public void start(EngineRunnable engineRunnable) {
    this.engineRunnable = engineRunnable;
    future = diskCacheService.submit(engineRunnable);
}

从代码得知,执行了diskCacheService.submit(engineRunnable);方法:

diskCacheService其实就是一个线程池,这里使用线程池执行异步请求,既然使用线程池了,那么可以回头看一下线程做了什么操作。

回到创建执行线程EngineRunnable类,该类继承Runnable接口,找到run方法:

    @Override
    public void run() {
        if (isCancelled) {
            return;
        }

        Exception exception = null;
        Resource<?> resource = null;
        ...
        // 主要是这行,解码
      	resource = decode();
		...
        // 取消就释放资源
        if (isCancelled) {
            if (resource != null) {
                resource.recycle();
            }
            return;
        }

        if (resource == null) {
            // 失败回调
            onLoadFailed(exception);
        } else {
            // 完成回调
            onLoadComplete(resource);
        }
    }

进入decode()方法,如下:

private Resource<?> decode() throws Exception {
    // 是否从缓存中解码
    if (isDecodingFromCache()) {
        return decodeFromCache();
    } else {
        // 从资源中解码
        return decodeFromSource();
    }
}

这里不做缓存分析,进入decodeFromSource()方法:

private Resource<?> decodeFromSource() throws Exception {
    return decodeJob.decodeFromSource();
}

进入decodeJob.decodeFromSource()方法:

public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}

这里调用了decodeSource()方法,进入该方法:

private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        ......
        // 从网络加载数据
        final A data = fetcher.loadData(priority);
      	......
        // 解码数据
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

这里主要做了两件事:

  • 从网络加载数据
  • 解码数据

先分析fetcher.loadData(priority)是如何加载数据的?

进入fetcher.loadData(priority)方法,进入到DataFetcher接口,找到该接口的实现类HttpUrlFetcher类,并找到该类的loadData方法:

@Override
public InputStream loadData(Priority priority) throws Exception {
    return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}

进入loadDataWithRedirects方法:

 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.setConnectTimeout(2500);
        urlConnection.setReadTimeout(2500);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);
        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");
            .......
            URL redirectUrl = new URL(url, redirectUrlString);
            return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else {
           ......
        }
    }

这里就开启了大家所熟悉的HttpURLConnection网络请求。

再分析decoded = decodeFromSourceData(data);

private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    // 是否开启了磁盘缓存,开启之后就把数据解码并缓存到磁盘
    if (diskCacheStrategy.cacheSource()) {
        decoded = cacheAndDecodeSourceData(data);
    } else {
        ......
        // 没有开启磁盘缓存就直接返回解码数据
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
        ......
    }
    return decoded;
}

如何解码的,这里不做分析。从网络下载完数据,就返回到EngineRunnable的run方法,run方法最后调用了onLoadComplete(resource);将数据通过回调返回出去。

进入onLoadComplete方法:

private void onLoadComplete(Resource resource) {
    manager.onResourceReady(resource);
}

这里调用了EngineRunnableManageronResourceReady方法。

Glide如何将数据设置到ImageView上的?

下面分析Glide是如何将数据设置到ImageView上的。

进入onResourceReady方法,到ResourceCallback接口,从前面的一段代码可以得知:

EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);

需要找到EngineRunnableManager的实现类EngineJobonResourceReady方法:

@Override
public void onResourceReady(final Resource<?> resource) {
    this.resource = resource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();这里使用handler将子线程消息发送到主线程,所以下一步在本类找到handleMessage方法:

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;
    }
}

找到匹配MSG_COMPLETE的执行方法job.handleResultOnMainThread();

private void handleResultOnMainThread() {
    ......
        engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;
    ......
        // 计数
        engineResource.acquire();
    listener.onEngineJobComplete(key, engineResource);
    // 这里使用了发布订阅模式,将消息发送到订阅者
    for (ResourceCallback cb : cbs) {
        if (!isInIgnoredCallbacks(cb)) {
            engineResource.acquire();
            cb.onResourceReady(engineResource);
        }
    }
    // 释放资源
    engineResource.release();
}

这里需要注意的是下面这个for循环,这里其实使用了发布订阅模式,遍历所有订阅了ResourceCallback的订阅者,然后将engineResource发布出去。

   for (ResourceCallback cb : cbs) {
        if (!isInIgnoredCallbacks(cb)) {
            engineResource.acquire();
            cb.onResourceReady(engineResource);
        }
    }

这里分析一下cbs,这里要从前面说起:

进入GenericRequestonSizeReady方法:

  @Override
    public void onSizeReady(int width, int height) {
        ......
        loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
       ......
    }

这里engine.load方法的最后一个参数传的是this。点进去load方法如下:

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) 

其实最后一个参数就是ResourceCallback接口,而该方法中调用了engineJob.addCallback(cb);将ResourceCallback添加到EngineJob类的cbs集合中:

private final List<ResourceCallback> cbs = new ArrayList<ResourceCallback>();
public void addCallback(ResourceCallback cb) {
    Util.assertMainThread();
    if (hasResource) {
        cb.onResourceReady(engineResource);
    } else if (hasException) {
        cb.onException(exception);
    } else {
        cbs.add(cb);
    }
}

添加到集合中,最后再从集合中将每一个ResourceCallback进行通知,其实就是使用了发布—订阅模式。

回到GenericRequestonSizeReady方法中,engine.loadthis参数,表示当前实现了ResourceCallback接口,查看GenericRequest类可以发现确实如此:

public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
        ResourceCallback {}

那么必然实现了ResourceCallback接口中的方法,其实一个是成功回调,一个是失败回调:

public interface ResourceCallback {
    void onResourceReady(Resource<?> resource);
    void onException(Exception e);
}

然后在找到GenericRequest中对ResourceCallback接口的实现方法onResourceReady

    @Override
    public void onResourceReady(Resource<?> resource) {
       ......
        Object received = resource.get();
		......
        onResourceReady(resource, (R) received);
    }

这里又调用了两个入参的onResourceReady方法:

    private void onResourceReady(Resource<?> resource, R result) {
        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.onResourceReady(result, animation);
        }
		......
        notifyLoadSuccess();
    }

最终调用了target.onResourceReady(result, animation);方法,将结果传给了target,那么target又是如何将数据设置给ImageView的呢?下面就分析一下。

回忆一下最开始的地方,在执行DrawableRequestBuilder类的into方法的时候,调用了父类GenericRequestBuilder的into方法,

    public Target<TranscodeType> into(ImageView view) {
        ......
        return into(glide.buildImageViewTarget(view, transcodeClass));
    }

这里调用了into(glide.buildImageViewTarget(view, transcodeClass));方法,这样需要关注一下glide.buildImageViewTarget(view, transcodeClass)方法,意思是创建一个ImageViewTarget

进入buildImageViewTarget方法:

    <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
        return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
    }

进入imageViewTargetFactory.buildTarget(imageView, transcodedClass);方法:

  @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)");
        }
    }

这里有GlideDrawableImageViewTargetBitmapImageViewTargetDrawableImageViewTarget三种类型的target,查看他们的源码可以看到都继承自ImageViewTarget方法,并且都实现了setResource方法

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
    ......
    @Override
    protected void setResource(Drawable resource) {
        // 设置Drawable图片
       view.setImageDrawable(resource);
    }
}
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
 	......
    @Override
    protected void setResource(Bitmap resource) {
        // 设置Bitmap图片
        view.setImageBitmap(resource);
    }
}
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
    ......
    @Override
    protected void setResource(GlideDrawable resource) {
        // 设置Drawable图片
        view.setImageDrawable(resource);
    }

再找到他们的父类ImageViewTarget

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
    ......
    @Override
    public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
        if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
            setResource(resource);
        }
    }
    
    protected abstract void setResource(Z resource);

ImageViewTarget类可以发现实现了onResourceReady方法,这里调用了setResource抽象方法,在子类中会执行给ImageView加载图片的操作。

这里追本溯源,再找到ImageViewTarget类的父类ViewTarget,然后是BaseTarget,最后是Target接口。
最后来一张加载流程时序图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ruiurrui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值