有兴趣可以看看郭霖的详细的Glide介绍,本文是基于几篇文章而做出一些总结笔记
介绍
Android上的图片加载框架有很多,比如UIL、Google的Volley、Glide、Picasso、Fresco,每个框架都是功能十分强大,所以我准备挑选Glide进行深入的了解。有兴趣可以看一下
Glide与Picasso的对比
基本用法
1、
添加库支持,目前比较稳定的版本是3.7.0
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
}
<uses-permission android:name="android.permission.INTERNET" />
3、加载图片
网上随便找了一张图作为使用
http://img.taopic.com/uploads/allimg/140326/235113-1403260U22059.jpg
在我们的程序中去使用,布局就是用了Button和ImageView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.wzhworld.imagepro.MainActivity">
<Button
android:id="@+id/downLoad_bt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test"/>
<ImageView
android:id="@+id/show_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView)findViewById(R.id.show_view);
mButton = (Button)findViewById(R.id.downLoad_bt);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Glide.with(MainActivity.this)
.load("http://img.taopic.com/uploads/allimg/140326/235113-1403260U22059.jpg")
.asBitmap()
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher_round)
.diskCacheStrategy(DiskCacheStrategy.NONE)
//.override(111,111)
.into(mImageView);
}
});
}
效果图
4、用法介绍
Glide.with(this).load(url).into(imageView);
简单的一句话就完成了Glide的使用,是不是觉得很简单。load除了url之外,还有以下功能
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);
// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
上面实现程序当中可不止with、load、into几个方法,还有其他方法
Glide.with(MainActivity.this)
.load("http://img.taopic.com/uploads/allimg/140326/235113-1403260U22059.jpg")
.asBitmap() //指定图片格式,asBitmap表示作为静态图的方法,还有另一个方法是asGif(),表示加载的图是动态的
.placeholder(R.mipmap.ic_launcher) //作为占位图,即在图片还没加载出来之前显示的一张你指定的图片
.error(R.mipmap.ic_launcher_round) //当没有网络或者静、动态图加载为动、静态图出现错误时出现的图片
.diskCacheStrategy(DiskCacheStrategy.NONE) //表示不将加载的图片进行缓存,每一次都需要进行加载
.override(111,111) //指定展示时图片的大小,因为可能造成内存的浪费,比如你加载的图片是1000*1000,
//但是你的ImageView布局只有200*200,根本就不需要这么高的像素,所以你可以指定为200*200,这样可以减少浪费
.into(mImageView);
源码
1、with()
with()的对象可以有很多,
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);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
可以看到
可以是activity、fragment等等,其通过一个单例的静态方法获取RequestManager对象,获取对象的目的其实是对生命周期的分析,有两种情况:
如果是Application的对象,此时的生命周期代表的就是应用程序的生命周期,所以Glide可以对生命周期进行了解,以此来判断当应用生命结束时,若此时正在加载图片,那么就终止
。
第二种情况是非Application对象,即Activity、fragment等,其会向当前的活动添加一个隐藏的Fragment,隐藏的Fragment与Activity的生命周期同步,Glide通过这个Fragment知道了活动的生命周期,就可以捕获生命周期
。
2、load()
load的方法郭霖大神说得算清楚吧,但是自己看了一遍之后还是不是很理解,先开始描述一遍,可能回来会有体会。
1、我们在with这个方法之中实现了获取RequestManager对象,那么很容易理解我们会利用这个对象的方法进行load的操作
RequestManager类代码简化:
public class RequestManager implements LifecycleListener {
...
/**
* Returns a request builder to load the given {@link String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
/**
* Returns a request builder that loads data from {@link String}s using an empty signature.
*
* <p>
* Note - this method caches data using only the given String as the cache key. If the data is a Uri outside of
* your control, or you otherwise expect the data represented by the given String to change without the String
* identifier changing, Consider using
* {@link GenericRequestBuilder#signature(Key)} to mixin a signature
* you create that identifies the data currently at the given String that will invalidate the cache if that data
* changes. Alternatively, using {@link DiskCacheStrategy#NONE} and/or
* {@link DrawableRequestBuilder#skipMemoryCache(boolean)} may be appropriate.
* </p>
*
* @see #from(Class)
* @see #load(String)
*/
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));
}
...
}
我们来分析一下,首先load方法返回的是DrawableTypeRequest类型,方法中有 fromString().load(string)
在来看一下fromString方法,同样返回的是
DrawableTypeRequest类型,方法中返回的是loadGeneric(String.class)
接下来自然而然我们会看loadGeneric方法有什么用,根据郭霖大神的说法,方法中获取了
Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象,ModelLoader对象主要是用来加载图片的,根据不同传入的参数,获得不同的ModelLoader对象。这里应该算是读源码的点到即止,里面的ModelLoader里的内容很复杂,第一次读源码还是需要跟着别人的脚步,才不会越陷越深。
好了,我们现在按照递归的方法往回看,loadGeneric(String.class)代入的是String.class,所以获得的是StreamStringLoader对象,loadGeneric方法要返回的是DrawableTypeRequest对象,所以在loadGeneric方法最后new 了一个DrawableTypeRequest对象出来,并把获取的那些ModelLoader一大堆都放进去。
2、上面提到很多DrawableTypeRequest,那么它的作用是什么,我们来看下他的源码
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
/**
* Attempts to always load the resource as a {@link android.graphics.Bitmap}, even if it could actually be animated.
*
* @return A new request builder for loading a {@link android.graphics.Bitmap}
*/
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
/**
* Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
* <p>
* If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
* represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
* just an {@link DrawableTypeRequest} is sufficient because it will determine whether or
* not the given data represents an animated GIF and return the appropriate animated or not animated
* {@link android.graphics.drawable.Drawable} automatically.
* </p>
*
* @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}
...
}
我们可以看到两个方法,asBitMap 、asGif,我们前面有用过,就是作为动态、静态图,返回的类型是GifTypeRequest、BitmapTypeRequest,默认状态之下使用的是DrawableTypeRequest这个类型。
好了,这个时候我们要回到RequestManager这个类中,我们也看到,
(DrawableTypeRequest<String>) fromString().load(string);
DrawableTypeRequeset这个类中并没有load方法,我们可以想到load可能是继承父类的,所以到父类中寻找,父类是
DrawableRequestBuilder
3、DrawableRequestBuilder类
public class DrawableRequestBuilder<ModelType>
extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
implements BitmapOptions, DrawableOptions {
DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}
public DrawableRequestBuilder<ModelType> thumbnail(
DrawableRequestBuilder<?> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> thumbnail(
GenericRequestBuilder<?, ?, ?, GlideDrawable> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
super.decoder(decoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
super.encoder(encoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> priority(Priority priority) {
super.priority(priority);
return this;
}
public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
public DrawableRequestBuilder<ModelType> fitCenter() {
return transform(glide.getDrawableFitCenter());
}
public DrawableRequestBuilder<ModelType> bitmapTransform(Transformation<Bitmap>... bitmapTransformations) {
GifBitmapWrapperTransformation[] transformations =
new GifBitmapWrapperTransformation[bitmapTransformations.length];
for (int i = 0; i < bitmapTransformations.length; i++) {
transformations[i] = new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);
}
return transform(transformations);
}
@Override
public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
super.transform(transformation);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> transcoder(
ResourceTranscoder<GifBitmapWrapper, GlideDrawable> transcoder) {
super.transcoder(transcoder);
return this;
}
public final DrawableRequestBuilder<ModelType> crossFade() {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
return this;
}
public DrawableRequestBuilder<ModelType> crossFade(int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
return this;
}
public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
duration));
return this;
}
@Override
public DrawableRequestBuilder<ModelType> dontAnimate() {
super.dontAnimate();
return this;
}
@Override
public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) {
super.placeholder(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> fallback(Drawable drawable) {
super.fallback(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> fallback(int resourceId) {
super.fallback(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> error(Drawable drawable) {
super.error(drawable);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> listener(
RequestListener<? super ModelType, GlideDrawable> requestListener) {
super.listener(requestListener);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> override(int width, int height) {
super.override(width, height);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> sourceEncoder(Encoder<ImageVideoWrapper> sourceEncoder) {
super.sourceEncoder(sourceEncoder);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> dontTransform() {
super.dontTransform();
return this;
}
@Override
public DrawableRequestBuilder<ModelType> signature(Key signature) {
super.signature(signature);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> clone() {
return (DrawableRequestBuilder<ModelType>) super.clone();
}
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
@Override
void applyFitCenter() {
fitCenter();
}
@Override
void applyCenterCrop() {
centerCrop();
}
}
我么可以看到里面的方法很多都是我们刚才用过的和一些没用过的API,到这里load()方法就结束了
3、into()
最最最复杂的部分来了,做好心理准备了
看回上面的DrawableRequestBuilde类,倒数第三个方法是into()方法,只有一句super,那么说明他是从DrawableRequestBuilde的父类继承而来的,那就要看到他的父类GenericRequestBuilder。
1、
我们来看下父类中的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));
}
前面很多种情况不讲,有兴趣参考郭霖博客,我们刘怡最后一行代码,创建了一个Target对象,这个对象是用来展示最终图片的、
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
调用到buildTarget方法,
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
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)");
}
}
}
这里有两种情况,一种值调用了asBitmap()方法,那么使用的是BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象
2、了解完获取到的方法之后,我们回到开始的
into(glide.buildImageViewTarget(view, transcodeClass));
进去这个方法:
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;
}
可以看到红色代码,主要是执行创建request对象以及进行请求,Request是用来请求加载图片的,进入buildRequest方法之后可以看见通过obtain获取
对象,然后继续进入obtain方法里面创建了GenericRequest并进行初始化,返回,到这里就得到request对象。
3、了解runRequest
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
这里是用来判断Glide是不是暂停状态,如果不是就begin()方法执行Request,否则进入队列等待。
4、begin() GenericRequest调用
图片URL地址为空,直接使用占位图
不为空,如果使用了override()为图片指定了一个固定的宽高,一种是没有指定。指定了的话,调用onSizeReady()方法。没指定的话,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高,最终都会调用到onSizeReady()方法,在onSizeReady()方法中,
5、onSizeReady() GenericRequest调用
EngineRunnable的run()-->HttpUrlFetcher中的loadData方法具体的请求得到一个InputStream-->之后再一层层封装得到Resource<GlideDrawable>—> GlideDrawableImageViewTarget的setResource设置上图片
图片URL地址为空,直接使用占位图
不为空,如果使用了override()为图片指定了一个固定的宽高,一种是没有指定。指定了的话,调用onSizeReady()方法。没指定的话,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高,最终都会调用到onSizeReady()方法,在onSizeReady()方法中,
5、onSizeReady() GenericRequest调用
EngineRunnable的run()-->HttpUrlFetcher中的loadData方法具体的请求得到一个InputStream-->之后再一层层封装得到Resource<GlideDrawable>—> GlideDrawableImageViewTarget的setResource设置上图片
见谅,因为第一次看源码有点消化不了,所以到了第三点还能看懂,到了第四点之后里面的网络处理,图片剪裁的没读下去,所以先mark,做以后继续读的记录,Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程,如果你想详细了解,可以看郭霖大神的详细解析。