前言
图片加载框架,相对于UniversalImageLoader,Picasso,它还支持video,Gif,SVG格式,支持缩略图请求,旨在打造更好的列表图片滑动体验。Glide有生命周期的概念(主要是对请求进行pause,resume,clear),而且其生命周期与Activity/Fragment的生命周期绑定,支持Volley,OkHttp,并提供了相应的integration libraries,内存方面也更加友好
使用
Glide基本使用:
Glide.with(this).load("https://...").into(imageView);
Glide调用基本分为三个步骤:with、load、into,那么我们就分为这三个步骤展开讨论。
with
with方法传入context作为参数,所以主要是初始化创建重要对象。with有许多重载,一般就通过参数分为两类:
applicationContext和非applicationContext。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
由此可见他们都是相同的作用,都是获取一个RequestManager实例,但既然要分开调用,说明他们所获取的RequestManager又有所区别。
那么看一下它们的get方法:
参数为applicationContext
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);
}
参数为非applicationContext
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}
以上可知,以applicationContext为参数的最终调用getApplicationManager方法获取;
以非applicationContext为参数的最终调用fragmentGet方法获取;
private RequestManager getApplicationManager(Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
当以applicationContext为参数时,用同步锁获取单例,并且添加了ApplicationLifecycle生命周期的监听,所以这个RequestManager实例的生命周期会和application相同。
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
//在fragementManager中找到RequestManagerFragment
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//若为空,则在pendingRequestManagerFragments集合中找到RequestManagerFragment
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
//若为空,则创建一个隐藏的RequestManagerFragment并绑定提交
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//创建RequestManager并与requestManagerFragment绑定
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
当传入参数为非applicationContext时,Glide会创建一个隐藏的requestManagerFragment,继承自fragment并依附于activity或fragment上,这样requestManagerFragement就和activity或fragment的生命周期一致。而requestManagerFragment中保存了requestManager对象,那么说明requestManager的生命周期和activity或fragement生命周期一致。
也就是说,如果当某个Glide加载图片请求是在Activity或fragment中进行的,当这个请求还没加载完成时,这个activity或fragemnt就暂停或销毁了,那么这个Glide图片加载请求也会随之暂停或消亡。很好的避免了资源浪费和内存泄漏。
而在传入参数为appplicationContext的Glide加载请求就会随着整个程序的推出而消亡。
这是一个很棒的设计思路,值得学习。
至此,Glide的第一步with的工作已经完成。
load
load方法里面其实也是在做一些准备工作,主要创建一些加载图片处理图片的类。不过在此之前,需要了解一下Glide加载图片的流程和几个基本概念。
Model:资源模型,表示数据来源。比如图片url,本地图片以及资源id;
Data:表示从网络或本地加载的实体图片数据流,一般是inputStream;
ModelLoader:指将model加工为Data的加载器;
Resource:将Data实体数据流解码后的Bitmap;
ResourceDecoder:将Data解码为bitmap的解码器;
TransfromedResource:将bitmap加工后(例如裁剪,缩略,模糊)后所转化的bitmap
TranscodedResource:由于图片的类型有bitmap、drawable等等,类型不同意,于是Glide就将他们统一转化为GlideBitmapDrawable,统称TranscodedResource;
Glide图片记载流程Target:Glide需要将图片显示到目标上,例如ImageView,所以Glide把显示目标封装为Target;
由上图我们可以看到,整个加载图片的流程为:
原始图片资源模型通过ModelLoader加载后变为Data数据流inputstream;然后经由ResourceDecoder解码为Resource再经由Transfrom转换加工变为TransfromedResource;之后统一类型变为TranscodedResource,最后显示到Target之上;
接下来我们再转换一下视角看一看load方法:
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<Uri> load(Uri uri) {
return (DrawableTypeRequest<Uri>) fromUri().load(uri);
}
public DrawableTypeRequest<File> load(File file) {
return (DrawableTypeRequest<File>) fromFile().load(file);
}
public DrawableTypeRequest<Integer> load(Integer resourceId) {
return (DrawableTypeRequest<Integer>) fromResource().load(resourceId);
}
由以上代码可以看出,加载图片的数据源有多种;但最终都会被转换为DrawableTypeRequest,这个类即为我们图片请求类;
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType>
它继承自DrawableRequestBuilder,有名称可知,它是一个构建器,里面应该都是配置属性参数的方法;
那么还是来看一看fromString做了哪些工作:
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
我们重点看看最后一行,代码中new DrawableTypeRequest创建了一个图片类型的请求:
而传入参数modelClass即为资源模型, streamModelLoader即为data模型加载器(将model变为inputstream), fileDescriptorModelLoader也是模型加载器(将model变为ParcelFileDescriptor);最后将DrawableTypeRequest作为参数传入apply方法中并返回DrawableTypeRequest;
然后看下 fromString().load(string)中的load方法:
在GenericRequestBuilder类(即DrawableTypeRequest父类)中:
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
就是指定一下资源模型,综上所述在Glide.load方法中,创建了对应类型的图片加载请求对象并设置相关属性值。
into
从上面的with和load方法中看出,它们只是为图片请求做一些准备工作,可见关键工作都在into中完成。
public Target<TranscodeType> 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;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
这段代码主要判断是否为主线程,然后设置了图片的显示样式,最后调用创建了ImageViewTarget并调用into方法;
glide.buildImageViewTarget方法最终点调用:
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
也就是说根据传入参数的转传类型返回相应的目标类型,所以asBiymap、asGif函数调用时决定的就是这个;
下面看into方法:
public <Y extends Target<TranscodeType>> 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;
}
into方法作用比较明显,首先通过target获取之前的请求,并加以回收便于下次复用;之后创建新的请求并设置到target中;
这时有两个比较重要的方法:buildRequest和runRequest方法,buildRequest方法最终创建了一个图片加载请求,那么我们看看runRequest方法干了啥:
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
首先它将request加入requests队列中,然后判断状态开始执行请求;
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
我们看到在测量好图片大小之后begin会调用onSizeReady这个方法,继续追踪onSizeReady
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
//获取图片宽高
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
//获取资源加载器
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
//获取数据流加载器
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
//获取资源转码器
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
//加载状态
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
我们发现这个函数里最最重要的函数engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
它就是用来开启加载我们的图片请求的函数,并会返回一个加载状态;那么让我们看看
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
//生成图片加载请求所对应的唯一key;
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//通过key查找内存缓存(LruCache算法)中是否含有此图片(内存缓存)
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;
}
//通过key查找内存缓存(弱引用)中是否含有此图片(内存缓存)
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;
}
//如果加载请求中已经含有次请求则结束
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//如果以上方案都不可行,则从网络中加载图片
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
//创建Runnable,准备加载图片
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
那么继续追踪EngineRunnable中的run方法,看看它又做了哪些有趣的工作呢?
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
首先它调用了一个decode方法得到一个resource,猜测他是加载本地文件缓存和网络缓存:
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();//加载本地文件缓存
} else {
return decodeFromSource();//加载网络数据
}
}
果然,decodeFromCache是加载我们本地缓存文件数据;而decodeFromSource是加载图片网络数据;
最后当然会执行子线程回调onLoadFailed和onLoadComplete,这两个函数的处理操作实在EngineJob当中,EngineJon当中有一个主线程Handler处理子线程加载状态的消息:
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
// synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
private void handleExceptionOnMainThread() {
if (isCancelled) {
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received an exception without any callbacks to notify");
}
hasException = true;
listener.onEngineJobComplete(key, null);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
cb.onException(exception);
}
}
}
这里面主要处理两件事:删除或存储缓存与图片的显示;
至此Glide加载图片的三大步骤执行完毕。
网友评论