前言
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的对象类型不一样。