Android图片加载框架最全解析(一),Glide的基本用法

本文同步发表于个人微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 便可关注,天天都有文章更新。html

如今 Android 上的图片加载框架很是成熟,从最先的老牌图片加载框架 UniversalImageLoader,到后来 Google 推出的 Volley,再到后来的新兴军 Glide 和 Picasso,固然还有 Facebook 的 Fresco。每个都很是稳定,功能也都十分强大。可是它们的使用场景基本都是重合的,也就是说咱们基本只须要选择其中一个来进行学习和使用就足够了,每个框架都尝试去掌握的话则有些浪费时间。android

在这几个框架当中,我对 Volley 和 Glide 研究得比较深刻,对 UniversalImageLoader、Picasso 和 Fresco 都只是有一些基本的了解。从易用性上来说,Glide 和 Picasso 应该都是完胜其余框架的,这两个框架都实在是太简单好用了,大多数状况下加载图片都是一行代码就能解决的,而 UniversalImageLoader 和 Fresco 则在这方面略逊一些。git

那么再拿 Glide 和 Picasso 对比呢,首先这两个框架的用法很是类似,但其实它们各有特点。Picasso 比 Glide 更加简洁和轻量,Glide 比 Picasso 功能更为丰富。以前已经有人对这两个框架进行过全方面的对比,你们若是想了解更多的话能够去参考一下 这篇文章github

总之,没有最好的框架,只有最适合本身的框架。通过多方面对比以后,我仍是决定选择了 Glide 来进行研究,而且这也是 Google 官方推荐的图片加载框架。缓存

说实话,关于 Glide 的文章我已经筹备了很久,去年这个时候原本就打算要写了,可是一直都没有动笔。由于去年个人大部分时间都放在了写《第二行代码》上面,只能用碎片时间来写写博客,可是 Glide 的难度远超出了我用碎片时间所能掌握的难度。固然,这里我说的是对它的源码进行解析的难度,不是使用上的难度,Glide 的用法是很简单的。因此,我以为去年我写很差 Glide 这个题材的文章,也就一直拖到了今年。微信

而如今,我花费了大量的精力去研究 Glide 的源码和各类用法,相信如今已经能够将它很是好地掌握了,所以我准备将我掌握的这些知识整理成一个新的系列,帮忙你们更好地学习 Glide。这个 Glide 系列大概会有 8 篇左右文章,预计花半年时间写完,将会包括 Glide 的基本用法、源码解析、高级用法、功能扩展等内容,可能会是目前互联网上最详尽的 Glide 教程。markdown

那么本篇文章是这个系列的第一篇文章,咱们先来了解一下 Glide 的基本用法吧。网络

Glide 是一款由 Bump Technologies 开发的图片加载框架,使得咱们能够在 Android 平台上以极度简单的方式加载和展现图片。app

目前,Glide 最新的稳定版本是 3.7.0,虽然 4.0 已经推出 RC 版了,可是暂时问题还比较多。所以,咱们这个系列的博客都会使用 Glide 3.7.0 版原本进行讲解,这个版本的 Glide 至关成熟和稳定。框架

要想使用 Glide,首先须要将这个库引入到咱们的项目当中。新建一个 GlideTest 项目,而后在 app/build.gradle 文件当中添加以下依赖:

dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
复制代码

若是你还在使用 Eclipse,能够点击 这里 下载 Glide 的 jar 包。

另外,Glide 中须要用到网络功能,所以你还得在 AndroidManifest.xml 中声明一下网络权限才行:

<uses-permission android: />
复制代码

就是这么简单,而后咱们就能够自由地使用 Glide 中的任意功能了。

如今咱们就来尝试一下如何使用 Glide 来加载图片吧。好比这是必应上一张首页美图的地址:

http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg
复制代码

而后咱们想要在程序当中去加载这张图片。

那么首先打开项目的布局文件,在布局当中加入一个 Button 和一个 ImageView,以下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Image"
        android:onClick="loadImage"
        />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
复制代码

为了让用户点击 Button 的时候可以将刚才的图片显示在 ImageView 上,咱们须要修改 MainActivity 中的代码,以下所示:

public class MainActivity extends AppCompatActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.image_view);
    }

    public void loadImage(View view) {
        String url = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";
        Glide.with(this).load(url).into(imageView);
    }

}
复制代码

没错,就是这么简单。如今咱们来运行一下程序,效果以下图所示:

能够看到,一张网络上的图片已经被成功下载,而且展现到 ImageView 上了。

而咱们到底作了什么?实际上核心的代码就只有这一行而已:

Glide.with(this).load(url).into(imageView);
复制代码

千万不要小看这一行代码,实际上仅仅就这一行代码,你已经能够作很是很是多的事情了,包括加载网络上的图片、加载手机本地的图片、加载应用资源中的图片等等。

下面咱们就来详细解析一下这行代码。

首先,调用 Glide.with() 方法用于建立一个加载图片的实例。with() 方法能够接收 Context、Activity 或者 Fragment 类型的参数。也就是说咱们选择的范围很是广,无论是在 Activity 仍是 Fragment 中调用 with() 方法,均可以直接传 this。那若是调用的地方既不在 Activity 中也不在 Fragment 中呢?也不要紧,咱们能够获取当前应用程序的 ApplicationContext,传入到 with() 方法当中。注意 with() 方法中传入的实例会决定 Glide 加载图片的生命周期,若是传入的是 Activity 或者 Fragment 的实例,那么当这个 Activity 或 Fragment 被销毁的时候,图片加载也会中止。若是传入的是 ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会中止。

接下来看一下 load() 方法,这个方法用于指定待加载的图片资源。Glide 支持加载各类各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri 对象等等。所以 load() 方法也有不少个方法重载,除了咱们刚才使用的加载一个字符串网址以外,你还能够这样使用 load() 方法:

File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);


int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);


byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);


Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
复制代码

最后看一下 into() 方法,这个方法就很简单了,咱们但愿让图片显示在哪一个 ImageView 上,把这个 ImageView 的实例传进去就能够了。固然,into() 方法不只仅是只能接收 ImageView 类型的参数,还支持不少更丰富的用法,不过那个属于高级技巧,咱们会在后面的文章当中学习。

那么回顾一下 Glide 最基本的使用方式,其实就是关键的三步走:先 with(),再 load(),最后 into()。熟记这三步,你就已经入门 Glide 了。

如今咱们来学一些 Glide 的扩展内容。其实刚才所学的三步走就是 Glide 最核心的东西,而咱们后面所要学习的全部东西都是在这个三步走的基础上不断进行扩展而已。

观察刚才加载网络图片的效果,你会发现,点击了 Load Image 按钮以后,要稍微等一会图片才会显示出来。这其实很容易理解,由于从网络上下载图片原本就是须要时间的。那么咱们有没有办法再优化一下用户体验呢?固然能够,Glide 提供了各类各样很是丰富的 API 支持,其中就包括了占位图功能。

顾名思义,占位图就是指在图片的加载过程当中,咱们先显示一张临时的图片,等图片加载出来了再替换成要加载的图片。

下面咱们就来学习一下 Glide 占位图功能的使用方法,首先我事先准备好了一张 loading.jpg 图片,用来做为占位图显示。而后修改 Glide 加载部分的代码,以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .into(imageView);
复制代码

没错,就是这么简单。咱们只是在刚才的三步走之间插入了一个 placeholder() 方法,而后将占位图片的资源 id 传入到这个方法中便可。另外,这个占位图的用法其实也演示了 Glide 当中绝大多数 API 的用法,其实就是在 load() 和 into() 方法之间串接任意想添加的功能就能够了。

不过若是你如今从新运行一下代码并点击 Load Image,极可能是根本看不到占位图效果的。由于 Glide 有很是强大的缓存机制,咱们刚才加载那张必应美图的时候 Glide 自动就已经将它缓存下来了,下次加载的时候将会直接从缓存中读取,不会再去网络下载了,于是加载的速度很是快,因此占位图可能根原本不及显示。

所以这里咱们还须要稍微作一点修改,来让占位图能有机会显示出来,修改代码以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
复制代码

能够看到,这里串接了一个 diskCacheStrategy() 方法,并传入 DiskCacheStrategy.NONE 参数,这样就能够禁用掉 Glide 的缓存功能。

关于 Glide 缓存方面的内容咱们将会在后面的文章进行详细的讲解,这里只是为了测试占位图功能而加的一个额外配置,暂时你只须要知道禁用缓存必须这么写就能够了。

如今从新运行一下代码,效果以下图所示:

能够看到,当点击 Load Image 按钮以后会当即显示一张占位图,而后等真正的图片加载完成以后会将占位图替换掉。

固然,这只是占位图的一种,除了这种加载占位图以外,还有一种异常占位图。异常占位图就是指,若是由于某些异常状况致使图片加载失败,好比说手机网络信号很差,这个时候就显示这张异常占位图。

异常占位图的用法相信你已经能够猜到了,首先准备一张 error.jpg 图片,而后修改 Glide 加载部分的代码,以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
复制代码

很简单,这里又串接了一个 error() 方法就能够指定异常占位图了。

如今你能够将图片的 url 地址修改为一个不存在的图片地址,或者干脆直接将手机的网络给关了,而后从新运行程序,效果以下图所示:

这样咱们就把 Glide 提供的占位图功能都掌握了。

咱们还须要再了解一下 Glide 另一个强大的功能,那就是 Glide 是支持加载 GIF 图片的。这一点确实很是牛逼,由于相比之下 Jake Warton 曾经明确表示过,Picasso 是不会支持加载 GIF 图片的。

而使用 Glide 加载 GIF 图并不须要编写什么额外的代码,Glide 内部会自动判断图片格式。好比这是一张 GIF 图片的 URL 地址:

http://p1.pstatp.com/large/166200019850062839d3
复制代码

咱们只须要将刚才那段加载图片代码中的 URL 地址替换成上面的地址就能够了,如今从新运行一下代码,效果以下图所示:

也就是说,无论咱们传入的是一张普通图片,仍是一张 GIF 图片,Glide 都会自动进行判断,而且能够正确地把它解析并展现出来。

可是若是我想指定图片的格式该怎么办呢?就好比说,我但愿加载的这张图必须是一张静态图片,我不须要 Glide 自动帮我判断它究竟是静图仍是 GIF 图。

想实现这个功能仍然很是简单,咱们只须要再串接一个新的方法就能够了,以下所示:

Glide.with(this)
     .load(url)
     .asBitmap()
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
复制代码

能够看到,这里在 load() 方法的后面加入了一个 asBitmap() 方法,这个方法的意思就是说这里只容许加载静态图片,不须要 Glide 去帮咱们自动进行图片格式的判断了。

如今从新运行一下程序,效果以下图所示:

因为调用了 asBitmap() 方法,如今 GIF 图就没法正常播放了,而是会在界面上显示第一帧的图片。

那么相似地,既然咱们能强制指定加载静态图片,就也能强制指定加载动态图片。好比说咱们想要实现必须加载动态图片的功能,就能够这样写:

Glide.with(this)
     .load(url)
     .asGif()
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);
复制代码

这里调用了 asGif() 方法替代了 asBitmap() 方法,很好理解,相信不用我多作什么解释了。

那么既然指定了只容许加载动态图片,若是咱们传入了一张静态图片的 URL 地址又会怎么样呢?试一下就知道了,将图片的 URL 地址改为刚才的必应美图,而后从新运行代码,效果以下图所示。

没错,若是指定了只能加载动态图片,而传入的图片倒是一张静图的话,那么结果天然就只有加载失败喽。

实际上,使用 Glide 在绝大多数状况下咱们都是不须要指定图片大小的。

在学习本节内容以前,你可能还须要先了解一个概念,就是咱们平时在加载图片的时候很容易会形成内存浪费。什么叫内存浪费呢?好比说一张图片的尺寸是 10001000 像素,可是咱们界面上的 ImageView 可能只有 200200 像素,这个时候若是你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,由于程序中根本就用不到这么高像素的图片。

关于图片压缩这方面,我以前也翻译过 Android 官方的一篇文章,感兴趣的朋友能够去阅读一下 Android 高效加载大图、多图解决方案,有效避免程序 OOM

而使用 Glide,咱们就彻底不用担忧图片内存浪费,甚至是内存溢出的问题。由于 Glide 历来都不会直接将图片的完整尺寸所有加载到内存中,而是用多少加载多少。Glide 会自动判断 ImageView 的大小,而后只将这么大的图片像素加载到内存当中,帮助咱们节省内存开支。

固然,Glide 也并无使用什么神奇的魔法,它内部的实现原理其实就是上面那篇文章当中介绍的技术,所以掌握了最基本的实现原理,你也能够本身实现一套这样的图片压缩机制。

也正是由于 Glide 是如此的智能,因此刚才在开始的时候我就说了,在绝大多数状况下咱们都是不须要指定图片大小的,由于 Glide 会自动根据 ImageView 的大小来决定图片的大小。

不过,若是你真的有这样的需求,必须给图片指定一个固定的大小,Glide 仍然是支持这个功能的。修改 Glide 加载部分的代码,以下所示:

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .override(100, 100)
     .into(imageView);
复制代码

仍然很是简单,这里使用 override() 方法指定了一个图片的尺寸,也就是说,Glide 如今只会将图片加载成 100*100 像素的尺寸,而不会管你的 ImageView 的大小是多少了。

好了,今天是咱们这个 Glide 系列的第一篇文章,写了这么多内容已经算是挺不错的了。如今你已经了解了 Glide 的基本用法,固然也是一些最经常使用的用法。下一篇文章当中,咱们会尝试去分析 Glide 的源码,研究一下在这些基本用法的背后,Glide 到底执行了什么神奇的操做,可以使得咱们加载图片变得这么简单?感兴趣的朋友请继续阅读 Android 图片加载框架最全解析(二),从源码的角度理解 Glide 的执行流程

相关文章
相关标签/搜索