前言
本篇作为史上最细Glide源码解读系列第一篇 , 主要对主流程进行分析
以后还会有若干篇对Glide 中 运用的设计模式 / 线程池 /图片优化/ 解码/转码/缓存 等细节的文章发布
使用
假设调用以下代码进行图片加载
Glide.with(activity).load("https/http开头的图片链接").into(imageView)
先来一张思维导图 流程分析开始 , 前排请系好安全带
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 方法主要执行以下几歩
-
- 初始化Glide
-
- 调用RequestManagerRetriever.get 构建RequestManager 并返回 RequestManager
-
- 将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 方法 , 主要分两歩
-
- 先从内存缓存中判断是否有缓存 , 如果有则从内存缓存中获取直接返回
-
- 内存缓存中没有缓存 , 就构建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 分三步
-
- 先从RESOURCE_CACHE 中去找 , 这个缓存拿到的图片已经解码过可以直接给ImageView使用, 对应的是 ResourceCacheGenerator这个类
-
- 如果 RESOURCE_CACHE 缓存中没有 , 则从 DATA_CACHE 中查找 , DATA_CACHE 缓存中缓存的是未经解码的源数据 , 需要解码才能给ImageView使用 , 对应的是DataCacheGenerator这个类
-
- 如果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 但因为传入的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 会找到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));
}
}
}
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步
-
- 将Stream 解码成 Resource
-
- 将 Resource 缓存在 RESOURCE_CACHE 中
-
- 如果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 是什么?
- dataClass是从DataCacheGenerator 通过 FileLoader 回调过来的 InputStream , 所以 dataClass = InputStream.class
- resourceClass 从 BaseRequestOptions 设置, 如果不设置默认为 Object.class
- transcodeClass 是 class RequestBuilder 的泛型TranscodeType , 由as方法指定 , 此处为Drawable.class
LoadPath<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歩
- 根据dataClass, resourceClass找到所有registeredResourceClasses
可以就只有看到Bitmap.class 满足条件 , 因为GifDrawable.class 的dataclass为ByteBuffer 不是InputStream的子类 , 所以registeredResourceClasses只有一个数据 Bitmap.class
- 根据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
- 根据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);
}
- 根据registeredTranscodeClass找到所有transcoder
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
转码表中根据registeredResourceClass =Bitmap.class 和 registeredTranscodeClass =Drawable.class 去获取 transcoder 符合条件就只有 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();
}
以上代码分两步
- 回调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);
}
- 把 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 实现
再次贴出文章开始放的图