Glide最新版4.11.0的使用

PREVIEW

  • 导入

    • 使用Project Structure导入
    • 添加依赖导入
  • 正文

    • 简单使用
    • GlideRequests.load()方法
    • 加载GIF
    • GlideRequest的更多方法
    • 缓存处理
    • 优先级设置
    • 使用Target
    • 图片变换
    • 使用动画
    • 使用Module

使用Project Structure导入

  • 打开Project Structure
->Project Structure
  • 添加库依赖

选择Library Dependency

->Library Dependency
  • 选择版本

搜索com.github.bumptech.glide:glide*,选择你要的版本

搜索Glide库

添加依赖导入

如果你不需要最新版本,在build.gradle下添加依赖

dependencies {
    ...
    implementation 'com.github.bumptech.glide:glide:4.11.0'
}

简单使用

本文使用的是Glide目前最新的4.11.0版本(截止2020/6/5)。Glide v4相比Glide v3拥有新的特性——Generated API

String url="https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

ImageView imageView = findViewById(R.id.imageView);

Glide.with(this)//返回RequestManager以调用RequestManager.load方法,默认为RequestManager<Drawable>
        .load(url)//加载指定url的图片,返回RequestBuilder(在into之前均返回RequestBuilder),默认为RequestBuilder<Drawable>
        .into(imageView);//显示图片的ImageView,返回用于完成本次请求的Target

确保网络权限已申请

  • 使用Generated API时的写法

需要使用Glide v4新的Generated API时,我们需要继承AppGlideModule,但暂时不需要重写其中的方法。之后我们就可以使用GlideApp.with(this)代替Glide.with(this)。而使用Glide.with(this)是无法调用Generated API的

@GlideModule//加上模块注解
public class MyAppGlideModule extends AppGlideModule {

}

GlideApp.with(this)//返回GlideRequests以调用GlideRequests.load方法
        .load(url)//加载指定url的图片,返回GlideRequest(在into之前均返回GlideRequest),默认为GlideRequest<Drawable>
        .into(imageView);//显示图片的ImageView,返回用于完成本次请求的Target

GlideRequests.load()方法

GlideRequests.load()有多种重载,一般使用的是GlideRequests.load(String string),其中传入URL字符串。另有三种常用重载,分别是GlideRequests.load(Integer id)GlideRequests.load(File file)GlideRequests.load(Uri uri)

  • 从资源文件加载
GlideApp.with(this)
    .load(R.drawable.close_filled)
    .into(imageView);
  • 从文件加载
File file = new File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_DCIM), "building.jpg");//DCIM目录下的一张图片

GlideApp.with(this)
    .load(file)
    .into(imageView);

确保访问外部存储权限已申请

  • 从URI加载
File file = new File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_DCIM), "building.jpg");//DCIM目录下的一张图片

Uri uri=Uri.fromFile(file);

GlideApp.with(this)
    .load(uri)
    .into(imageView);

加载GIF

用法同静态图片

String url = "https://media0.giphy.com/media/3oriObQfEBGIn9qlIA/" +
    "giphy.gif?cid=ecf05e476515e2a51ff992790e3c19c96cbdbd1716e8f0fe&rid=giphy.gif";

GlideApp.with(this)
    .load(url)
    .into(imageView);

GlideRequest的更多方法

  • placeholder(int id)
    占位符,当url未加载完成时显示的图片

  • error(int id)
    当url加载失败时显示的图片

  • fallback(int id)
    当url为null时显示的图片

  • thumbnail(RequestBuilder<TranscodeType> builder)
    以另一张图片作为缩略图

  • thumbnail(float sizeMultiplier)
    以本张图片为缩略图,按长度宽度缩小

  • dontAnimate()
    不显示动画

  • override(int width, int height)
    按(width,height)规则裁剪图片,但不会拉伸图片,长度宽度中较大的不合理值将被调整为较小的合理值,最终显示时依据scaleType可能进行拉伸,但缓存中为裁剪后非拉伸的图片

  • centerCrop()
    设置显示图片的ImageView的scaleType值为CENTER_CROP

  • fitCenter()
    设置显示图片的ImageView的scaleType值为FIT_CENTER(scaleType的默认值就是FIT_CENTER)

  • circleCrop()
    将图片裁剪为圆形,缓存中也是圆形

String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";
String url2 = "https://img.alicdn.com/tps/TB1ld1GNFXXXXXLapXXXXXXXXXX-200-200.png";

GlideRequest<Drawable> thumbnailRequest = GlideApp
    .with(this)
    .load(url2);

ImageView imageView = findViewById(R.id.imageView);
GlideApp.with(this)//返回GlideRequests以调用GlideRequests.load方法
    .load(url)////加载指定url的图片,返回GlideRequest(在into之前均返回GlideRequest),默认为GlideRequest<Drawable>
    .placeholder(R.drawable.help_filled)
    .error(R.drawable.close_filled)
    .fallback(R.drawable.close_filled)
    .thumbnail(thumbnailRequest)
    .thumbnail(0.2f)
    .dontAnimate()
    .override(100,100)
    .centerCrop()
    .fitCenter()
    .circleCrop()
    .into(imageView);//显示图片的ImageView,返回用于完成本次请求的Target

使用Generated API

Generated API是Glide v4的新特性,让我们能够通过编写自定义方法来复用一系列option的流式调用,相当于扩展了Glide的API。Glide将会根据@GlideExtension注解找到所有扩展类,在编译阶段全部合并,并作为Glide API的一部分

  • 添加Glide注解处理器的依赖
dependencies {
    ...
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'//版本与Glide一致
}
  • 新建工具类MyGlideExtension
    Glide4.9.0以后的写法(包括4.9.0)
@GlideExtension//加上扩展注解
public class MyGlideExtension {
    private MyGlideExtension() { }//必须是私有的空的构造方法

    @GlideOption//加上扩展注解
    public static BaseRequestOptions<?> basicPreparation(BaseRequestOptions<?> options) {
        return options
                .placeholder(R.drawable.help_filled)
                .error(R.drawable.close_filled)
                .fallback(R.drawable.close_filled);
    }
}

Glide4.9.0之前的写法

@GlideExtension
public class MyGlideExtension {
    private MyGlideExtension() { }

    @GlideOption
    public static void basicPreparation(RequestOptions options) {
        options
            .placeholder(R.drawable.help_filled)
            .error(R.drawable.close_filled)
            .fallback(R.drawable.close_filled);
    }
}

@GlideExtension注解的类应以工具类的思维编写。这种类应该有一个私有的、空的构造方法,应为final类型,并且仅包含静态方法。被注解的类可以含有静态变量可以引用其他的类或对象。所有的扩展类都会在编译时合并如果构造方法不是私有的、空的或是合并时产生冲突都会产生编译错误。

  • 扩展API的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

GlideApp.with(this)
    .load(url)
    .basicPreparation()//完成占位符等一系列方法的调用
    .into(imageView);

缓存处理

Glide默认使用LRU缓存。关于检查缓存的过程,以下内容的一部分是来自Glide中文文档的介绍:

默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:

活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?
内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?
资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?(例如使用了.transform().circleCrop().override()等方法改变了长宽或像素之后的图片)
数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?(检查原图)

前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等)。

内存缓存和磁盘缓存设置

GlideApp.with(this)
    .load(url)
    .skipMemoryCache(true)//跳过内存缓存
    .diskCacheStrategy(DiskCacheStrategy.NONE)//不使用磁盘缓存
    .into(imageView);

DiskCacheStrategy参数

Glide v4参数

  • DiskCacheStrategy.NONE
    不缓存

  • DiskCacheStrategy.DATA
    只缓存下载后的原图

  • DiskCacheStrategy.RESOURCE
    只缓存通过各种变换最终显示在界面上的的图片

  • DiskCacheStrategy.ALL
    同时进行DATA和RESOURCE两种缓存,但如果数据来自本地,则只进行RESOURCE缓存

  • DiskCacheStrategy.AUTOMATIC
    如果是远程下载的图片则只缓存DATA,如果是本地加载的图片则只缓存RESOURCE(默认策略)

Glide v3参数

  • DiskCacheStrategy.NONE
    不缓存

  • DiskCacheStrategy.SOURCE
    只缓存下载后的原图

  • DiskCacheStrategy.RESULT
    只缓存通过各种变换最终显示在界面上的的图片(默认策略)

  • DiskCacheStrategy.ALL
    同时进行SOURCE和RESULT两种缓存

仅尝试从缓存中检索图片

GlideApp.with(this)
    .load(url)
    .onlyRetrieveFromCache(true)//若未从缓存中检索到图片则加载失败
    .into(imageView);

清除磁盘缓存

final Context context=getApplicationContext();

new AsyncTask<Void, Void, Void>() {
    @Override
    protected Void doInBackground(Void... params) {
        Glide.get(context).clearDiskCache();//清除磁盘缓存,必须在子线程上调用
        return null;
    }
}.execute(null,null,null);

动态URL处理

当需要向服务器传递一些参数时,我们可能会使用这样的URL:
http://www.placehold.it/500x500?source=feed
http://www.placehold.it/500x500?source=detail
两个URL不同但指向同一张图片,而Glide在缓存时会以URL作为图片缓存的唯一标识。不同的URL将导致多余的缓存。加载第一个URl后,Glide会缓存对应的图片,再加载第二个URL时,Glide会因找不到这个URL对应的缓存而再次发送请求,消耗不必要的网络资源,实际上两个URL都应该指向同一个缓存

  • 解决方案——继承GlideUrl

GlideUrl.getCacheKey()返回的值是Glide对图片进行缓存时使用的唯一标识,我们只需要确保Glide使用的是经过处理得到的不含参数的URL即可,以下GlideUrlWithQueryParameter类来自Future Studio

public class GlideUrlWithQueryParameter extends GlideUrl {
    private String mSourceUrl;

    public GlideUrlWithQueryParameter(String baseUrl, String key, String value) {
        super(buildUrl(baseUrl, key, value));

        mSourceUrl = baseUrl;
    }

    public GlideUrlWithQueryParameter(String baseUrl, Map<String, Object> queryParams) {
        super(buildUrl(baseUrl, queryParams));

        mSourceUrl = baseUrl;
    }

    private static String buildUrl(String baseUrl, String key, String value) {
        StringBuilder stringBuilder = new StringBuilder(baseUrl);

        if (stringBuilder.toString().contains("?")) {
            stringBuilder.append("&");
        }
        else {
            stringBuilder.append("?");
        }

        stringBuilder.append(key);
        stringBuilder.append("=");
        stringBuilder.append(value);

        return stringBuilder.toString();
    }

    private static String buildUrl(String baseUrl, Map<String, Object> queryParams) {
        StringBuilder stringBuilder = new StringBuilder(baseUrl);

        for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            if (stringBuilder.toString().contains("?")) {
                stringBuilder.append("&");
            }
            else {
                stringBuilder.append("?");
            }

            stringBuilder.append(key);
            stringBuilder.append("=");
            stringBuilder.append(value);
        }

        return stringBuilder.toString();
    }

    @Override
    public String getCacheKey() {
        return mSourceUrl;
    }

    @Override
    public String toString() {
        return super.getCacheKey();
    }
}

优先级设置

如果有多张图片请求同时发起,而你希望优先加载其中一张,可以将其请求优先级调高,但Glide不保证实际加载顺序一定依据优先级而定

GlideApp.with(this)
    .load(url)
    .priority(Priority.HIGH)
    .into(imageView);

Priority参数

  • Priority.LOW
  • Priority.NORMAL
  • Priority.HIGH
  • Priority.IMMEDIAT

使用Target

由以上内容可以看出into()方法完成了加载图片到imageView的全部过程,但如果要将图片用于其他控件(比如自定义控件)时就需要借助Target。由于SimpleTargetViewTargetGlide4.8.0已弃用,我们应该在Glide新版本中使用CustomTargetCustomViewTarget分别代替SimpleTargetViewTarget

弃用的原因是在过去使用SimpleTargetViewTarget时继承的Target.onLoadCleared()拥有默认的空实现。用户容易忽略在bitmap回收时清除来自UI的引用,这就有可能导致崩溃等问题

CustomTargetCustomViewTarget分别将onLoadCleared()onResourceCleared()写成了抽象方法,强制用户去实现,为的是尽可能避免以往的问题。以下是来自CustomTarget的API文档的说明:

You MUST implement Target.onLoadCleared(Drawable) and ensure that all references to any resource passed into the target in Target.onResourceReady(Object, Transition) are removed before Target.onLoadCleared(Drawable) completes.Failing to do so can result in graphical corruption, crashes caused by recycled Bitmaps, and other undefined behavior. It is never safe to leave Target.onLoadCleared(Drawable) unimplemented or empty.

  • CustomTarget的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

CustomTarget<Bitmap> target = new CustomTarget<Bitmap>() {
    @Override
    public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
        //回调内容
        imageView.setImageBitmap(resource);
    }

    @Override
    public void onLoadCleared(@Nullable Drawable placeholder) {
        //这个方法在target被回收时调用,如果在除了imageView以外的地方引用了imageView中的bitmap,在这里清除引用以避免崩溃
    }
};

GlideApp.with(this)
        .load(url)
        .into(target);
  • CustomViewTarget的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

CustomViewTarget<ImageView,Drawable> customViewTarget=new CustomViewTarget<ImageView, Drawable>(imageView) {
    @Override
    protected void onResourceCleared(@Nullable Drawable placeholder) {
        //这个方法在drawable被回收时调用,如果在除了imageView以外的地方引用了imageView中的bitmap,在这里清除引用以避免崩溃
    }

    @Override
    public void onLoadFailed(@Nullable Drawable errorDrawable) {

    }

    @Override
    public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
        //回调内容
        imageView.setImageDrawable(resource);
    }
};

GlideApp.with(this)
        .load(uri)
        .into(customViewTarget);

图片变换

  • 为圆角卡片效果实现抽象类BitmapTransformation
public class CardTransformation extends BitmapTransformation {
    private int radius_px;

    public CardTransformation(int radius_px) {
        this.radius_px = radius_px;
    }

    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        //尝试从缓存中获取长宽及config与下载图片(toTransform)相同的bitmap
        //若找到则将全部像素置为透明并返回,反之重新分配内存新建bitmap并返回
        Bitmap blankBitmap = pool.get(toTransform.getWidth(), toTransform.getHeight(),
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(blankBitmap);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        canvas.drawRoundRect(0, 0, toTransform.getWidth(), toTransform.getHeight(),
                radius_px, radius_px, paint);
        return blankBitmap;
    }

    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { }
}
  • BitmapTransformation的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

Transformation cardTransformation = new CardTransformation(48);//半径像素为48的圆角卡片效果

GlideApp.with(this)
    .asBitmap()//使用BitmapTransformation需要GlideRequest<Bitmap>,而不是默认的GlideRequest<Drawable>
    .load(url)
    .transform(cardTransformation)
    .into(imageView);

使用动画

Glide v4开始已取消RequestBuilder.animate()方法,我们使用GlideRequest.transition()代替

通过XML资源实现动画

  • 在res/anim目录下定义XML动画fade_in_and_fill.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200">
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />
    <scale
        android:fromXScale="0.7"
        android:fromYScale="0.7"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1" />
</set>
  • GlideRequest.transition()的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

Transformation cardTransformation = new CardTransformation(48);

GlideApp.with(this)
    .load(url)
    .transform(cardTransformation)
    .transition(GenericTransitionOptions.with(R.anim.fade_in_and_fill))
    .into(imageView);

通过继承ViewPropertyTransition.Animator实现动画

使用属性动画的方式可用于自定义控件

  • 继承ViewPropertyTransition.Animator
public class EnterAnimator implements ViewPropertyTransition.Animator {
    @Override
    public void animate(View view) {
        AnimatorSet set=new AnimatorSet();
        ObjectAnimator oaAlpha=ObjectAnimator.ofFloat(view,"alpha",0,1);
        ObjectAnimator oaScaleX=ObjectAnimator.ofFloat(view,"scaleX",0.7f,1);
        ObjectAnimator oaScaleY=ObjectAnimator.ofFloat(view,"scaleY",0.7f,1);
        set.playTogether(oaAlpha,oaScaleX,oaScaleY);
        set.setDuration(200);
        set.start();
    }
}
  • GlideRequest.transition()的使用
String url = "https://w.wallhaven.cc/full/md/wallhaven-md5z28.jpg";

ViewPropertyTransition.Animator amin=new EnterAnimator();

GlideApp.with(this)
    .asBitmap()
    .load(url)
    .transform(cardTransformation)
    .transition(GenericTransitionOptions.with(amin))
    .into(imageView);

使用Module

集成OkHttp3

在build.gradle中添加除了基本的OkHttp3依赖以外,还需要添加继承库的依赖,之后Glide会自动使用OkHttp3代替默认的HttpURLConnection

dependencies {
    ...
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
    implementation 'com.squareup.okhttp3:okhttp:4.7.2'
    implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'//版本和Glide一致
}

Glide设置

  • 继承AppGlideModule
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
        //将解码方式设置为ARGB_8888,但Glide4.11.0中默认解码方式已经是ARGB_8888
        RequestOptions options=new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888);
        builder.setDefaultRequestOptions(options);
    }
}
  • 配置Manifest文件
<application
    ...
    <meta-data
        android:name="包名.MyAppGlideModule"
        android:value="AppGlideModule"/>
</application>
  • 设置内存缓存大小和位图池大小
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build();
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();//单位为字节
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();//单位为字节,默认为内存缓存的一半

int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);

builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
  • 设置磁盘缓存大小
int _6MB = 6 * 1024 * 1024;

builder.setDiskCache(new InternalCacheDiskCacheFactory(context, _6MB));//6MB,单位为字节