Android Glide4 异步图片框架
实战项目案例
Google在2013年发布了网络文本和图片异步加载的Volley框架,而在2014年的Google IO app中推举Glide框架来加载图片。这说明,Glide比起Volley中ImageRequst更具备优势,节省内存和节省宽带数据。
这里,Volley框架和Glide框架用加载同样的网络资源,进行比较了一番。
RecyclerView中使用Volley的NetWorkImageView的内存情况:
RecyclerView中ImageView使用Glide的内存情况:
若是不熟悉Glide框架使用情况,可以阅读
Glide 框架
和
Glide v4新特性
。
使用Glide v4中的Generated API 开发
前期配置,在项目中Gradle中引入库的依赖
:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0-RC0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC0'
自定义AppGlideModule:
为运用程序定义一个带有@GlideModule注解的AppGlideModule,运用程序会使用和AppGlideMoudle同一个包下的GlideApp类。通过GlideApp.with()方式使用Glide的Generated API。
@GlideModule
public final class CustomAppGlideModule extends AppGlideModule{
* 通过GlideBuilder设置默认的结构(Engine,BitmapPool ,ArrayPool,MemoryCache等等).
* @param context
* @param builder
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setMemoryCache(new LruResourceCache(10*1024*1024));
* 为App注册一个自定义的String类型的BaseGlideUrlLoader
* @param context
* @param registry
@Override
public void registerComponents(Context context, Registry registry) {
registry.append(String.class, InputStream.class,new CustomBaseGlideUrlLoader.Factory());
* 清单解析的开启
* 这里不开启,避免添加相同的modules两次
* @return
@Override
public boolean isManifestParsingEnabled() {
return false;
注意点:
- 必需带有
@GlideModule注解。 isManifestParsingEnabled() 返回false,关闭解析AndroidManifest,不需要再配置GlideModule.
自定义BaseGlideUrlLoader:
根据带有图片尺寸的URl,来获取合适比例的图片资源。通过指定String类型的Model, BaseGliUrlLOader中getgetURL()来覆盖原本的带有http或者htpps的URL. 这里处理方式来源于,Google IO App.
public class CustomBaseGlideUrlLoader extends BaseGlideUrlLoader<String> {
private static final ModelCache<String, GlideUrl> urlCache =
new ModelCache<>(150);
* Url的匹配规则
private static final Pattern PATTERN = Pattern.compile("__w-((?:-?\\d+)+)__");
public CustomBaseGlideUrlLoader(ModelLoader<GlideUrl, InputStream> concreteLoader,ModelCache<String, GlideUrl> modelCache) {
super(concreteLoader,modelCache);
* If the URL contains a special variable width indicator (eg "__w-200-400-800__")
* we get the buckets from the URL (200, 400 and 800 in the example) and replace
* the URL with the best bucket for the requested width (the bucket immediately
* larger than the requested width).
* 控制加载的图片的大小
@Override
protected String getUrl(String model, int width, int height, Options options) {
Matcher m = PATTERN.matcher(model);
int bestBucket = 0;
if (m.find()) {
String[] found = m.group(1).split("-");
for (String bucketStr : found) {
bestBucket = Integer.parseInt(bucketStr);
if (bestBucket >= width) {
break;
if (bestBucket > 0) {
model = m.replaceFirst("w"+bestBucket);
return model;
@Override
public boolean handles(String s) {
return true;
* 工厂来构建CustormBaseGlideUrlLoader对象
public static class Factory implements ModelLoaderFactory<String,InputStream>{
@Override
public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new CustomBaseGlideUrlLoader(multiFactory.build(GlideUrl.class,InputStream.class),urlCache);
@Override
public void teardown() {
ProGuard Rules中添加混淆规则:
根据上面的自定义,保持AppGlideModule子类和GlideModule实现类不被混淆。
#Glide的混淆规则
-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 *;
使用权限 : 联网权限,读写权限,AndroidManifest.xml中自行配置。
确保GlideApp类正常引用:
当配置完以上步骤后,发觉不能使用GlideApp类。
解决方式:在AndroidStudio中Build–>Make Project
–>将会出现build/generated/source中,便可以使用GlideApp
使用GlideApp类用于各种场景:
1. 单个ImageView加载图像资源:
Url:https://www.baidu.com/img/bd_logo1.png
Uri:content://media/external/images/1
Resource Id :R.drawable.image或者R.mipmap.ic_launcher
当然,还有其他的图像资源。
加载本地图片:
* 加载本地图片,这里是mipmap文件夹下的资源
private void loadLocalImage() {
RequestBuilder<Drawable> drawableRequestBuilder = GlideApp.with(this).load(R.mipmap.ic_launcher);
drawableRequestBuilder.into(this.local_iv);
加载网络图片:
* 从远程网路上加载图片
private void loadRemoteImage() {
GlideApp.with(this).asBitmap()
.load(ImageResouce.imageResource[0])
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.into(this.remote_iv);
预先下载,本地缓存中加载:
* 预先加载资源
private void startPreload() {
GlideApp.with(this).asBitmap()
.load(ImageResouce.imageResource[1])
.diskCacheStrategy(DiskCacheStrategy.ALL)
.preload();
* 预先下载原始图片资源后,本地加载
private void loadPreloadImage() {
GlideApp.with(this).asBitmap().load(ImageResouce.imageResource[1]).diskCacheStrategy(DiskCacheStrategy.ALL).into(this.preload_iv);
error 和placeholder的处理:
.error(R.mipmap.ic_launcher) //异常图片
.placeholder(R.mipmap.ic_launcher) //占位图片
.fallback(R.mipmap.ic_launcher)
2. RecyclerView(或者ListView,GridView)中加载图片资源:
Glide单一要求是任何重复使用的View Target,调用Clear()API明确清除先前的加载,以防加载到旧数据。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
ImageView imageView=holder.getImageView();
String url=imageList.get(position);
if(TextUtils.isEmpty(url)){
GlideApp.with(context).clear(imageView);
imageView.setImageResource(R.mipmap.ic_launcher);
}else{
loadImage(url,imageView);
* 加载图片
* @param url
* @param imageView
public void loadImage(String url,ImageView imageView){
RequestBuilder<Bitmap> bitmapRequestBuilder= GlideApp.with(context)
.asBitmap()
.load(url)
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.fallback(R.mipmap.ic_launcher);
bitmapRequestBuilder.into(imageView);
Glide拥有两个默认的转换(transformation):
- Fit center:类似Android’s ScaleType.FIT_CENTER
- Center crop:类似Android’s ScaleType.CENTER_CROP
这里继承BitmapTransformation,复写transform():
public class CircleTransform extends BitmapTransformation{
public CircleTransform(Context context){
super(context);
* 重写 生成圆角图片
* @param pool
* @param toTransform
* @param outWidth
* @param outHeight
* @return
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
return circleCrop(pool,toTransform);
private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
return result;
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
Glide使用自定义的Transform,最后将圆形Bitmap加载到ImageView上:
* 加载图片
* @param url
* @param imageView
public void loadImage(String url,ImageView imageView){
RequestBuilder<Bitmap> bitmapRequestBuilder= GlideApp.with(context)
.asBitmap()
.load(url)
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.fallback(R.mipmap.ic_launcher);
RequestOptions requestOptions=new RequestOptions();
requestOptions.transform(new CircleTransform(context));
bitmapRequestBuilder.apply(requestOptions).into(imageView);
4. 自定义BitmapImageViewTarget实现圆角图片:
自定义一个BitmapImageViewTarget,复写setResource():
public class CircularBitmapImageViewTarget extends BitmapImageViewTarget {
private Context context;
private ImageView imageView;
public CircularBitmapImageViewTarget(Context context,ImageView view) {
super(view);
this.context=context;
this.imageView=view;
* 重写 setResource(),生成圆角的图片
* @param resource
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable bitmapDrawable= RoundedBitmapDrawableFactory.create(this.context.getResources(),resource);
* 设置图片的shape为圆形.
* 若是需要制定圆角的度数,则调用setCornerRadius()。
bitmapDrawable.setCircular(true);
this.imageView.setImageDrawable(bitmapDrawable);
Glide使用自定义ViewTarget:
* 加载图片
* @param url
* @param imageView
public void loadImage(String url,ImageView imageView){
RequestBuilder<Bitmap> bitmapRequestBuilder= GlideApp.with(context)
.asBitmap()
.load(url)
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.fallback(R.mipmap.ic_launcher);
bitmapRequestBuilder.into(new CircularBitmapImageViewTarget(context,imageView));
项目运行效果如下:
资源汇总:
问题汇总:
在listView或者RecyclerView中使用Glide框架,内存剧增或者爆内存溢出(OutOfMemoryError):
原因:在ImageView中scaleType使用了fitxy属性:
<ImageView
android:id="@+id/item_movielist_iv"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitXY"/>
将fitXY改动成:fitCenter 或者centerCrop,内存情况如下:
解决方式最终来源:https://github.com/bumptech/glide/issues/464
解决同一URL远程图片,多次按不同大小比例加载多次加载的问题:
Preload Images,将URL对应的数据源即原图保存下载,下次按比例来本地加载:
diskCacheStrategy(DiskCacheStrategy.ALL)
//Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
新建一个java类,只需继承自AppGlideModule和添加@GlideModule标注
控制台输出错误信息如下:
Failed to find GeneratedAppGlideModule. You should include an annotationProcessor compile dependency
创建一个GeneratedAppG...
RequestOptions options = new RequestOptions() .skipMemoryCache(true); Glide.with(this) .load(url) .apply(options) .into(imageView);
可以看到,只需要调用skipMemoryCache()方法并传入true,就表示禁用掉Glide的内存缓存功能。
接下来我们开始学习硬盘缓存方面的内容。
其实在刚刚学习占位图功能的时候,我们就使用
Android Studio4.x踩坑:W/Glide: Failed to find GeneratedAppGlideModule. You should include an annota…
最近用android studio写项目真的遇到太多坑了,每天都在踩坑出坑(生无可恋.jpg)
言归正传,今天我运行项目时给我报出了一个这个错:W/Glide: Failed to find GeneratedAppGlideModule. You should include an annotationPro
听枫烨阁:
Android NDK之Strip裁减so库
Android NDK之Strip裁减so库
听枫烨阁:
Android安装系统App(adb push实现)
Abro.: