glide源码分析
李保成
glide是 Bump出品的一个图片加载库
开源地址:https://github.com/bumptech/glide
glide使用方法如下:
Glide.with(this).load("http://pic9.nipic.com/20100919/5123760_093408576078_2.jpg")
.into(mImageview);
效果如下:
其总体架构如下:
with
先看一下with方法:
public static RequestManager with(@NonNull FragmentActivity
activity) {
return getRetriever(activity).get(activity);
}
作用:初始化glide,返回一个RequestManger对象
以传入的是FragmentActivity为例:
getRetriever (activity)流程如下:
initializeGlide主要做了以下工作:
1. 获取应用中带注解的GlideModule(annotationGeneratedModule),这里要解释一下GlideModule:用户自定义glide配置模块,用来修改默认的glide配置信息。如果这个为空或者可配置menifest里面的标志为true,则获取menifest里面配置的GlideModule模块(manifestModules)。
2. 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替换glide默认组件(registerComponents)
3. 各种初始化信息Glide glide = builder.build(applicationContext);
4. 看一下build的源码:
主要做了以下工作:
1)创建请求图片线程池sourceExecutor,创建硬盘缓存线程池diskCacheExecutor。动画线程池animationExecutor
2)依据设备的屏幕密度和尺寸设置各种pool的size
3)创建图片线程池LruBitmapPool,缓存所有被释放的bitmap, LruBitmapPool依赖默认的缓存策略和缓存配置。缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMap。
4)创建对象数组缓存池LruArrayPool,默认4M
5)创建LruResourceCache,内存缓存
6)注册管理任务执行对象的类(Registry),可以简单理解为:Registry是一个工厂,而其中所有注册的对象都是一个工厂员工,当任务分发时,根据当前任务的性质,分发给相应员工进行处理。
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);
}
glide参数含义:
context上下文,engine:任务和资源管理(线程池,内存缓存和硬盘缓存对象
),memoryCache:内存缓存,bitmapPool:bitmap内存缓存,后续会单独介绍。
arrayPool: connectivityMonitorFactory:回调监听,defaultRequestOptions:默认请求配置,
defaultTransitionOptions:默认过度效果
getRetriever(activity).get(activity)
然后我们看一下get(activity)的流程:
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread())
{
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm =
activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/
null,
isActivityVisible(activity));
}
}
流程图如下:
主要工作是,创建一个supportFragment(SupportRequestManagerFragment),把Request和Fragment绑定在一起,主要是生命周期。
引用xiaodanchen.github.io/2016/08/19/…
文章中的一个图片:
总结以下with中的工作主要有以下几点:
1. 初始化配置信息(包括缓存,请求线程池,大小,图片格式等等)以及glide组件,
2. 将glide和Fragment的生命周期绑定在一块。
Load
asDrawable().load(string);
先分析一下asDrawable:
最后是创建了一个RequestBuilder对象
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
最终返回RequestBuider对象
into
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType>
targetListener,
@NonNull RequestOptions
options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You
must call #load() before calling #into()");
}
options = options.autoClone();
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);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
构建Request对象,实际是在buildRequestRecursive里面创建了一个buildThumbnailRequestRecursive的对象以及errorRequestCoordinator(异常处理对象)
最后调用requestManager.track(target,
request);
track干了两件事:
void track(@NonNull Target<?> target, @NonNull Request
request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
1. 加入target目标队列(view)
2. 加入请求Request队列,如果缓存中没有开始请求数据
3.
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime
= LogTime.getLogTime();
if (model == null) {
if
(Util.isValidDimensions(overrideWidth, overrideHeight)) {
width
= overrideWidth;
height
= overrideHeight;
}
//
Only log at more verbose log levels if the user has set a fallback drawable,
because
// fallback Drawables indicate the
user expects null models occasionally.
int
logLevel = getFallbackDrawable() == null ? Log.WARN
: Log.DEBUG;
onLoadFailed(new GlideException("Received
null model"),
logLevel);
return;
}
if
(status == Status.RUNNING) {
throw
new IllegalArgumentException("Cannot restart a running request");
}
//
If we're restarted after we're complete (usually via something like a
notifyDataSetChanged
// that starts an identical request
into the same Target or View), we can simply use the
// resource and size we retrieved the
last time around and skip obtaining a new size, starting a
// new load etc. This does mean that
users who want to restart a load because they expect that
// the view size has changed will need
to explicitly clear the View or Target before starting
// the new load.
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;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
}
else {
target.getSize(this);
}
if
((status == Status.RUNNING || status ==
Status.WAITING_FOR_SIZE)
&&
canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if
(IS_VERBOSE_LOGGABLE) {
logV("finished
run method in " + LogTime.getElapsedMillis(startTime));
}
}
begin在onSizeReady执行engine.load, 先从弱引用中查找loadFromActiveResources(),如果有的话直接返回,没有再从内存中查找loadFromCache, 有的话会取出并放到ActiveResources里面,如果内存中没有,则创建engineJob(decodejob的回调类,管理下载过程以及状态)线程decodeJob,先下载,后解析,并把Job放到Hashmap里面。
流程图如下:
开启线程是从engineJob.start开始的
看一下DecodeJob线程的run方法,实际起作用的是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;
default:
throw new IllegalStateException("Unrecognized
run reason: " + runReason);
}
}
完整执行的情况下,会依次调用ResourceCacheGenerator、DataCacheGenerator和SourceGenerator中的startNext()
首次下载图片创建的是SourceGenerator:
runGenerators流程如下:
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator
!=
null
&& !(isStarted
= currentGenerator.startNext()))
{
stage =
getNextStage(stage);
currentGenerator =
getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages
and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) &&
!isStarted) {
notifyFailed();
}
// Otherwise a generator
started a new load and we expect to be called back in
// onDataFetcherReady.
}
调用了SourceGenerator的startNext方法:
1、dataToCache数据不为空的话缓存到硬盘(非第一次)
2、 在modelLoaders里面找到ModelLoder对象(每个Generator对应一个ModelLoader)
3、通过(HttpGlideUrlLoader)buildLoadData获取到实际的loadData对象(key 为URL,value创建的HttpUrlFetcher对象)
4、通过loadData对象的fetcher对象(HttpUrlFetcher)的loadData方法来获取图片数据。
5、HttpUrlFetcher 通过HttpURLConnection网络请求数据
时序图如下:
至此,算是部分完整的分析了glide的过程,后续会继续完善。
参考:
xiaodanchen.github.io/2016/08/22/…
掘金的格式还不怎么会调整,看格式完整的可以去我的github