Android图片加载库Glide简介(上)

官网:Glide
Glide是Google员工的开源项目,Google官方App中已经使用,在2015年的Google I/O上被推荐。
Glide的优点:
- 使用简单
- 可配置度高,自适应程度高
- 支持多种数据源,网络、本地、资源、
Assets等 - 支持
Gif图片。 - 支持
WebP。 - 加载速度快、流畅度高。
Glide的with()方法不光接受Context,还接受Activity和Fragment,这样图片加载会和Activity/Fragment的生命周期保持一致,比如Pause状态在暂停加载,在Resume的时候又自动重新加载。- 支持设置图片加载优先级。
- 支持缩略图,可以在同一时间加载多张图片到同一个
ImageView中,例如可以首先加载只有ImageView十分之一大小的缩略图,然后等再加载完整大小的图片后会再显示到该ImageView上。 - 内存占用低,
Glide默认的Bitmap格式是RGB_565,比ARGB_8888格式的内存开销要小一半,所以图片质量会稍微差一些,当然这些配置都是可以修改的。 Glide缓存的图片大小是根据ImageView尺寸来缓存的的。这种方式优点是加载显示非常快。且可以设置缓存图片的尺寸- 默认使用
HttpUrlConnection下载图片,可以配置为OkHttp或者Volley下载,也可以自定义下载方式。 - 默认使用两个线程池来分别执行读取缓存和下载任务,且可以自定义。
- 默认使用手机内置存储进行磁盘缓存,可以配置为外部存储,可以配置缓存大小,图片池大小。
- 在加载同样配置的图片时,
Glide内存占用更少,因为Glide是针对每个ImageView适配图片大小后再存储到磁盘的,这样加载进内存的是压缩过的图片,内存占用自然就比较少。这种做法有助于减少OutOfMemoryError的出现。 - 高效处理
Bitmap,使用Bitmap Pool来对Bitmap进行复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力
缺点:
- 体积相对来说比较大,目前最新版的大小在
500k左右 - 当我们从远程
URL地址下载图片时,Picasso相比Glide要快很多。可能的原因是Picasso下载完图片后直接将整个图片加载进内存,而Glide还需要针对每个ImageView的大小来适配压缩下载到的图片,这个过程需要耗费一定的时间。(当然我们可以使用thumbnail()来减少压缩的时间)
使用方法:
repositories {
mavenCentral()
maven { url 'https://maven.google.com' }
}
dependencies {
compile 'com.github.bumptech.glide:glide:4.1.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.1.1'
}
Java代码使用:
和Picasso一样,Glide使用fluent interface的模式,想要加载图片,需要至少传入三个参数:
with(Context context):Context是很多android api所必要的参数,glide也一样。可以传递Activity/Fragment,而且
它会由Activity/Fragment的生命周期进行绑定。load(String imageUrl):图片的URL地址。into(ImageView targetImageView):需要将加载的图片显示到的对应的ImageView。
// For a simple view:
@Override public void onCreate(Bundle savedInstanceState) {
...
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
GlideApp.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}
// For a simple image list:
@Override public View getView(int position, View recycled, ViewGroup container) {
final ImageView myImageView;
if (recycled == null) {
myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
} else {
myImageView = (ImageView) recycled;
}
String url = myUrls.get(position);
GlideApp
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.into(myImageView);
return myImageView;
}
Proguard防混淆配置:
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
接下来是一些常用的方法:
- 从网络加载图片
GlideApp.with(context).load(internetUrl).into(targetImageView);
- 从文件加载图片
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"Test.jpg");
GlideApp.with(context).load(file).into(imageViewFile);
- 加载
resource资源
int resourceId = R.mipmap.ic_launcher;
GlideApp.with(context).load(resourceId).into(imageViewResource);
- 加载
URI地址
GlideApp.with(context).load(uri).into(imageViewUri);
- 设置占位图
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.into(imageViewPlaceholder);
- 设置出错时的图片
GlideApp
.with(context)
.load("http://futurestud.io/non_existing_image.png")
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
.into(imageViewError);
fallback占位图
上面的error()展位图是当资源无法获取时使用的,例如图片地址无效,但是还有另外一种情况是你传递了null值。比如在一个显示用户资料列表中,由于并不是
每个人都有头像图片,所有这时可能会传递null值。如果想要指定一个在传递null值时显示的错误图片可以使用.fallback().
String nullString = null; // could be set to null dynamically
GlideApp
.with(context)
.load( nullString )
.fallback( R.drawable.floorplan )
.into( imageViewNoFade );
- 结合
ListView使用
public static String[] eatFoodyImages = {
"http://i.imgur.com/rFLNqWI.jpg",
"http://i.imgur.com/C9pBVt7.jpg",
"http://i.imgur.com/rT5vXE1.jpg",
"http://i.imgur.com/aIy5R2k.jpg",
"http://i.imgur.com/MoJs9pT.jpg",
"http://i.imgur.com/S963yEM.jpg",
"http://i.imgur.com/rLR2cyc.jpg",
"http://i.imgur.com/SEPdUIx.jpg",
"http://i.imgur.com/aC9OjaM.jpg",
"http://i.imgur.com/76Jfv9b.jpg",
"http://i.imgur.com/fUX7EIB.jpg",
"http://i.imgur.com/syELajx.jpg",
"http://i.imgur.com/COzBnru.jpg",
"http://i.imgur.com/Z3QjilA.jpg",
};
public class UsageExampleAdapter extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_usage_example_adapter);
listView.setAdapter(
new ImageListAdapter(
UsageExampleAdapter.this,
eatFoodyImages
)
);
}
}
public class ImageListAdapter extends ArrayAdapter {
private Context context;
private LayoutInflater inflater;
private String[] imageUrls;
public ImageListAdapter(Context context, String[] imageUrls) {
super(context, R.layout.listview_item_image, imageUrls);
this.context = context;
this.imageUrls = imageUrls;
inflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(R.layout.listview_item_image, parent, false);
}
GlideApp
.with(context)
.load(imageUrls[position])
.into((ImageView) convertView);
return convertView;
}
}
- 图片过度
Transitions
无论你是否使用占位图,在UI过程中改变ImageView的图片都是一个很大的动作。有一个简单的方法可以使这种改变变的更平滑,更容易让人接受,那就是使用
crossfade动画。
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
.transition(DrawableTransitionOptions.withCrossFade())// withCrossFade(int duration)方法可以传入时间,默认时间是300毫秒
.into(imageViewCombined);
- 自定义过度动画
上面提供了crossfade动画,但是有些时候我们需要自定义更多的样式。
Glide也是支持xml中自定义的动画文件的。
GlideApp
.with(context)
.load(eatFoodyImages[0])
.transition(GenericTransitionOptions.with(R.anim.zoom_in))
.into(imageView1);
- 图片大小调整
理想情况下,你的服务器或者API能够返回所需分辨率的图片,这是在网络带宽、内存消耗和图片质量下的完美方案。
跟Picasso比起来,Glide在内存上占用更优化。Glide在缓存和内存里自动限制图片的大小去适配ImageView的尺寸。Picasso也有同样的能力,但需要调用fit()方法。用Glide时,如果图片不需要自动适配ImageView,调用override(horizontalSize, verticalSize),它会在将图片显示在ImageView之前调整图片的大小。
这个设置也有利于没有明确目标,但已知尺寸的视图上。例如,如果app想要预先缓存在splash屏幕上,还没法测量出ImageVIews具体宽高。但是如果你已经知道图片应当为多大,使用override可以提供一个指定的大小的图片。
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200) // resizes the image to these dimensions (in pixel). resize does not respect aspect ratio
.into(imageViewResize);
- 缩放图片
对于任何图像的任何处理,调整图像的大小可能会扭曲长宽比,丑化图片的显示。在大多数情况下,你希望防止这种事情发生。Glide提供了变换去处理图片显示,通过设置centerCrop和fitCenter,可以得到两个不同的效果。CenterCrop()会缩放图片让图片充满整个ImageView的边框,然后裁掉超出的部分。ImageVIew会被完全填充满,但是图片可能不能完全显示出。
fitCenter()会缩放图片让两边都相等或小于ImageView的所需求的边框。图片会被完整显示,可能不能完全填充整个ImageView。
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200) // resizes the image to these dimensions (in pixel)
.centerCrop() // this cropping technique scales the image so that it fills the requested bounds and then crops the extra.
.into(imageViewResizeCenterCrop);
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200)
.fitCenter()
.into(imageViewResizeFitCenter);
- 更变图片转换
上面介绍了两种自带的图片转换方式fitCenter和centerCrop。 但有些时候我们需要自定义一些别的转换方式,自定义转换方式需要继承BitmapTransformation类。
public class BlurTransformation extends BitmapTransformation {
public BlurTransformation(Context context) {
super( context );
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return null; // todo
}
@Override
public String getId() {
return null; // todo
}
}
接下来使用Renderscript来实现图片的模糊处理。
public class BlurTransformation extends BitmapTransformation {
private RenderScript rs;
public BlurTransformation(Context context) {
super( context );
rs = RenderScript.create( context );
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );
// Allocate memory for Renderscript to work with
Allocation input = Allocation.createFromBitmap(
rs,
blurredBitmap,
Allocation.MipmapControl.MIPMAP_FULL,
Allocation.USAGE_SHARED
);
Allocation output = Allocation.createTyped(rs, input.getType());
// Load up an instance of the specific script that we want to use.
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setInput(input);
// Set the blur radius
script.setRadius(10);
// Start the ScriptIntrinisicBlur
script.forEach(output);
// Copy the output to the blurred bitmap
output.copyTo(blurredBitmap);
toTransform.recycle();
return blurredBitmap;
}
@Override
public String getId() {
// getId()方法为这个变换描述了一个独有的识别。Glide使用那个关键字作为缓存系统的一部分。防止出现异常问题,确保其唯一。
return "blur";
}
}
传递你的类的实例作为.transform()的参数。不管是图片还是gif都可以进行变换。
GlideApp
.with(context)
.load(eatFoodyImages[0])
.transform(new BlurTransformation(context))
.into(imageView2);
通常,Glide的fluent interface允许方法被连接在一起,然而变换并不是这样的。确保你只调用.transform()一次,不然之前的设置将会被覆盖!然而,你可以通过传递多个转换对象当作参数到.transform()中来进行多重变换:
GlideApp
.with(context)
.load(eatFoodyImages[0])
.transform(
new MultiTransformation(
new GrayscaleTransformation(context),
new BlurTransformation(context)))
.into(imageView3);
- 播放
gif动画
String gifUrl = "http://i.kinja-img.com/gawker-media/image/upload/s--B7tUiM5l--/gf2r69yorbdesguga10i.gif";
GlideApp
.with(context)
.load(gifUrl)
.into(imageViewGif);
gif检查
Glide接受Gif和图片作为load()的参数。上面代码中潜在的一个问题,如果提供的源不是Gif,可能是一个普通的图片。即使是一个完好的图片(非Gif),Glide也会加载失败。.error()回调方法会被调用,并加载错误占位图。这样引入了一个额外的方法.asGif()强迫生成一个Gif。
GlideApp
.with(context)
.asGif()
.load(gifUrl)
.error(R.drawable.full_cake)
.into(imageViewGifAsGif);
- 把
Gif当作Bitmap播放
如果你的app需要显示一组网络URL,可能包括普通的图片或者Gif。在一些情况下,你可能并不在意是否要播放完整的Gif。如果你只是想要显示Gif的第一帧,当URl指向的的确是Gif,你可以调用asBitmap()将其作为常规图片显示。
GlideApp
.with(context)
.asBitmap()
.load(gifUrl)
.into(imageViewGifAsBitmap);
- 显示本地视频缩略图
String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
GlideApp
.with(context)
.asBitmap()
.load(Uri.fromFile(new File(filePath)))
.into(imageViewGifAsBitmap);
- 缓存设置
GlideApp
.with(context)
.load(gifUrl)
.asGif()
.error(R.drawable.full_cake)
.diskCacheStrategy(DiskCacheStrategy.DATA)
.into(imageViewGif);
- 内存缓存
默认情况下,我们不用去特意的操作缓存设置,因为Glide默认会使用内存和硬盘缓存,但是如果在知道某一个图片会快速变化时,你可能会关闭缓存功能。
GlideApp
.with(context)
.load(eatFoodyImages[0])
.skipMemoryCache(true)
.into(imageView1);
上面调用了.skipMemoryCache(true)方法来告诉Glide禁用内存缓存功能。这就意味着Glide不会将图片缓存到内存中,但是这只是影响内存缓存,Glide仍然会将图片
缓存到硬盘中来避免下一次显示该图片时重复请求。
- 硬盘缓存
如上面所讲到的,即使你关闭了内存缓存,所请求的图片仍然会被保存在设备的磁盘存储上。如果你有一张不段变化的图片,但是都是用的同一个URL,你可能需要禁止磁盘缓存了。
你可以用.diskCacheStrategy()方法改变Glide的行为。不同于.skipMemoryCache()方法,它将需要从枚举型变量中选择一个,而不是一个简单的boolean。如果你想要禁止请求的磁盘缓存,使用枚举型变量DiskCacheStrategy.NONE作为参数。
GlideApp
.with(context)
.load(eatFoodyImages[1])
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView2);
上面的这种方式只是会禁用硬盘缓存,Glide还会使用内存缓存。如果想把内存缓存和硬盘缓存都禁用,需要把上面的两个方法都设置
GlideApp
.with(context)
.load(eatFoodyImages[1])
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(imageView2);
- 自定义磁盘缓存行为
如从上面提到的,Glide为硬盘缓存提供了多种设置方式。Glide的磁盘缓存是相当复杂的。例如,Picasso只缓存全尺寸图片。Glide,会缓存原始,全尺寸的图片和额外的小版本图片。例如,如果你请求一个1000x1000像素的图片,你的ImageView是500x500像素,Glide会保存两个版本的图片到缓存里。
DiskCacheStrategy.NONE禁用硬盘缓存功能DiskCacheStrategy.DATA只缓存原始的全尺寸图. 例如上面例子中1000x1000像素的图片DiskCacheStrategy.RESOURCE只缓存最终剪辑转换后降低分辨的图片。例如上面离职中500x500像素的图片DiskCacheStrategy.AUTOMATIC基于资源只能选择缓存策略(默认的行为)DiskCacheStrategy.ALL缓存所有分辨率对应的类型的图片图片请求优先级
Glide支持使用.priority()方法来设置图片请求的优先级。
- Priority.LOW
- Priority.NORMAL
- Priority.HIGH
- Priority.IMMEDIATE
private void loadImageWithHighPriority() {
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.priority(Priority.HIGH)
.into(imageViewHero);
}
private void loadImagesWithLowPriority() {
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[1])
.priority(Priority.LOW)
.into(imageViewLowPrioLeft);
GlideApp
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[2])
.priority(Priority.LOW)
.into(imageViewLowPrioRight);
}
- 缩略图
缩略图不同于前面文章中提到的占位图。占位图应当是跟app绑定在一起的资源。缩略图是一个动态的占位图,可以从网络加载。缩略图也会被先加载,直到实际图片请求加载完毕。如果因为某些原因,缩略图获得的时间晚于原始图片,它并不会替代原始图片,而是简单地被忽略掉。
Glide提供了两种产生缩略图的方式。第一种,是通过在加载的时候指定一个小的分辨率,产生一个缩略图。这个方法在ListView和详细视图的组合中非常有用。如果你已经在ListView中用到了250x250像素的图片,那么在在详细视图中会需要一个更大分辨率的图片。然而从用户的角度,我们已经看见了一个小版本的图片,为什么需要好几秒,同样的图片(高分辨率的)才能被再次加载出来呢?
在这种情况下,从显示250x250像素版本的图片平滑过渡到详细视图里查看大图更有意义。Glide里的.thumbnail()方法让这个变为可能。这里.thumbnal()的参数是一个浮点乘法运算。
String internetUrl = "http://i.imgur.com/DvpvklR.png";
GlideApp
.with(context)
.load(internetUrl)
.thumbnail(0.1f)
.into(imageView);
例如,如果你传递一个0.1f作为参数,Glide会加载原始图片大小的10%的图片。如果原始图片有1000x1000像素,缩略图的分辨率为100x100像素。
为.thumbnail()传入一个浮点类型的参数,非常简单有效,但并不是总是有意义。如果缩略图的生成也需要从网络加载同样全分辨率图片后才可以,那这样加载速度并不会比不用缩略图快。
因此Glide提供了另一个方法去加载和显示缩略图。第二种方式需要传递一个新的Glide请求作为参数。
String internetUrl = "http://i.imgur.com/DvpvklR.png";
// setup Glide request without the into() method
RequestBuilder<Drawable> thumbnailRequest = GlideApp
.with(context)
.load(internetUrl);
// pass the request as a a parameter to the thumbnail request
GlideApp
.with(context)
.load(UsageExampleGifAndVideos.gifUrl)
.thumbnail(thumbnailRequest)
.into(imageView);
区别在于第一个缩略图请求是完全独立于第二个原始请求的。缩略图可以来自不同资源或者图片URL,你可以在它上面应用不同的变换。
- 旋转图片
android.graphics.Matrix提供了旋转的方法:
Bitmap toTransform = ... // your bitmap source
Matrix matrix = new Matrix();
matrix.postRotate(rotateRotationAngle);
Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
但是如果要在Glide中使用旋转方法,需要用BitmapTransformation()进行包裹:
public class RotateTransformation extends BitmapTransformation {
private float rotateRotationAngle = 0f;
public RotateTransformation(Context context, float rotateRotationAngle) {
super( context );
this.rotateRotationAngle = rotateRotationAngle;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Matrix matrix = new Matrix();
matrix.postRotate(rotateRotationAngle);
return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
}
@Override
public String getId() {
return "rotate" + rotateRotationAngle;
}
}
然后将上面的方法传递到.transform()中:
private void loadImageOriginal() {
Glide
.with( context )
.load( eatFoodyImages[0] )
.into( imageView1 );
}
private void loadImageRotated() {
Glide
.with( context )
.load( eatFoodyImages[0] )
.transform( new RotateTransformation( context, 90f ))
.into( imageView3 );
}
参考
- Glide — Getting Started
- Glide — Advanced Loading
- Glide — ListAdapter (ListView, GridView)
- Glide — Placeholders & Fade Animations
- Glide — Image Resizing & Scaling
- Glide — Displaying Gifs & Video Thumbnails
- Glide — Caching Basics
- Glide — Request Priorities
- Glide — Thumbnails
- Glide — Callbacks: SimpleTarget and ViewTarget for Custom View Classes
- Glide — Exceptions: Debugging and Error Handling
- Glide — Custom Transformations
- Glide — Custom Animations with animate()
- Glide — Integrating Networking Stacks
- Glide — Customize Glide with Modules
- How to Rotate Images
- Glide Module Example: Customize Caching
- Glide Module Example: Optimizing By Loading Images In Custom Sizes
你的star是我的动力!!!
本文详细介绍Android图片加载库Glide的功能和使用方法,包括基本配置、加载不同来源的图片、图片转换、缓存策略、优先级设置等内容。
1055

被折叠的 条评论
为什么被折叠?



