glide生命周期管理、内存管理、缓存机制、加载器、观察者模式
前言:本系列博文主要通过借鉴其他优秀博文以及自己的实际开发经验综合得出,主要分析glide相关使用、机制以及设计模式等内容,相信这篇博文会让你对glide有更加深入的理解。本篇是系列文章的第一章。
篇一 glide的基本使用及进阶使用
前言:本篇博客将介绍glide的基本使用和进阶使用 篇二
目录
一、简介
Glide,一个被google所推荐的图片加载库,作者是bumptech。这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方app。还是有很多开发者使用Square公司的picasso,Glide 对于 Android SDK 的最低要求是 API level 10。
二、使用
1-添加依赖:
dependencies {
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.android.support:support-v4:23.2.1'
}
2-简单例子:
String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
.load(url)
.into(imageView);
- with(Context context) 需要上下文,这里还可以使用 Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 的对象。将 Activity/Fragment 对象作为参数的好处是图片的加载会和 Activity/Fragment 的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是 Context。
- load(String url) 这里我们所使用的一个字符串形式的网络图片的 URL,后面会讲解 load() 的更多使用方式
- into(ImageView imageView) 你需要显示图片的目标 ImageView
3-占位图片设置:用于设置图片没有加载出来或者加载错误时的图片
String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
.load(url)
.placeholder(R.drawable.place_image)//图片加载出来前,显示的图片
.error(R.drawable.error_image)//图片加载失败后,显示的图片
.into(imageView);
4-缩略图设置
Glide.with( context )
.load( url )
.thumbnail( 0.2f )
.into( imageView );
- Glide 的缩略图功能在这里不得不说,和占位图略有不同,占位图必须使用资源文件才行,而缩略图是动态的占位图可以从网络中加载。缩略图会在实际请求加载完成或者处理完之后才显示。在原始图片到达之后,缩略图不会取代原始图片,只会被抹除。
- Glide 为缩略图提供了2种不同的加载方式,比较简单的方式是调用 thumbnail() 方法,参数是 float 类型,作为其倍数大小。例如,你传入 0.2f 作为参数,Glide 将会显示原始图片的20%的大小,如果原图是 1000x1000 的尺寸,那么缩略图将会是 200x200 的尺寸。为缩略图明显比原图小得多,所以我们需要确保 ImageView 的ScaleType设置的正确。
- 使用 thumbnail() 方法来设置是简单粗暴的,但是如果缩略图需要通过网络加载相同的全尺寸图片,就不会很快的显示了。所以 Glide 提供了另一种防止去加载缩略图,先看代码:
private void loadImageThumbnailRequest(){
// setup Glide request without the into() method
DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );
// pass the request as a a parameter to the thumbnail request
Glide.with( context )
.load( url )
.thumbnail( thumbnailRequest )
.into( imageView );
}
5-动画开关
动画效果可以让图片加载变得更加的平滑,crossFade() 方法强制开启 Glide 默认的图片淡出淡入动画,当前版本3.7.0是默认开启的。crossFade() 还有一个重载方法 crossFade(int duration)。可以控制动画的持续时间既然可以添加动画,也可以设置没有任何淡出淡入效果,调用 dontAnimate()。glide添加自定义动画将在后面介绍。
Glide.with(context)
.load(url)
.crossFade()//或者使用 dontAnimate() 关闭动画
.placeholder(R.drawable.place_image)
.error(R.drawable.error_image)
.into(imageView);
6-图片大小裁剪
Glide.with(context)
.load(url)
.override(width,height)//这里的单位是px
.into(imageView);
在项目开发过程中,指定图片显示大小长长可能用到,毕竟从服务器获取的图片不一定都是符合设计图的标准的。我们在这里就可以使用 override(width,height) 方法,在图片显示到 ImageView 之前,重新改变图片大小。在设置图片到 ImageView 的时候,为了避免图片被挤压失真,ImageView 本身提供了 ScaleType 属性。
7-内存缓存
内存缓存是 Glide 默认实现的,可以调用 skipMemoryCache(true) 告诉 Glide 跳过内存缓存,这样 Glide 就不会把这张图片放到内存缓存中,该方法只影响内存缓存。也可以调用 .diskCacheStrategy( DiskCacheStrategy.NONE )来关闭磁盘管理。
- 使用 DiskCacheStrategy 可以为 Glide 配置磁盘缓存行为。Picasso 只缓存了全尺寸的图片,而 Glide 的不同之处在于,Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。
DiskCacheStrategy 的枚举意义:
- DiskCacheStrategy.NONE 什么都不缓存
- DiskCacheStrategy.SOURCE 只缓存全尺寸图
- DiskCacheStrategy.RESULT 只缓存最终的加载图
- DiskCacheStrategy.ALL 缓存所有版本图(默认行为)
8-图片请求的优先级
//设置 HIGH 优先级
Glide.with( context )
.load( highPriorityImageUrl )
.priority (Priority.HIGH )
.into( imageView );
//设置 LOW 优先级
Glide.with( context )
.load( lowPriorityImageUrl )
.priority( Priority.LOW )
.into( imageView );
- Priority.LOW
- Priority.NORMAL
- Priority.HIGH
- Priority.IMMEDIAT
注意:优先级并不是完全严格遵守的。Glide 将会用他们作为一个准则,尽可能的处理这些请求,但是不能保证所有的图片都会按照所有要求的顺序加载。
9-显示Gif和Video
String gifUrl = "http://i2.mhimg.com/M00/0E/AE/CgAAilTPWJ2Aa_EIACcMxiZi5xE299.gif";
Glide.with( context )
.load( gifUrl )
.asGif()//会保证只显示Gif图片,如果不是则会调用errer()
.placeholder( R.drawable.default )
.error( R.drawable.error )
.into( imageView );
如果你想显示 Gif 但只是向现实静态的图片你就可以这么做:
Glide.with( context )
.load( gifUrl )
.asBitmap()//只显示Gif第一帧图片
.error( R.drawable.error )
.into( imageView );
但是glide只能显示本地视频:
String filePath = "/storrage/emulated/0/Pictures/video.mp4";
Glide.with( context )
.load( Uri.fromFile( new File( filePath ) ) )//本地视频资源
.into( imageView );
三、Target篇
在我们使用glide加载图片的时候,Glide 隐藏做了所有的网络请求和后台的线程处理,图片准备好之后切回到 UI 线程刷新 ImageView。也就是说 ImageView 在我们代码的链式结构中成为了最后一步,但是如果我们需要获取到 Bitmap 本身的话就需要用到 Target 了,Target 其实就是整个图片的加载的生命周期,所以我们就可以通过它在图片加载完成之后获取到 Bitmap。
1-SimpleTarget:
private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
mImageView.setImageBitmap(resource);
}
};
private void loadImageSimpleTarget() {
Glide.with( this)
.load( mUrl )
.asBitmap()
.into( mSimpleTarget );//当成ImageView来使用
}
首先创建了一个 SimpleTarget 的对象并且实现了 onResourceReady() 方法,参数就有我们需要的 Bitmap 。而使用 SimpleTarget 的对象的时候就像使用 ImageView 一样,作为参数传给 into() 方法,Glide 会内部去处理并返回结果给任何一个对象。
如果需要改变图片的大小怎么办?这点小问题 Glide 还是有考虑到的,加入原尺寸 1000x1000 的图片,我们显示的时候只需要是 500x500 的尺寸来节省时间和内存,你可以在 SimpleTarget 的回调声明中指定图片的大小。
private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(500,500) {//设置需要的图片大小
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
mImageView.setImageBitmap(resource);
}
};
with() 方法传入 Activity 或者 Fragment 时 Glide 的图片加载会与他们的生命周期关联起来,但是如果我们使用 Target 的话,这个 Target 就有可能独立于他们的生命周期以外,这时候我们就需要使用 context.getApplicationContext() 的上下文了,这样只有在应用完全停止时 Glide 才会杀死这个图片请求:
Glide.with(mContext.getApplicationContext())
.load(mUrl)
.asBitmap()
.into(target);
2-ViewTarget:
当我们使用自定义View时,Glide 并不支持加载图片到自定义 view 中的,使用 ViewTarget 更容易实现。
public class CustomView extends FrameLayout {
private ImageView mImageView;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = new ImageView(getContext());
addView(mImageView , LayoutParams.MATCH_PARENT , LayoutParams.MATCH_PARENT);
}
public void setImage(Drawable drawable){
mImageView.setImageDrawable(drawable);
}
}
使用ViewTarger将图片加载到自定义View中:
public void loadImageTarget(Context context){
CustomView mCustomView = (CustomView) findViewById(R.id.custom_view);
ViewTarget viewTarget = new ViewTarget<CustomView,GlideDrawable>( mCustomView ) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
this.view.setImage(resource);
}
};
Glide.with(context)
.load(mUrl)
.into(viewTarget);
}
3-其他Target......未完待续
四、Transformations篇
图片显示之前我们可能还需要对图片进行处理操作,比如:图片切圆角,灰阶处理等等;这些需求我们通过 Transformations 操作 bitmap 来实现,我们可以修改图片的任意属性:尺寸,范围,颜色,像素位置等等。
自定义Transformation:
我们需要创建一个类去实现 Transformation 接口,但是要实现这个方法还是比较复杂的,接口中 transform 方法提供的参数 Resource<T> resource 不是那么好处理的。如果你只是想要对图片(不是 Gif 和 video)做常规的 bitmap 转换,我们推荐你使用抽象类 BitmapTransformation,它简化了很多的实现。
下面的代码实现了对图片切圆角的操作:
public class RoundTransformation extends BitmapTransformation {
private float radius = 0f;
public RoundTransformation(Context context) {
this(context, 4);
}
public RoundTransformation(Context context, int px) {
super(context);
this.radius = px;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null)
return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);//从位图池中得到位图result
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);//创建画布,同时传入得到的result位图
Paint paint = new Paint();//创建画笔
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());//创建相同大小矩形
canvas.drawRoundRect(rectF, radius, radius, paint);//在画布上切圆角
return result;
}
@Override
public String getId() {
return getClass().getName() + Math.round(radius);
}
}
使用自定义的transformation:
调用 .transform() 方法,将自定义的 Transformation 的对象作为参数传递进去就可以使用你的 Transformation 了,这里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的转换。
Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20))
//.bitmapTransform( new RoundTransformation(context , 20))//只能为Bitmap做转化
.into(mImageView);
//如果需要使用多个tansformation,只需要在方法中传入多个对象即可
Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20) , new RotateTransformation(context , 90f))
.into(mImageView);
这里提供大牛的优秀轮子方便使用:https://github.com/wasabeef/glide-transformations
五、自定义动画篇
这是个 XML 动画缩放动画,图片刚开始小的,然后逐渐增大到原尺寸。我们现在要应用到 Glide 加载图片中去,调用 .animate() 方法传入 XML 动画的 id 即可。
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1"/>
</set>
Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(this , 20))
.animate( R.anim.zoom_in )//设置动画
.into(mImageView);
这种加载方式用在常规的 ImageView 上是没有问题的,但如果使用的 Target 是一些自定义的时候我们就可以通过传入实现了 ViewPropertyAnimation.Animator 接口的类对象来实现。
ViewPropertyAnimation.Animator animator = new ViewPropertyAnimation.Animator() {
@Override
public void animate(View view) {
view.setAlpha( 0f );
ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
fadeAnim.setDuration( 2500 );
fadeAnim.start();
}
};
Glide.with(context)
.load(mUrl)
.animate( animator )
.into(viewTarget);
六、Module篇
Glide 的 Module 是一个可以全局改变 Glide 的行为的东西,为了定制 Glide 的行为我们要去实现 interface GlideModule 来写我们自己的代码。
public class ExampleModule implements GlideModule{
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// todo
}
@Override
public void registerComponents(Context context, Glide glide) {
// todo
}
}
GlideModule 为我们提供了两个方法,这里我们主要使用的是 applyOptions(Context context, GlideBuilder builder) , 我们自己的需要重新定义的代码写在该方法里就可以了。然后我们还需要去 AndroidManifest.xml 中使用 meta 声明我们上面实现的 Module:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mrtrying.demoglide">
<application>
<meta-data
android:name="com.mrtrying.demoglide.module.ExampleModule"
android:value="GlideModule" />
</application>
</manifest>
applyOptions(Context context, GlideBuilder builder) 中有两个参数, 我们通过使用 GlideBuilder 来实现我们的需求,其提供了如下方法:
- .setMemoryCache(MemoryCache memoryCache)
- .setBitmapPool(BitmapPool bitmapPool)
- .setDiskCache(DiskCache.Factory diskCacheFactory)
- .setDiskCacheService(ExecutorService service)
- .setResizeService(ExecutorService service)
- .setDecodeFormat(DecodeFormat decodeFormat)
例如增加图片质量:
public class QualityModule implements GlideModule{
@Override
public void applyOptions(Context context , GlideBuilder builder){
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override
public void registerComponents(Context context , Glide glide){
// nothing to do here
}
}
往往我们还会遇到一些情况,希望 Glide 可以使用我们自己的网络框架,我们就需要做一些事情来实现这个需求了。Glide 的开发者不强制设置网络库给你,所以Glide可以说和 HTTPS 无关。理论上,它可以与任何的网络库实现,只要覆盖了基本的网络能力就行。同样是需要实现 Glide 的 ModuleLoader 的接口,为了让我们更加易用,Glide 为 OkHttp 和 Volley 两个网络库提供了实现。
假设我要集成 OkHttp 作为 Glide 的网络库,我可以手动实现一个 GlideModule 也可以在 build.gradle 中添加依赖:
dependencies{
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Glide's OkHttp Integration
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
compile 'com.squareup.okhttp:okhttp:3.2.0'
}
Gradle 会自动合并必要的 GlideModule 到你的 AndroidManifest.xml , Glide 会认可在 manifest 中存在,然后使用 OkHttp 做到的所有网络连接。注意结尾的@aar可以将库中的AndroidManifest.xml文件一起导出,所以不用再将一下文本添加到项目的AndroidManifest.xml文件中:
<meta-data
android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
android:value="GlideModule" />
集称OKhttp代码:(注册过后就可以使用了)
Glide.get(getApplicationContext()).register(GlideUrl.class, InputStream.class,
new OkHttpUrlLoader.Factory(getGlideOkHttpClient()));
总结
本篇博客主要借鉴https://www.jianshu.com/p/7ce7b02988a4,加入自己的理解,整理了其中的重点性质,简介、使用、Target篇、Transformations篇、自定义动画篇、Module篇 希望对读者有所帮助。