本文是阅读郭大神的八篇Android图片加载框架最全解析后,根据自己的理解做的笔记,本篇基于 Glide3.7.0讲解。
一、Glide简介
Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片。
二、Glide的基本用法
Glide.with(this)
.load(url)
.asBitmap() //加载静态图
.placeholder(R.drawable.loading)//加载过程中的占位图
.error(R.drawable.error) //异常占位图
.skipMemoryCache(true) //禁用掉内存缓存
.diskCacheStrategy(DiskCacheStrategy.NONE) //禁用掉Glide的缓存
.override(100, 100) //指定加载图片的尺寸像素
.into(imageView);
//Glide 4的用法: 在Glide的三步走之间加入一个apply()方法,来应用我们刚才创建的RequestOptions对象。
RequestOptions options = new RequestOptions()
? ? ? ? .placeholder(R.drawable.loading)
?? ??? ?.error(R.drawable.error)
? ? ? ? .diskCacheStrategy(DiskCacheStrategy.NONE)
?? ??? ?.override(Target.SIZE_ORIGINAL);
Glide.with(this)
? ? ?.load(url)
? ? ?.apply(options)
? ? ?.into(imageView);
with()
with()方法中传入的实例会决定Glide加载图片的生命周期,传入的参数分2中情况,即传入Application类型的参数,和传入非Application类型的参数。如果传入的是Activity或者Fragment的实例,那么被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。 如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。
load()
load()方法用于指定待加载的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等,因此load()方法也有很多个方法重载。
// 加载本地图片
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);
load()支持加载GIF图片,Glide内部会自动判断图片格式。
into()
into()方法在源码中是整个Glide图片加载流程中逻辑最复杂的部分。into()方法不仅仅是只能接收ImageView类型的参数,还支持很多更丰富的用法。
diskCacheStrategy()
- diskCacheStrategy()方法可以接收多种参数:
- DiskCacheStrategy.NONE: 表示不缓存任何内容。
- DiskCacheStrategy.SOURCE: 表示只缓存原始图片。(Glide 4为:DiskCacheStrategy.DATA)
- DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。(Glide 4为:DiskCacheStrategy.RESOURCE)
- DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
- DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪一种缓存策略((Glide 4特有,默认选项)。
asBitmap()
asBitmap() 只允许加载静态图片,不需要Glide去帮我们自动进行图片格式的判断了。如果是gif动图,就无法正常播放了,而是会在界面上显示第一帧的图片。 在Glide 3中的语法是先load()再asBitmap()的,而在Glide 4中是先asBitmap()再load()的。
asGif()
asGif() 只允许加载动态图片,如果这时传入的是一张静态图,就会加载失败。
override()
override()方法指定图片的尺寸,设置后Glide不会管你的ImageView的大小,只会将图片加载成100*100像素的尺寸。 默认情况下Glide会自动根据ImageView的大小来决定图片的大小,节省了内存开支,所以一般不需要使用override()来指定大小。
三、Glide缓存
Glide缓存分成了两个模块,一个是内存缓存,一个是硬盘缓存。内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。
缓存Key
Glide的缓存Key生成规则非常繁琐,决定缓存Key的参数竟然有10个之多,有id连同着signature、width、height等参数。
内存缓存
Glide默认开启了内存缓存。 Glide内存缓存的实现使用LruCache算法,结合一种弱引用的机制,共同完成了内存缓存功能。正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。
硬盘缓存
硬盘缓存使用的LruCache算法,Google提供有一个现成的工具类DiskLruCache,Glide是使用的自己编写的DiskLruCache工具类,但是基本的实现原理都是差不多的。
四、Glide的回调与监听
into()方法中是可以传入ImageView的,如果想传入其他参数,使加载出来的图片不显示到ImageView上,这就需要用到自定义Target功能。
SimpleTarge的用法
SimpleTarget是一种极为简单的Target,它可以将Glide加载出来的图片对象获取到,而不是像之前那样只能将图片在ImageView上显示出来。
SimpleTarget<Bitmap> simpleTarget = new SimpleTarget<Bitmap>() {
? ? @Override
? ? public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
? ? ? ? imageView.setImageBitmap(resource);
? ? }
};
public void loadImage(View view) {
? ? String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
? ? Glide.with(this)
? ? ? ? ?.load(url)
? ? ? ? ?.asBitmap()
? ? ? ? ?.into(simpleTarget);
}
在加载图片的时候调用了asBitmap()方法强制指定这是一张静态图,这样就能在onResourceReady()方法中获取到这张图的Bitmap对象了。
ViewTarget的用法
Glide在内部自动帮我们创建的GlideDrawableImageViewTarget就是ViewTarget的子类。只不过GlideDrawableImageViewTarget被限定只能作用在ImageView上,而ViewTarget的功能更加广泛,它可以作用在任意的View上。
public class MyLayout extends LinearLayout {
? ? private ViewTarget<MyLayout, GlideDrawable> viewTarget;
? ? public MyLayout(Context context, AttributeSet attrs) {
? ? ? ? super(context, attrs);
? ? ? ? viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
? ? ? ? ? ? ? ? MyLayout myLayout = getView();
? ? ? ? ? ? ? ? myLayout.setImageAsBackground(resource);
? ? ? ? ? ? }
? ? ? ? };
? ? }
? ? public ViewTarget<MyLayout, GlideDrawable> getTarget() {
? ? ? ? return viewTarget;
? ? }
? ? public void setImageAsBackground(GlideDrawable resource) {
? ? ? ? setBackground(resource);
? ? }
}
在MyLayout的构造函数中,我们创建了一个ViewTarget的实例,并将Mylayout当前的实例this传了进去。ViewTarget中需要指定两个泛型,一个是View的类型,一个图片的类型(GlideDrawable或Bitmap)。然后在onResourceReady()方法中,我们就可以通过getView()方法获取到MyLayout的实例,并调用它的任意接口了。比如说这里我们调用了setImageAsBackground()方法来将加载出来的图片作为MyLayout布局的背景图。
public class MainActivity extends AppCompatActivity {
? ? MyLayout myLayout;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? myLayout = (MyLayout) findViewById(R.id.background);
? ? }
? ? public void loadImage(View view) {
? ? ? ? String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
? ? ? ? Glide.with(this)
? ? ? ? ? ? ?.load(url)
? ? ? ? ? ? ?.into(myLayout.getTarget());
? ? }
}
preload()预加载
preload()提前对图片进行一个预加载,等真正需要加载图片的时候就直接从缓存中读取,不想再等待慢长的网络加载时间了 preload()方法有两个方法重载,一个不带参数,表示将会加载图片的原始尺寸,另一个可以通过参数指定加载图片的宽和高。
Glide.with(this)
? ? ?.load(url)
? ? ?.diskCacheStrategy(DiskCacheStrategy.SOURCE)
? ? ?.preload();
?? ??
//调用了预加载之后,再去加载这张图片就会非常快了?? ??
Glide.with(this)
? ? ?.load(url)
? ? ?.diskCacheStrategy(DiskCacheStrategy.SOURCE)
? ? ?.into(imageView);
listener()
listener()方法的作用非常普遍,可以用来监听Glide加载图片的状态。比如使用了preload()方法来对图片进行预加载,通过listener()可以确定预加载释放完成,如果Glide加载图片失败了,可以获得调试错误的原因。
public void loadImage(View view) {
? ? String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
? ? Glide.with(this)
? ? ? ? ? ? .load(url)
? ? ? ? ? ? .listener(new RequestListener<String, GlideDrawable>() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public boolean onException(Exception e, String model, Target<GlideDrawable> target,
? ? ? ? ? ? ? ? ? ? boolean isFirstResource) {
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public boolean onResourceReady(GlideDrawable resource, String model,
? ? ? ? ? ? ? ? ? ? Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? })
? ? ? ? ? ? .into(imageView);
}
onResourceReady()方法和onException()方法都有一个布尔值的返回值,返回false就表示这个事件没有被处理,还会继续向下传递,返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递。例如我们在RequestListener的onResourceReady()方法中返回了true,那么就不会再回调Target的onResourceReady()方法了。 如果我们在onException()方法中返回了true,那么Glide请求中使用error(int resourceId)方法设置的异常占位图就失效了。
五、Glide 图片变换功能
图片变换的意思就是说,Glide从加载了原始图片到最终展示给用户之前,又进行了一些变换处理,从而能够实现一些更加丰富的图片效果,如图片圆角化、圆形化、模糊化等等。 添加图片变换的用法需要调用transform()并将想要执行的图片变换操作作为参数传入transform()方法即可。
Glide自带的图片变换功能
Glide已经内置了两种图片变换操作,我们可以直接拿来使用,一个是CenterCrop,一个是FitCenter,这2个方法其实也只是对transform()方法进行了一层封装而已,它们背后的源码仍然还是借助transform()方法来实现的。 FitCenter是会将图片按照原始的长宽比充满全屏。 CenterCrop是对原图的中心区域进行裁剪后得到的图片,可以配合override()方法来实现更加丰富的效果,比如指定图片裁剪的比例。
Glide.with(this)
? ? ?.load(url)
? ? ?.override(500, 500)
? ? ?.centerCrop()
? ? ?.into(imageView);
Glide.with(this)
? ? ?.load(url)
? ? ?.fitCenter()
? ? ?.into(imageView);
dontTransform()?方法表示让Glide在加载图片的过程中不进行图片变换
自定义图片变换
Glide给我们定制好了一个图片变换的框架,大致的流程是我们可以获取到原始的图片,然后对图片进行变换,再将变换完成后的图片返回给Glide,最终由Glide将图片显示出来。理论上,在对图片进行变换这个步骤中我们可以进行任何的操作,你想对图片怎么样都可以。包括圆角化、圆形化、黑白化、模糊化等等,甚至你将原图片完全替换成另外一张图都是可以的。 自定义图片变换功能的实现逻辑可以参考CenterCrop的源码,自定义一个类让它继承自BitmapTransformation ,然后重写transform()方法,并在这里去实现具体的图片变换逻辑就可以了。 继承BitmapTransformation有一个限制,就是只能对静态图进行图片变换。如果要对GIF动图进行变换,那就得去自己实现Transformation接口才可以了。不过这个就非常复杂了。实现对图片进行圆形化变换
public class CircleCrop extends BitmapTransformation {
? ? public CircleCrop(Context context) {
? ? ? ? super(context);
? ? }
? ? public CircleCrop(BitmapPool bitmapPool) {
? ? ? ? super(bitmapPool);
? ? }
? ? @Override
? ? public String getId() {
?? ??? ?//getId()方法中要求返回一个唯一的字符串来作为id,以和其他的图片变换做区分。一般直接返回当前类的完整类名就可以了。
? ? ? ? return "com.example.glidetest.CircleCrop";
? ? }
? ? @Override
? ? protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
? ? ? ? int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
? ? ? ? final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
? ? ? ? final Bitmap result;
? ? ? ? if (toReuse != null) {
? ? ? ? ? ? result = toReuse;
? ? ? ? } else {
? ? ? ? ? ? result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
? ? ? ? }
? ? ? ? int dx = (toTransform.getWidth() - diameter) / 2;
? ? ? ? int dy = (toTransform.getHeight() - diameter) / 2;
? ? ? ? Canvas canvas = new Canvas(result);
? ? ? ? Paint paint = new Paint();
? ? ? ? BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BitmapShader.TileMode.CLAMP);
? ? ? ? if (dx != 0 || dy != 0) {
? ? ? ? ? ? Matrix matrix = new Matrix();
? ? ? ? ? ? matrix.setTranslate(-dx, -dy);
? ? ? ? ? ? shader.setLocalMatrix(matrix);
? ? ? ? }
? ? ? ? paint.setShader(shader);
? ? ? ? paint.setAntiAlias(true);
? ? ? ? float radius = diameter / 2f;
? ? ? ? canvas.drawCircle(radius, radius, radius, paint);
? ? ? ? if (toReuse != null && !pool.put(toReuse)) {
? ? ? ? ? ? toReuse.recycle();
? ? ? ? }
? ? ? ? return result;
? ? }
}
自定义CircleCrop的使用
Glide.with(this)
? ? ?.load(url)
? ? ?.transform(new CircleCrop(this))
? ? ?.into(imageView);
第三方图片变换功能
glide-transformations这个库实现了很多通用的图片变换效果,如裁剪变换、颜色变换、模糊变换等等。 glide-transformations的项目主页地址是?https://github.com/wasabeef/glide-transformations?
//对图片进行模糊化处理
Glide.with(this)
? ? ?.load(url)
? ? ?.bitmapTransform(new BlurTransformation(this))
? ? ?.into(imageView);
//对图片进行黑白化处理?? ??
Glide.with(this)
? ? ?.load(url)
? ? ?.bitmapTransform(new GrayscaleTransformation(this))
? ? ?.into(imageView);
//对图片同时执行模糊化和黑白化的变换?? ?
Glide.with(this)
? ? ?.load(url)
? ? ?.bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))
? ? ?.into(imageView);?? ??
注意这里我们调用的是bitmapTransform()方法而不是transform()方法,因为glide-transformations库都是专门针对静态图片变换来进行设计的。?? ?
六、Glide的自定义模块功能
自定义模块功能可以更改Glide配置,替换Glide组件等操作独立出来,使得我们能轻松地对Glide的各种配置进行自定义,并且又和Glide的图片加载逻辑没有任何交集,这也是一种低耦合编程方式的体现。
在AndroidManifest.xml文件配置
<manifest>
? ? ...
? ? <application>
? ? ? ? <meta-data
? ? ? ? ? ? android:name="com.example.glidetest.MyGlideModule"
? ? ? ? ? ? android:value="GlideModule" />
? ? ? ? ...
? ? </application>
</manifest>
更改Glide配置 applyOptions()
如果想要更改Glide的默认配置,其实只需要在applyOptions()方法中提前将Glide的配置项进行初始化就可以了。 Glide的配置项如下
- setMemoryCache() 用于配置Glide的内存缓存策略,默认配置是LruResourceCache。
- setBitmapPool() 用于配置Glide的Bitmap缓存池,默认配置是LruBitmapPool。
- setDiskCache() 用于配置Glide的硬盘缓存策略,默认配置是InternalCacheDiskCacheFactory。
- setDiskCacheService() 用于配置Glide读取缓存中图片的异步执行器,默认配置是FifoPriorityThreadPoolExecutor,也就是先入先出原则。
- setResizeService() 用于配置Glide读取非缓存中图片的异步执行器,默认配置也是FifoPriorityThreadPoolExecutor。
- setDecodeFormat() 用于配置Glide加载图片的解码模式,默认配置是RGB_565。
其实Glide的这些默认配置都非常科学且合理,使用的缓存算法也都是效率极高的,因此在绝大多数情况下我们并不需要去修改这些默认配置。如果默认的配置可能将无法满足需求时,就需要自定义修改默认配置了。
public class MyGlideModule implements GlideModule {
? ? public static final int DISK_CACHE_SIZE = 500 * 1024 * 1024;
? ? @Override
? ? public void applyOptions(Context context, GlideBuilder builder) {
?? ??? ?/**将Glide加载的图片都会缓存到SD卡上,(Glide默认的硬盘缓存在当前应用的私有目录下)
?? ??? ?*将Glide硬盘缓存的大小调整成500M(默认硬盘缓存大小都是250M)
?? ??? ?*/
? ? ? ? builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, DISK_CACHE_SIZE));
?? ??? ?//将加载图片的格式改为ARGB_8888(Glide加载图片的默认格式是RGB_565,而Picasso加载图片的默认格式是ARGB_8888)
? ? ? ? builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
? ? }
? ? @Override
? ? public void registerComponents(Context context, Glide glide) {
? ? }
}
替换Glide组件 registerComponents()
Glide中的组件非常繁多,也非常复杂,但其实大多数情况下并不需要我们去替换,替换的比较多的是Glide的HTTP通讯组件。 将Glide的HTTP通讯组件从默认的HttpURLConnection替换成OkHttp,有2种方式
1、手动实现 替换Glide组件功能需要在自定义模块的registerComponents()方法中加入具体的替换逻辑。 新建一个OkHttpFetcher类,并且同样实现DataFetcher<InputStream>接口, 然后仿照着HttpUrlGlideUrlLoader再写一个OkHttpGlideUrlLoader。 将创建的OkHttpGlideUrlLoader和OkHttpFetcher注册到Glide的当中,在registerComponents()中实现。
2、引用库实现
Glide官方给我们提供了非常简便的HTTP组件替换方式。并且除了支持OkHttp3之外,还支持OkHttp2和Volley。 dependencies { ? ? compile 'com.squareup.okhttp3:okhttp:3.9.0' ? ? compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar' }
七、实现带进度的Glide图片加载功能
手动将Glide的HTTP通讯组件替换成OkHttp,再向OkHttp中添加一个自定义的拦截器,就可以在拦截器中捕获到整个HTTP的通讯过程,然后加入一些自己的逻辑来计算下载进度,这样就可以实现下载进度监听的功能了。
参考链接 https://guolin.blog.csdn.net/article/details/53759439 ?
|