Glide图片框架使用详细介绍(五)之Glide-源码详解(1)

最大内存:如果是低配手机,就每个进程可用的最大内存乘以0.33,否则就每个进程可用的最大内存乘以0.4

//MemorySizeCalculator.java

int screenSize = screenDimensions.getWidthPixels() * screenDimensions.getHeightPixels()

  • BYTES_PER_ARGB_8888_PIXEL;(宽4)

int targetPoolSize = screenSize * BITMAP_POOL_TARGET_SCREENS;(宽4*4)

int targetMemoryCacheSize = screenSize * MEMORY_CACHE_TARGET_SCREENS;(宽4*2)

//判断是否超过最大值,否则就等比缩小

if (targetMemoryCacheSize + targetPoolSize <= maxSize) {

memoryCacheSize = targetMemoryCacheSize;

bitmapPoolSize = targetPoolSize;

} else {

int part = Math.round((float) maxSize / (BITMAP_POOL_TARGET_SCREENS + MEMORY_CACHE_TARGET_SCREENS));

memoryCacheSize = part * MEMORY_CACHE_TARGET_SCREENS;

bitmapPoolSize = part * BITMAP_POOL_TARGET_SCREENS;

}

targetPoolSize 和 targetMemoryCacheSize 之和不能超过maxSize 否则就等比缩小

//GlideBuilder.java

memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());

内存缓存用的是targetMemoryCacheSize (即一般是缓存大小是屏幕的宽 * 高 * 4 * 2)

2.BitmapPool 图片池 LruBitmapPool

int size = calculator.getBitmapPoolSize();

bitmapPool = new LruBitmapPool(size);

图片池用的是targetPoolSize(即一般是缓存大小是屏幕的宽*高*4*4)

3.DecodeFormat 图片格式

DecodeFormat DEFAULT = PREFER_RGB_565

默认是RGB_565

4.Engine 引擎类

//GlideBuilder.java

engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);

engine 里面主要参数

内存缓存 memoryCache

本地缓存 diskCacheFactory

处理源资源的线程池 sourceService

处理本地缓存的线程池 diskCacheService

(1)memoryCache:内存缓存 LruBitmapPool

上面已经做了介绍

(2)diskCacheFactory:本地缓存 DiskLruCacheFactory

//DiskCache.java

/** 250 MB of cache. */

int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;

String DEFAULT_DISK_CACHE_DIR = “image_manager_disk_cache”;

默认大小:250 MB

默认目录:image_manager_disk_cache

(3)sourceService 处理源资源的线程池 (ThreadPoolExecutor的子类)

final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());//获得可用的处理器个数

sourceService = new FifoPriorityThreadPoolExecutor(cores);

线程池的核心线程数量等于获得可用的处理器个数

(4)diskCacheService 处理本地缓存的线程池 (ThreadPoolExecutor的子类)

diskCacheService = new FifoPriorityThreadPoolExecutor(1);

线程池的核心线程数量为1

二.with方法

with方法有很多重载,最后会返回一个RequestManager

//Glide.java

/**

  • @see #with(android.app.Activity)

  • @see #with(android.app.Fragment)

  • @see #with(android.support.v4.app.Fragment)

  • @see #with(android.support.v4.app.FragmentActivity)

  • @param context Any context, will not be retained.

  • @return A RequestManager for the top level application that can be used to start a load.

*/

public static RequestManager with(Context context) {

RequestManagerRetriever retriever = RequestManagerRetriever.get();

return retriever.get(context);

}

就算你传入的是Context ,这里也会根据你Context 实际的类型,走不同的分支

//RequestManagerRetriever.java

public RequestManager get(Context context) {

if (context == null) {

throw new IllegalArgumentException(“You cannot start a load on a null Context”);

} else if (Util.isOnMainThread() && !(context instanceof Application)) {

if (context instanceof FragmentActivity) {

return get((FragmentActivity) context);

} else if (context instanceof Activity) {

return get((Activity) context);

} else if (context instanceof ContextWrapper) {

return get(((ContextWrapper) context).getBaseContext());

}

}

return getApplicationManager(context);

}

这里以FragmentActivity为例,最后会创建一个无界面的Fragment,即SupportRequestManagerFragment ,让请求和你的activity的生命周期同步

//RequestManagerRetriever.java

public RequestManager get(FragmentActivity activity) {

if (Util.isOnBackgroundThread()) {

return get(activity.getApplicationContext());

} else {

assertNotDestroyed(activity);

FragmentManager fm = activity.getSupportFragmentManager();

return supportFragmentGet(activity, fm);

}

}

RequestManager supportFragmentGet(Context context, FragmentManager fm) {

SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);

RequestManager requestManager = current.getRequestManager();

if (requestManager == null) {

requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());

current.setRequestManager(requestManager);

}

return requestManager;

}

这里需要注意一下,如果你是在子线程调用with方法,或者传入的Context是Application的话,请求是跟你的Application的生命周期同步

//RequestManagerRetriever.java

private RequestManager getApplicationManager(Context context) {

// Either an application context or we’re on a background thread.

if (applicationManager == null) {

synchronized (this) {

if (applicationManager == null) {

// Normally pause/resume is taken care of by the fragment we add to the fragment or activity.

// However, in this case since the manager attached to the application will not receive lifecycle

// events, we must force the manager to start resumed using ApplicationLifecycle.

applicationManager = new RequestManager(context.getApplicationContext(),

new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());

}

}

}

return applicationManager;

}

三.load方法

这里方法也有很多重载

//RequestManager.java

public DrawableTypeRequest load(String string) {

return (DrawableTypeRequest) fromString().load(string);

}

但是最后都会返回一个DrawableTypeRequest (继承了DrawableRequestBuilder)

DrawableRequestBuilder就是支持链式调用的一个类,我们平时有类似的需求的时候也可以模仿这样的处理方式,把一些非必须参数用链式调用的方式来设置

四.into方法

//GenericRequestBuilder.java

public Target into(ImageView view) {

Util.assertMainThread();

if (view == null) {

throw new IllegalArgumentException(“You must pass in a non null View”);

}

if (!isTransformationSet && view.getScaleType() != null) {

switch (view.getScaleType()) {

case CENTER_CROP:

applyCenterCrop();

break;

case FIT_CENTER:

case FIT_START:

case FIT_END:

applyFitCenter();

break;

// C A S E S − O M I T T E D CASES-OMITTED CASESOMITTED

default:

// Do nothing.

}

}

return into(glide.buildImageViewTarget(view, transcodeClass));

}

这里有三点需要注意的:

1.Util.assertMainThread();这里会检查是否主线程,不是的话会抛出异常,所以into方法必须在主线程中调用.

2.当你没有调用transform方法,并且你的ImageView设置了ScaleType,那么他会根据你的设置,对图片做处理(具体处理可以查看DrawableRequestBuilder的applyCenterCrop或者applyFitCenter方法,我们自己自定义BitmapTransformation也可以参考这里的处理).

3.view在这里被封装成一个Target.

//GenericRequestBuilder.java

public <Y extends Target> Y into(Y target) {

Util.assertMainThread();

if (target == null) {

throw new IllegalArgumentException(“You must pass in a non null Target”);

}

if (!isModelSet) {

throw new IllegalArgumentException(“You must first set a model (try #load())”);

}

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;

}

这里可以看到控件封装成的Target能够获取自身绑定的请求,当发现之前的请求还在的时候,会把旧的请求清除掉,绑定新的请求,这也就是为什么控件复用时不会出现图片错位的问题(这点跟我在Picasso源码中看到的处理方式很相像).

接着在into里面会调用buildRequest方法来创建请求

//GenericRequestBuilder.java

private Request buildRequest(Target target) {

if (priority == null) {

priority = Priority.NORMAL;

}

return buildRequestRecursive(target, null);

}

//GenericRequestBuilder.java

private Request buildRequestRecursive(Target target, ThumbnailRequestCoordinator parentCoordinator) {

if (thumbnailRequestBuilder != null) {

Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);

Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);

coordinator.setRequests(fullRequest, thumbRequest);

return coordinator;

} else if (thumbSizeMultiplier != null) {

ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);

Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);

Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);

coordinator.setRequests(fullRequest, thumbnailRequest);

return coordinator;

} else {

// Base case: no thumbnail.

return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);

}

}

1.这里就是请求的生成,buildRequestRecursive里面if有三个分支,这里是根据你设置thumbnail的情况来判断的,第一个是设置缩略图为新的请求的情况,第二个是设置缩略图为float的情况,第三个就是没有设置缩略图的情况.

前两个设置了缩略图的是有两个请求的,fullRequest和thumbnailRequest,没有设置缩略图则肯定只有一个请求了.

2.请求都是通过obtainRequest方法生成的(这个简单了解一下就行)

//GenericRequestBuilder.java

private Request obtainRequest(Target target, float sizeMultiplier, Priority priority,

RequestCoordinator requestCoordinator) {

return GenericRequest.obtain(…);

}

REQUEST_POOL是一个队列,当队列中有,那么就从队列中取,没有的话就新建一个GenericRequest

//GenericRequest.java

public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(…) {

GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();

if (request == null) {

request = new GenericRequest<A, T, Z, R>();

}

request.init(…);

return request;

}

回到into方法:当创建了请求后runRequest会调用Request的begin方法,即调用GenericRequest的begin方法

//GenericRequestBuilder.java

public <Y extends Target> Y into(Y target) {

Request request = buildRequest(target);

requestTracker.runRequest(request);

}

//GenericRequest.java

public void begin() {

if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

onSizeReady(overrideWidth, overrideHeight);

} else {

target.getSize(this);

}

}

最终会调用Engine的load方法

//GenericRequest.java

public void onSizeReady(int width, int height) {

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,

priority, isMemoryCacheable, diskCacheStrategy, this);

}

我们先看load方法的前面一段:

1.首先会尝试从cache里面取,这里cache就是Glide的构造函数里面的MemoryCache(是一个LruResourceCache),如果取到了,就从cache里面删掉,然后加入activeResources中

2.如果cache里面没取到,就会从activeResources中取,activeResources是一个以弱引用为值的map,他是用于存储使用中的资源.之所以在内存缓存的基础上又多了这层缓存,是为了当内存不足而清除cache中的资源中,不会影响使用中的资源.

//Engine.java

public <T, Z, R> LoadStatus load(…) {

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);

if (cached != null) {

cb.onResourceReady(cached);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey(“Loaded resource from cache”, startTime, key);

}

return null;

}

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);

if (active != null) {

cb.onResourceReady(active);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey(“Loaded resource from active resources”, startTime, key);

}

return null;

}

}

load方法接着会通过EngineJobFactory创建一个EngineJob,里面主要管理里两个线程池,diskCacheService和sourceService,他们就是Glide构造函数中Engine里面创建的那两个线程池.

//Engine.java

public <T, Z, R> LoadStatus load(…) {

EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);

}

//Engine.java

static class EngineJobFactory {

private final ExecutorService diskCacheService;

private final ExecutorService sourceService;

private final EngineJobListener listener;

public EngineJobFactory(ExecutorService diskCacheService, ExecutorService sourceService,

EngineJobListener listener) {

this.diskCacheService = diskCacheService;

this.sourceService = sourceService;

this.listener = listener;

}

public EngineJob build(Key key, boolean isMemoryCacheable) {

return new EngineJob(key, diskCacheService, sourceService, isMemoryCacheable, listener);

}

}

接着说load方法,前面创建了EngineJob,接着调用EngineJob的start方法,并将EngineRunnable放到diskCacheService(处理磁盘缓存的线程池里面运行),接着线程池就会调用EngineRunnable的run方法.

//Engine.java

public <T, Z, R> LoadStatus load(…) {

EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);

jobs.put(key, engineJob);

engineJob.addCallback(cb);

engineJob.start(runnable);

}

//EngineJob.java

public void start(EngineRunnable engineRunnable) {

this.engineRunnable = engineRunnable;

future = diskCacheService.submit(engineRunnable);

}

//EngineRunnable.java

public void run() {

try {

resource = decode();

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, “Exception decoding”, e);

}

exception = e;

}

if (resource == null) {

onLoadFailed(exception);

} else {

onLoadComplete(resource);

}

}

run里面调用的是decode()方法,里面会尝试先从磁盘缓存中读取,如果不行就从源资源中读取

//EngineRunnable.java

private Resource<?> decode() throws Exception {

if (isDecodingFromCache()) {

//第一次会走这

return decodeFromCache();//从磁盘缓存中读取

} else {

return decodeFromSource();//从源资源中读取

}

}

我们先来看从磁盘中读取的策略

//EngineRunnable.java

private Resource<?> decodeFromCache() throws Exception {

Resource<?> result = null;

try {

result = decodeJob.decodeResultFromCache();

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Exception decoding result from cache: " + e);

}

}

if (result == null) {

result = decodeJob.decodeSourceFromCache();

}

return result;

}

我们可以看到这里先尝试读取处理后的图片(Result),然后再尝试读取原图,但是这里面具体逻辑会根据你设置的磁盘缓存策略来决定是否真的会读取处理图和原图

那么我们再回到EngineRunnable的run()方法中

总结

【Android 详细知识点思维脑图(技能树)】

我个人是做Android开发,已经有十来年了,目前在某创业公司任职CTO兼系统架构师。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

最后,赠与大家一句话,共勉!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Exception decoding result from cache: " + e);

}

}

if (result == null) {

result = decodeJob.decodeSourceFromCache();

}

return result;

}

我们可以看到这里先尝试读取处理后的图片(Result),然后再尝试读取原图,但是这里面具体逻辑会根据你设置的磁盘缓存策略来决定是否真的会读取处理图和原图

那么我们再回到EngineRunnable的run()方法中

总结

【Android 详细知识点思维脑图(技能树)】

[外链图片转存中…(img-ASRKdjie-1715830834932)]

我个人是做Android开发,已经有十来年了,目前在某创业公司任职CTO兼系统架构师。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-tYHYi3jD-1715830834935)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

最后,赠与大家一句话,共勉!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值