Android Glide 图片加载框架解析

时间:2022-07-22 09:18:29

        在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载框架,作者是  bumptech,这个库被广泛的应用在 Google 开源项目中,包括 2014 年 Google I/O 大会上发布的官方 App,现在 Android 开发中已经有非常多的图片类的加载框架库,有前几年非常流行的 ImageLoader,以及现在的 Picasso、Fresco 等一些非常稳定和流行的框架,并且每一个功能都非常强大,但我们终究还是要选择一个进行学习,这里我选择 Glide,因为从简单易用性方面来说,Picasso 和 Glide 更简单易用,功能上都非常强大,但是 Picasso 和 Glide 相比较,Picasso 更加简洁,也更轻量,但 Glide 功能也更为丰富,但是事情总是没有绝对的,没有最好的最好的框架,可以根据项目的需求选择自己更合适的,接下来我们开始解析


一、概述

下面我们简单的描述一下 Glide 图片开发框架的相关情况:

1)简单易用

2)可配置度高,自适应程度强

3)支持常见的图片格式,Jpg、png、gif、webp 等

4)支持各种数据源,网络、本地、资源、Assets 等

5)高效缓存策略,支持 Memory 和 Disk 图片缓存,默认 Bitmap 格式采用 RGB_565 内存使用至少减少一半

6)生命周期集成根据 Activity/Fragment 生命周期自动管理请求

7)高效处理 Bitmap,主动调用 recycle 回收需要回收的 Bitmap 减小系统回收压力

8)下载地址:https://github.com/bumptech/glide


二、使用

要使用 Glide,首先要把这个库引入到我们的项目中,新建一个 GlideSample 项目,然后在 app 模块的 build.gradle 里添加如下依赖:

这里需要注意的是如果项目依赖库里没有 support-v4 的支持库的话也需要添加一下

dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}

另外 Glide 需要用到网络,需要在 AndroidManifest.xml 中声明一下网络权限:

<uses-permission android:name="android.permission.INTERNET"/>

到这里我们就可是使用 Glide 的所有功能了

接下来我们就来使用 Glide 来加载一张图片:

图片路径:http://pic68.nipic.com/file/20150601/13044986_145039092624_2.jpg

那么接下来在布局文件中加入一个 ImageView 和一个 Button 如下所示: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_glide"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.qiudengjiao.glidesample.GlideActivity">

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="load"
android:text="点击加载图片" />

<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

接下来我们来写 GlideActivity 中的代码如下:

/**
* Glide图片加载框架测试
*/
public class GlideActivity extends AppCompatActivity {

ImageView image;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_glide);
initView();
}

private void initView() {
image = (ImageView) findViewById(R.id.iv_image);
}

/**
* 响应图片加载的点击事件
*
* @param view
*/
public void load(View view) {
String url = "http://pic68.nipic.com/file/20150601/13044986_145039092624_2.jpg";

//这里就是加载图片的代码
Glide.with(GlideActivity.this).load(url).into(image);
}
}

这样我们的图片就成功加载了,就是这么简单,一行代码搞定,我们运行一下程序来欣赏一下美丽的金门大桥:

Android Glide 图片加载框架解析

可以看到网络上的金门大桥图片已经被成功加载,并且展示到了我们的 ImageView 上

而我们只写了一行简单的代码:

Glide.with(GlideActivity.this).load(url).into(image);

接下来我们对这行代码进行解析:


1)先看 Glide.with() 方法:

         在这里我们首先调用了 Glide.with() 方法,创建一个实例,然后我们来看 with 里面接受的参数,这里接受 Context、Activity、FragmentActivity、Fragment 类型的参数,这里也就是说不管是在 Activity 还是在 Fragment 中调用 with() 方法,直接传入 this 就好,但是如果不在 Activity 或者 Fragment 中我们还可以直接传应用程序的上下文 ApplicationContext,这里需要注意的是,就像我们上面概述第六条里说的:Glide 生命周期集成根据 Activity/Fragment 生命周期自动管理请求,也就是说,with 方法里面传入的实例Fragment、Activity、FragmentActivity、ApplicationContext,会影响 Glide 的生命周期,如果是 Fragment 或 Activity、FragmentActivity 那么当相对应的 Fragment 和 Activity 和 FragmentActivity 销毁的时候,图片加载也会停止,如果传入的是应用的上下文 ApplicationContext  那么只有当应用销毁的时候,图片加载才会停止,为了大家看起来简单明了

总结如下:

  • with(Context context) 使用 Application 上下文,Glide  请求将不受 Activity/Fragment 生命周期控制
  • with(Activity activity) 使用 Activity 上下文,Glide 请求将受到 Activity 生命周期控制
  • with(Fragment fragment)  使用 Fragment 上下文,Glide 请求将受到 Fragment 生命周期控制
  • with(FragmentActivity activity)  Glide 请求将会受到 FragmentActivity 生命周期的控制
返回关联了相应上下文的 RequestManager 实例,我们简单看一下源码如下:
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}

public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

 public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

 public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}

 public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
如果想看得更明白,大家可以在自己的代码里具体看

2)再看 load() 方法:

同样我们来看 load() 方法中能传入的实例,可以是 String、Uri、File、Integer、byte[] 等,也就是说支持加载各种图片资源,包括网络图片、Uri 对象、本地图片、应用资源、二进制流等等,

        //这里就是加载图片的代码(网络图片)
Glide.with(GlideActivity.this).load(url).into(image);

//加载Uri
Uri image = getImageUri();
Glide.with(GlideActivity.this).load(image).into(image1);

//加载本地图片
File file = new File(getExternalCacheDir()+"/image.jpg");
Glide.with(GlideActivity.this).load(file).into(image2);

//加载应用资源
int resource = R.drawable.ic_launcher;
Glide.with(GlideActivity.this).load(resource).into(image3);

//加载二进制流
byte[] image = getImageBytes();
Glide.with(GlideActivity.this).load(image).into(image4);

3)接下来看最后一个 into() 方法:

这里就是你要往哪个 ImageView 控件上显示,把对应实例传进去就好了,这里我们来总结一下 Glide 的用法,先 with(),再 load(),最后 into(),这样 Glide 的最简单用法我们就介绍完了,相信你对这三步也更加理解了一些,深入的理解的话我们可以去点进去看相关的源码,我们上面说到的都能看到

4)指定图片的大小:

   /**
* 响应图片加载的点击事件
*
* @param view
*/
public void load(View view) {
String url = "http://pic68.nipic.com/file/20150601/13044986_145039092624_2.jpg";

//这里就是加载图片的代码
Glide.with(GlideActivity.this)
.load(url)
.override(200, 300)
.into(image);
}

这里我们在刚才代码的 load() 方法和 into() 方法中间加了一个 override() 方法,直接传入int类型的值就可以指定和调整图片的尺寸,一般实际在项目里面,我们都不在这里指定图片的大小,可以在 xml 文件里面直接指定,Glide 可以防止内存的浪费和溢出,因为 Glide 从来不会将图片的完整尺寸加载到内存中,用多少加载多少,Glide 会自动判断 ImageView 的大小,可以有效色避免运行程序的 OOM

5)占位图

我们在实际项目的应用中,图片的加载往往有一个过程,如果是从网络上下载图片,就会有等待的过程,因为图片的加载都需要一定的时间,这样在网络不好的情况下,用户往往只能看到一片空白,感觉不是很好,为了有更好的额体验,我们可以先显示一张占位图,等图片加载出来再替代占位图,修改代码加载如下:

 Glide.with(GlideActivity.this)
.load(url)
.override(200, 300)
.placeholder(R.drawable.ic_launcher)
.into(image);

      这里我们又插入了一个 placeholder() 方法,然后将图片的资源 Id 传入即可,为了方便起见这里我们直接用默认系统自带的小机器人图片,在实际项目中大家可以换成 UI 规定的图片,到这里我们大概也就明白了,我们想要添加什么功能,只需要在 load() 方法和 into() 方法中间去添加相应的方法就好了,我们概述里第一条说的简单易用就完全体现出来了,我们运行程序,根本都没有看到占位图,这是什么鬼,难道是我们的代码有问题?我们的代码没有任何问题,这是因为 Glide 自带缓存机制,我们刚才加载的图片 Glide 已经缓存下来了,再次加载就会从缓存中直接读取,不会再去网络中加载,这样显示的速度就特别快,所以我们看能看不到先有占位图的效果,为了让占位图显示出来,我们修改代码如此下:

        Glide.with(GlideActivity.this)
.load(url)
.override(200, 300)
.placeholder(R.drawable.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(image);
        可以看到我们又在中间插入了一个 diskCacheStrategy() 方法,并且传入了 diskCacheStrategy.NONE 参数,利用这句代码我们就禁止了 Glide 的缓存功能,当然这里我们只是为了能够更好的去看到占位图,实际项目中大多情况下我们用不到,因为这样我们将会非常的消耗用户的流量,另外 Glide 还为我们提供了异常占位图,用发和上面的类似,使用 error() 方法,这样可以防止在某些异常情况下图片加载失败的情况下用来显示,这里我们也用系统图片代替了,用法如下:

        Glide.with(GlideActivity.this)
.load(url)
.override(200, 300)
.placeholder(R.drawable.ic_launcher)
.error(R.drawable.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(image);

6)支持加载 GIF 图片

       这里就要介绍 Gilde 的大招,也是 Glide 一个非常强大的功能,是 Picasso 所不具备的,那就是 Glide 支持加载 GIF 图片,这里给大家提供一个动图的地址如下:

这里本来想给大家展示一下的,但遗憾没有转化成功,大家就自行在程序里看一下吧

地址:http://image94.360doc.com/DownloadImg/2016/02/0520/65575746_6.gif

这里大家直接传入 load() 里就好,Glide 会自动判断是静态图还是 GIF 图,并且可以正确的解析出来


7)下面对一些方法进行简单介绍:

  • asGif() 把资源作为 GifDrawable 对待,如果资源不是 gif 动画,将会加载失败,回调我们上面提到的 error() 方法
  • asBitmap() 无论资源是不是 gif 动画都作为 Bitmap 对待,如果是 gif 动画会停在第一帧
  • diskCacheStrategy(DiskCacheStrategy strategy) 设置缓存策略。DiskCacheStrategy.SOURCE:缓存原始数据,DiskCacheStrategy.RESULT:缓存变换(如缩放、裁剪等)后的资源数据,DiskCacheStrategy.NONE:什么都不缓存,DiskCacheStrategy.ALL:缓存SOURC和RESULT。默认采用DiskCacheStrategy.RESULT 策略,对于 download only 操作要使用 DiskCacheStrategy.SOURCE
  • thumbnail(float sizeMultiplier)  请求给定系数的缩略图,如果缩略图比全尺寸图先加载完,就显示缩略图,否则就不显示。系数sizeMultiplier必须在(0,1)之间,可以递归调用该方法
  • priority(Priority priority)  指定加载的优先级,优先级越高越优先加载,但不保证所有图片都按序加载。枚举 Priority.IMMEDIATE、Priority.HIGH、Priority.NORMAL、Priority.LOW,默认为 Priority.NORMAL
  • sizeMultiplier(float sizeMultiplier). 在加载资源之前给 Target 大小设置系数
今天的内容就写到这里,只是对 Glide 的相关用法做了简单的介绍,大家可以更加深入的去了解 Glide 的背后实现原理