前言
Glide 是如何加载图片的,如何优化的,你可能知道是处理了,但是具体处理策略没准说不上来,只能想到常规Bitmap的那几个处理方案(其实大多框架对图片处理都是调用那几个Bitmap API,Glide整体流程也看过,对于这部分没有仔细看,之前着重点在于如何绑定生命周期、如何管理缓存、如何传递图片给控件显示,至于编解码、图片没有仔细关注,还有一些线程锁的使用、线程唤醒处理
这次是带着问题查看图片处理流程的,然后一不小心整个流程又全看了一遍,干脆记录一下,方便以后看,有些知识点可能并没有写全(后文大部分是贴代码),后续遗漏的抽空继续补充。
目录
一、Bitmap加载必备知识
在日常开发中,我们都会留意Bitmap的相关加载,如果产生内存溢出,报类似 java.lang.OutofMemoryError: bitmap size exceeds VM budget ,这是我们不希望看到的。
1、BitmapFactory创建Bitmap的四种方法
decodeFile、decodeResource、decodeStream、decodeByteArray,其中decodeFile和decodeResource又间接调用了decodeStream方法,最终是在native方法实现的。
2、Bitmap占用内存计算
将图片放到res/drawable-xhdpi目录下,图片分辨率 600 * 600 ,大小65kb。
通过 bitmap.getAllocationByteCount() 方法,你可以获取到内存大小位 1440000,为什么呢?
因为默认BitmapFactory使用的是Bitmap.Config.ARGB_8888的存储方式来加载图片内容的,每个像素占了 4 个字节,
内存大小 = 宽 * 高 * 4 = 600 * 600 * 4 = 1440000
假如我们将该图片移动到res/drawable-hdpi下,得到的内存大小是 2560000,内存上涨了?为什么呢?
因为在BitmapFactory 在解析图片的过程中,会根据当前设备屏幕密度和图片所在的 drawable 目录来做一个对比,根据这个对比值进行缩放操作。
实际内存大小 = 600 * (320 / 240 )* 600 * (320 / 240) * 4 = 2560000
drawable目录对应的屏幕密度如下
| 目录 | drawable-mdpi | drawable-hdpi | drawable-xhdpi | drawable-xxhdpi | drawable-xxxhdpi |
|---|---|---|---|---|---|
| density | 1 | 1.5 | 2 | 3 | 4 |
| densityDpi | 160 | 240 | 320 | 480 | 640 |
计算规则如下
内存大小 = 宽 *(屏幕的desityDip / drawable所在文件的desityDip) * 高 (屏幕的desityDip / drawable所在文件的desityDip)*4 (Bitmap.Config.ARGB_8888 每个像素占4个字节)
如果该图片移动到 assets 中,内存大小依旧是 1440000,系统并不会对assets目录中的图片进行缩放操作。
3、Config参数
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565
通过上面 inPreferredConfig 指定像素占用的字节,RGB_565 占用 2 个字节,内存相对ARGB_8888直接减半。
4、inSampleSize参数
options.inSampleSize =2;
采样率,值为 2 ,采样后的宽/高 均是原图大小的 1/2,像素为原图的 1/4,内存也占原图的 1/4。值为1就是原始大小,如果值为 3 ,系统会默认向下取整,采样后 和 值为2的表现一样。
原理: 对原图取样,行列方向每隔 inSampleSize 大小格取一个像素,最后合并为一张图
5、尺寸压缩
public static void compressBitmapToFileBySize(Bitmap bmp,File file){
int ratio = 4;//压缩尺寸的倍数,值越大,图片的尺寸就越小
Bitmap result = Bitmap.createBitmap(bmp.getWidth()/ratio,bmp.getHeight()/ratio,Bitmap.Config.ARGE_8888);
Canvas canvas = new Canvas(result);
RectF rect = new RectF(0,0,bmp.getWidth()/ratio,bmp.getHeight()/ratio);
canvas.drawBitmap(bmp,null,rect,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
result.compress(Bitmap.CompressFormat.JPEG,100,baos);
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
}
尺寸压缩是通过减少单位尺寸的像素值,真正意义上的降低像素
6、质量压缩
public static void compressImageToFile(Bitmap bmp,File file) throws Exception{
int quality = 50;// 0 ~ 100
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG,quality,baos);
FileOutputSteam fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
}
通过算法扣掉了图片中的一些某些点附近相近的像素,达到降低质量减少文件大小的目的
7、Bitmap复用
像一些页面中有很多图片,通过按钮控件切换,可能会使用到。
按正常写法,我们可能会直接新建出两个Bitmap
BitmapFactory.decodeResource(getResources(),R.drawable.icon,options);
如果不作为全局变量存放这两个Bitmap,那就面临创建新的Bitmap后,前一个Bitmap被GC回收,频繁操作对应于Bitmap不断创建和销毁,导致频繁GC、内存抖动,这明显不可取。
如果作为全局变量来存储这两个Bitmap,如果该页面有很多个呢?不可取
所以需要使用 Options.inBitmap 来进行优化
int[] resIds ={R.drawable.icon,R.drawable.icon2};
Bitmap resultBitmap = getBitmap();
private Bitmap getBitmap(){
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResource(),resIds[resIndex],options);
if(canUserForInBitmap(resultBitmap,options)){ //是否可以复用内存
options.inMutable = true; //这里如果不置为 true 的话,BitmapFactory 将不会重复利用 Bitmap 内存,并输出相应 warning 日志
options.inBitmap = resultBitmap;//赋值 为之前创建的 resultBitmap 对象,避免重新分配内存
}
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(),resIds[resIndex],options);
}
为什么复用的时候还去判断是否能复用呢?
因为在Android 4.4 之前,只能重用相同大小的 Bitmap内存区域,Android 4.4 之后只要这块内存比将要分配内存的Bitmap大就可以重用
public static boolean canUserForInBitmap(Bitmap bitmap,BitmapFactory.Options targetOptions){
int width = targetOptions.outWidth / Math.max(targetOptions.inSampleSize,1);
int height = targetOptions.outHeight / Math.max(targetOptions.inSampleSize,1);
int byteCount = widht * height* getBytesPerPixel(bitmap.getConfig());
return byteCount <= bitmap.getAllocationByteCount();
}
private static int getBytesPerPixel(Bitmap.Config config){
int bytePerPixel;
switch(config){
case ALPHA_8:
bytePerPixel= 1;
break;
case RGB_565:
case ARGB_4444:
bytePerPixel = 2;
break;
default:
bytePerPixel= 4;
break;
}
return bytePerPixel;
}
8、recycle 方法
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}
Bitmap内存分配 在 三、Android内存优化 提到过
-
在
Android 3.0前,Bitmap对象放在堆,像素数据放在Native种,如果不手动调用recycle,Bitmap Native内存回收完全依赖 finalize 回调,这个时机很不可控 -
Android 3.0到Android 7.0,将Bitmap对象和像素数据一起放在Java堆中,这样就算不调用recycle,Bitmap内存也会随着对象一起被回收。不过Bitmap是内存消耗大户,放到Java堆中,App最大内存会不会立马被使用完了,而且放到堆中,不好好优化,还会出现频繁GC -
Android 8.0,将像素数据放到了Native中,实现了和对象一起快速释放回收。并且还增加了硬件位图Hardware Bitmap,减少了图片内存,提升了绘制效率
那么问题来了,recycle() 方法需不需要调用?目前开发是不需要主动调用的,毕竟api target 都在 Android 3.0 ,Android 3.0 后,垃圾回收器会自动收集不可用的Bitmap对象进行回收。
9、图片分片显示
加载的图片很长时,例如滚动截图生成的图片,我们可能就需要使用到BitmapRegionDecoder 类。
使用规则,具体用法可仔细搜索,或者见 Android高清加载巨图方案 拒绝压缩图片、网上成熟的例子,添加touch事件动态设置Bitmap显示的区域 LargeImageView
首先需要使用 BitmapRegionDecoder 将图片加载到内存中,图片可以以绝对路径、文件描述符、输入流的方式传递给 BitmapRegionDecoder
//以下代码是显示图片左上角(200,200) 区域
private void showRegionImage(){
try{
InputStream is = getAssets().open("big.png");
//设置显示图片的中心区域
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is,false);
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = decoder.decodeRegion(new Rect(0,0,200,200), options);
regionImage.setImageBitmap(bitmap);
}catch(IOException e){
}
}
10、注意点
由于加载大图时不能真正加载图片到内存,然后获取原图宽高,所以对一些参数的设置,例如 inSampleSzie 等,需要设置 inJustDecodeBounds 参数,实现只解析图片的宽高信息,并不会真正的加载图片,所以这个操作时轻量级
int inSampleSize = 8;//数值越高,图片像素越低
BitmapFactory.Options options = new BitmapFatcory.Options();
options.inJustDecodeBounds = true;//为true不会真正加载图片
options.inSampleSize = inSampleSize;//这里计算采样率
.......//各种操作
options.inJustDecodeBounds = false; //重新加载
Bitmap bitamp = BitampFactory.decodeFile(filePath,options);
注意避免 先加载原来的Bitmap,然后再进行一系列的优化。
11、IOS拍照1M的图片为什么比Android拍照出来的5M图片还清楚
-
1995年出来的JPEG图片处理引擎,用于最初的PC上面 -
2005年Skia开发了一套Skia引擎,基于JPEG的二次开发,便于浏览器的使用 -
Android使用的是Skia引擎,不过是阉割版的Skia引擎,谷歌去掉了内部的 哈夫曼算法,采用定长编码算法,导致图片处理后文件变大了。 -
为什么会替换成定长编码算法,因为
Android手机上,CPU和内存都非常吃紧,而哈夫曼算法非常吃CPU
二、Glide加载图片流程
这里对加载流程的详细过程不过多分析,大概流程如下图,至于基于Glide哪个版本看的忘了。
大概流程就是通过一个SupportRequestManagerFragment,绑定了LifeCycle,后续在onDestroy时会触发回收弱引用缓存(ActiveResources持有的Map),放回到内存缓存(LruResourceCache)中。
图片处理利用了Bitmap的各种优化参数 inBitmap、inDensity等。
三、Glide加载图片
Glide版本4.5.0
1、Glide的with方法
就是拿到RequestManager对象,在RequestManager的构造函数中,会构造一个默认的RequestOptions,顾名思义,这是一个对图片操作的类
这个过程中会利用SupportRequestManagerFragment来绑定到当前Activity中,并通过以下的代码片段避免了类似并发场景获取Fragment
SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
通过with阶段也初始化了各种配置信息,可以大概看一下
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
// GlideModule的相关配置
//获取注解@GlideModule
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
.......
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
//这里就是一些缓存的配置
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
builder.build
public Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
}
2、Glide的load方法
返回了 RequestBuilder对象,例如load(String url)
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;//设置的 url
isModelSet = true; //url已经设置
return this;
}
可以看到这两个方法并没有编解码的设置,那就是在into方法中
3、Glide的into方法
构建请求、发起请求、取缓存、读缓存、服务器请求、设置缓存、显示图片的一个过程
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
// 这里看官方注释也知道,会根据ImageView的设置来保存对应的图片缩放类型
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
......
}
}
//下面参数 transcodeClass 是Drawable.class 或者是 Bitmap.class ,这个参数是 load方法时构建RequestBuilder设置好的
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
继续看一下 glideContext.buildImageTarget方法
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
这里 如果调用 load(String url),那么返回的时DrawableImageViewTarget,如果是 loadBitmap 那么返回的是BitmapImageViewTarget
接下来看 into 方法,参数target 为上面说到的两种ImageViewTarget
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
//注释 1 使用Glide必须调用load方法,否则这里会报错
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
//注释 2 建立请求
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
//注释 3 内部为对View设置Tag
target.setRequest(request);
//注释 4 跟踪请求
requestManager.track(target, request);
return target;
}
第一问,Request 内部是如何构建的,真实类形是什么?
注释 2 建立请求这里,内部会调用到 buildRequestRecursive 方法
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
Request mainRequest =
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions);
if (errorRequestCoordinator == null) {
return mainRequest;
}
.....
}
我们继续看一下 buildThumbnailRequestRecursive 方法
private Request buildThumbnailRequestRecursive(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
//如果是下面这样调用Glide加载图片的话,缩略图 thumbnailBuilder = null,thumbSizeMultiplier = null
//Glide.witt(this).load("xxx").thumbnail().into(view)
//一般都会调用thumbnail传递浮点数,即thumbSizeMultiplier,0.1f就代表原图的 10%,所以这里直接分析第二个
if (thumbnailBuilder != null) {
......
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
//获取一个正常请求的对象
Request fullRequest =
obtainRequest(
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
RequestOptions thumbnailOptions = requestOptions.clone()
.sizeMultiplier(thumbSizeMultiplier);
//获取一个缩略图请求对象
Request thumbnailRequest =
obtainRequest(
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight);
// ThumbnailRequestCoordinator coorinator是一个能同时加载缩略图和正常图的请求对象
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
}
}
真正建立请求时都调用了 obtainRequest 方法
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListener,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
可以看到最终返回的Request是 SingleReqeust 对象,可同时进行缩略图和正常图的请求。
第二问,跟踪请求里做了些什么?
我们看最初into方法内的requestManager.track(target, request) 方法
void track(Target<?> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
直接看 requestTracker.runRequest 方法
public void runRequest(Request request) {
requests.add(request);
//不是暂停状态则开始请求
if (!isPaused) {
//之前已经分析了,Request的真实对象类型是 SingleRequest
request.begin();
} else {
pendingRequests.add(request);
}
}
SingleRequest 的 begin方法
public void begin() {
.....
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
.......
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
//注释
//如果在使用Glide时,通过apply传递了一个新的RequestOptions,并且设置了oveerideWith和overrideHeight
//就会执行 onSizeReady
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//无效就会等到设置ViewTreeObervable 等待通知拿到,然后调用 onSizeReady
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//注释
//占位图设置
target.onLoadStarted(getPlaceholderDrawable());
}
}
通过上面代码以及分析可以了解到,begin方法,最终会执行到 onSizeReady方法,然后会设置占位图。
关键代码在onSizeReady中
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
....
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
...
}
上述代码中的 Engine 是一个负责加载、管理活跃缓存和缓存资源的类,我们进入load方法一探究竟,分析直接卸载代码内,这里对缓存的Key和Value说明可以看看 常用集合类相关知识点总结 LruCahche部分,后续代码分析中也有体现,根据不同策略,构建的Key的对象类型不一样。