Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战

2023-05-25,,

http://blog.csdn.net/sk719887916/article/details/40073747 请尊重原创 : skay

《Android 图片加载库Glide 实战(一),初始,加载进阶到实践 》

中我们了解了giide的集成,简单图片加载,本篇将介绍他的高级用法,

Glide 的流式接口让这个变得非常容易的去做到!只需要调用 .placeHolder() 用一个 drawable(resource) 引用,Glide 将会显示它作为一个占位符,直到你的实际图片准备好。

Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .into(imageViewPlaceholder);

做为一个显而易见的原因,你不能设置一个网络 url 作为占位符,因为这也会被去请求加载的。App 资源和 drawable 能保证可用和可访问的。然而,作为 load() 方法的参数,Glide 接受所有值。这可能不是可加载的(没有网络连接,服务器宕机…),删除了或者不能访问。在下一节中,我们将讨论一个错误的占位符。

错误占位符:.error()

假设我们的 App 尝试从一个网站去加载一张图片。Glide 给我们一个选项去获取一个错误的回调并采取合适的行动。我们会在后面来讨论,对现在来说,可能太复杂了。在大多数情况下使用占位符,来指明图片不能被加载已经足够了。

调用 Glide 的流式接口和之前显示预加载占位符的例子是相同的,不同的是调用了名为 error() 的函数。

 Glide
    .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);

就这样。如果你定义的 load() 值的图片不能被加载出来,Glide 会显示 R.mipmap.future_studio_launcher 作为替换。再说一次,error()接受的参数只能是已经初始化的 drawable 对象或者指明它的资源(R.drawable.<drawable-keyword>)。

 使用 crossFade() 
无论你是在加载图片之前是否显示一个占位符,改变 ImageView 的图片在你的 UI 中有非常显著的变化。一个简单的选项是让它改变是更加平滑和养眼的,就是使用一个淡入淡出动画。Glide 使用标准的淡入淡出动画,这是(对于当前版本3.6.1)默认激活的。如果你想要如强制 Glide 显示一个淡入淡出动画,你必须调用另外一个建造者:

    Glide
    .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
    .crossFade()
    .into(imageViewFade);

crossFade() 方法还有另外重载方法 .crossFade(int duration)。如果你想要去减慢(或加快)动画,随时可以传一个毫秒的时间给这个方法。动画默认的持续时间是 300毫秒。

使用 dontAnimate()
如果你想直接显示图片而没有任何淡入淡出效果,在 Glide 的建造者中调用 .dontAnimate() 。

  Glide
    .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
    .dontAnimate()
    .into(imageViewFade);

这是直接显示你的图片,而不是淡入显示到 ImageView。请确保你有更好的理由来做这件事情。

需要知道的是所有这些参数都是独立的,而不需要彼此依赖的。比如,你可以设定 .error() 而不调用 .placeholder()。你可能设置 crossFade() 动画而没有占位符。任何参数的组合都是可能的。

缩放图像 

**调整大小` resize(x,y)` **

lide 有更加高效的内存管理。Glide 自动限制了图片的尺寸在缓存和内存中,并给到 ImageView 需要的尺寸。Picasso 也有这样的能力,但需要调用 fit() 方法。对于 Glide,如果图片不会自动适配到 ImageView,调用 override(horizontalSize, verticalSize) 。这将在图片显示到 ImageView之前重新改变图片大小。

    Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .override(600, 200) // resizes the image to these dimensions (in pixel). does not respect aspect ratio
    .into(imageViewResize);

当你还没有目标 view 去知道尺寸的时候,这个选项也可能是有用的。比如,如果 App 想要在闪屏界面预热缓存,它还不能测量 ImageView 的尺寸。然而,如果你知道这个图片多少大,用 override 去提供明确的尺寸。

**缩放图像**

现在,对于任何图像操作,调整大小真的能让长宽比失真并且丑化图像显示。在你大多数的使用场景中,你想要避免发生这种情况。Glide 提供了一般变化去处理图像显示。提供了两个标准选项:centerCrop 和 fitCenter。

CenterCrop
CenterCrop()是一个裁剪技术,即缩放图像让它填充到 ImageView 界限内并且裁剪额外的部分。ImageView 可能会完全填充,但图像可能不会完整显示。

  Glide
    .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);

**裁剪图片**


fitCenter() 是裁剪技术,即缩放图像让图像都测量出来等于或小于 ImageView 的边界范围。该图像将会完全显示,但可能不会填满整个 ImageView。

    Glide

Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .override(600, 200)
    .fitCenter()
    .into(imageViewResizeFitCenter);

.with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .override(600, 200)
    .fitCenter() 
    .into(imageViewResizeFitCenter);

我们会在以后的博客去做自定义的转换,以及 centerCrop() 和 fitCenter() 。

显示 Gif和视频

显示 Gif 
有很多图片加载库来去加载和显示图片。能支持 Gif 有一些特别也是非常有帮助的,如果在你的 App 需要的话。Glide 实现 Gif 是如此的特别和令人惊讶,因为它是如此的简单。如果你想显示一个 Gif,你可以只使用和过去相同的调用方式就可以了:

    

String gifUrl = "http://i.kinja-img.com/gawker-media/image/upload/s--B7tUiM5l--/gf2r69yorbdesguga10i.gif";
    Glide
    .with( context )
    .load( gifUrl )
    .into( imageViewGif );

就这样!这将在 ImageView 中显示 Gif 并自动开始播放它。另外一个关于 Glide 的伟大的事情是你仍然可以使用你的标准去调用处理这个 Gif:

    Glide
    .with( context )
    .load( gifUrl )
    .placeholder( R.drawable.cupcake )
    .error( R.drawable.full_cake )
    .into( imageViewGif );

Gif 检查 
上面的代码有一个潜在的问题是,如果提供的来源不是一个 Gif,可能只是一个常规图片,这就没有办法显示这个问题。Glide 接受 Gif 或者图片作为 load() 参数。如果你期望这个 URL 是一个 Gif,Glide 不会自动检查是否是 Gif。因此他们引入了一个额外的防区强制 Glide变成一个 Gif asGif():

Glide
    .with( context )
    .load( gifUrl )
    .asGif()
    .error( R.drawable.full_cake )
    .into( imageViewGif );

如果 gifUrl 是一个 git,这没什么变化。然而,不像之前那样,如果这个 gifUrl 不是一个 Gif,Glide 将会把这个 load 当成失败处理。这样做的的好处是,.error() 回调被调用并且错误占位符被显示,即使 gifUrl 是一个完美的图片(但不是一个 Gif)。

Gif 转为 Bitmap 
如果你的 App 显示一个位置的网络 URL 列表,它可能遇到常规的图片或者 Gif。在某些情况下,你可能对不想系那是整个 Gif。如果你仅仅想要显示 Gif 的第一帧,你可以调用 asBitmap() 去保证其作为一个常规的图片显示,即使这个 URL 是一个 Gif。

  Glide
    .with( context )
    .load( gifUrl )
    .asBitmap()
    .into( imageViewGifAsBitmap );

这让你用 Glide 显示所有已知的 url 显示为图片的形式。这很简单,试一试!

显示本地视频 
现在来谈谈视频。Glide 还能显示视频!只要他们是存储在手机上的。让我们假设你通过让用户选择一个视频后得到了一个文件路径:

 String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
    Glide
    .with( context )
    .load( Uri.fromFile( new File( filePath ) ) )
    .into( imageViewGifAsBitmap );

这里需要注意的是,这仅仅对于本地视频起作用。如果没有存储在该设备上的视频(如一个网络 URL 的视频),它是不工作的!如果你想显示视频从网络 URL,去看看 VideoView。

缓存 

内存缓存 
让我们想象一个非常简单的请求,从网络中加载图片到 ImageView。

Glide
    .with( context )
    .load( eatFoodyImages[0] )
    .skipMemoryCache( true )
    .into( imageViewInternet );

你已经注意到,我们调用了 .skipMemoryCache(true) 去明确告诉 Glide 跳过内存缓存。这意味着 Glide 将不会把这张图片放到内存缓存中去。这里需要明白的是,这只是会影响内存缓存!Glide 将会仍然利用磁盘缓存来避免重复的网络请求。

这也容易知道 Glide 将会默认将所有的图片资源放到内存缓存中去。因为,指明调用 .skipMemoryCache(false) 是没有必要的。

提示:注意一个事实,对于相同的 URL ,如果你的初始请求没调用 .skipMemoryCache(true) 方法,你后来又调用了 .skipMemoryCache(true) 这个方法,这个资源将会在内存中获取缓存。当你想要去调整缓存行为时,确保对同一个资源调用的一致性。

跳过磁盘缓存 

正如你上面这部分所了解到的,即使你关闭内存缓存,请求图片将会仍然被存储在设备的磁盘缓存中。如果你有一张图片具有相同的 URL,但是变化很快,你可能想要连磁盘缓存也一起禁用。

你可以用 .diskCacheStrategy() 方法为 Glide 改变磁盘缓存的行为。不同的于 .skipMemoryCache() 方法,它需要一个枚举而不是一个简答的布尔值。如果你想要为一个请求禁用磁盘缓存。使用枚举 DiskCacheStrategy.NONE 作为参数。

Glide
    .with( context )
    .load( eatFoodyImages[0] )
    .diskCacheStrategy( DiskCacheStrategy.NONE )
    .into( imageViewInternet );

图片在这段代码片段中将不会被保存在磁盘缓存中。然而,默认的它将仍然使用内存缓存!为了把这里两者都禁用掉,两个方法一起调用:

 Glide
    .with( context )
    .load( eatFoodyImages[0] )
    .diskCacheStrategy( DiskCacheStrategy.NONE )
    .skipMemoryCache( true )
    .into( imageViewInternet );

自定义缓存 
正如我们之前提到的,Glide 有多个选项去配置磁盘缓存行为。在我们向你展示这些选项之前,你必须了解到 Glide 的磁盘缓存是相当复杂的。比如,Picasso 仅仅缓存了全尺寸的图像。然而 Glide 缓存了原始图像,全分辨率图像和另外小版本的图像。比如,如果你请求的一个图像是 1000x1000 像素的,但你的 ImageView 是 500x500 像素的,Glide 将会把这两个尺寸都进行缓存。

现在你将会理解对于 `.diskCacheStrategy()` 方法来说不同的枚举参数的意义:

DiskCacheStrategy.NONE 什么都不缓存,就像刚讨论的那样
DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像。在我们上面的例子中,将会只有一个 1000x1000 像素的图片
DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即,降低分辨率后的(或者是转换后的)
DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)

作为最后一个例子,如果你有一张图片,你知道你将会经常操作处理,并做了一堆不同的版本,对其有意义的仅仅是缓存原始分辨率图片。因此,我们用 DiskCacheStrategy.SOURCE 去告诉 Glide 仅仅保存原始图片:

 Glide
    .with( context )
    .load( eatFoodyImages[2] )
    .diskCacheStrategy( DiskCacheStrategy.SOURCE )
    .into( imageViewFile );

图片请求的优先级 

lide 可以用 Priority 枚举来支持你这样的行为,调用 .priority() 方法。

但在看这个方法调用的示例代码之前,让么我看看 priority 的枚举值,它首先作为 .priority() 方法的参数的。

了解 Priority (优先级)枚举
这个枚举给了四个不同的选项,下面是按照递增priority(优先级)的列表:

- Priority.LOW
- Priority.NORMAL
- Priority.HIGH
- Priority.IMMEDIATE

Glide
    .with( context )
    .load( UsageExampleListViewAdapter.eatFoodyImages[2] )
    .priority( Priority.LOW )
    .into( imageViewLowPrioRight );

简单的缩略图 

Glide 为缩略图提供2个不同的方式。第一个是简单的选择,在原始图像被用过之后,这只需要一个较小的分辨率。这个方法在 ListView的组合和详细视图中是非常有用的。如果你已经在 ListView 中显示了图像。这么说吧,在250x250 像素的中,图像将在详细视图中需要一个更大的分辨率图像。然而,从用户的角度来看,他已经看到较小版本的图像,为什么在详情页中出现一个占位符显示了几秒,然后相同图像又再次一次显示(高分辨率的)?

在这种情况下,它有更好的意义去继续显示这张 250x250 像素版本的图像在详情视图上,并且后台去加载全分辨率的图像。Glide 的 .thumbnail() 方法让这一切成为可能。 在这样的情况下,这个参数是一个 float 作为其大小的倍数。

    Glide  
    .with( context )
    .load( UsageExampleGifAndVideos.gifUrl )
    .thumbnail( 0.1f )

 Transformations 

在图片被显示之前,transformations(转换) 可以被用于图像的操作处理。比如,如果你的应用需要显示一个灰色的图像,但是我们只能访问到原始色彩的版本,你可以用 transformation 去操作 bitmap,从而将一个明亮色彩版本的图片转换成灰暗的版本。不要理解错啦,transformation 不仅限于颜色转换。你可以图片的任意属性:尺寸,范围,颜色,像素位置等等!Glide 已经包含了2个 transformation,我们之前已经看了图像重设大小,即:fitCenter 和 centerCrop。这两个选项都非常有意义,他们在 Glide 中拥有自己的实现。当然,我们这篇博客不再介绍他们。
    
    Glide  
    .with( context )
    .load( eatFoodyImages[0] )
    .transform( new BlurTransformation( context ) )
    //.bitmapTransform( new BlurTransformation( context ) ) // this would work too!
    .into( imageView1 );

Glide Modules 

Glide module 是一个抽象方法,全局改变 Glide 行为的一个方式。如果你需要访问 GlideBuilder,它要在你要做的地方创建 Glide 实例,这是要做的一种方法。为了定制 Glide,你需要去实现一个 GlideModule 接口的公共类。

    public class SimpleGlideModule implements GlideModule {  
    @Override public void applyOptions(Context context, GlideBuilder builder) {
    // todo
    }
    
    @Override public void registerComponents(Context context, Glide glide) {
    // todo
    }
    }

该接口提供了两种发发来调整 Glide 不同的组件。在这篇博客中,我们主要看第一个方法 applyOptions(Context context, GlideBuilder builder)。

所以你知道要创建一个额外的类去定制 Glide。下一步是要全局的去声明这个类,让 Glide 知道它应该在哪里被加载和使用。Glide 会扫描 AndroidManifest.xml 为 Glide module 的 meta 声明。因此,你必须在 AndroidManifest.xml 的 <application> 标签内去声明这个刚刚创建的 Glide module。

    <manifest

    ...

    <application>

    <meta-data
    android:name="io.futurestud.tutorials.glide.glidemodule.SimpleGlideModule"
    android:value="GlideModule" />

    ...

    </application>
</manifest>  

请确保你将 android:name 属性改成你的包名+类名的形式,这样的引用才是正确的。就这样,你不需要去添加其他任何代码。如果你想删掉 Glide Module,只需要把它从 AndroidManifest.xml 中移除就可以了。Java 类可以保存,说不定以后会用呢。如果它没有在 AndroidManifest.xml 中被引用,那它不会被加载或被使用。

你去定制 module 的话 Glide 会有这样一个优点:你可以同时声明多个 Glide module。Glide 将会(没有特定顺序)得到所有的声明 module。因为你当前不能定义顺序,请确保定制不会引起冲突!

GlideBuilder 
好了,你知道如何用 Glide module 定制 Glide 了。现在来看看接口的第一个方法:applyOptions(Context context, GlideBuilder builder)。该方法给你了一个 GlideBuilder 对象作为变量。这个方法是一个 void 的返回类型,所以你可以在这个方法里去调 GlideBuilder 中可用的方法。

    .setMemoryCache(MemoryCache memoryCache)
    .setBitmapPool(BitmapPool bitmapPool)
    .setDiskCache(DiskCache.Factory diskCacheFactory)
    .setDiskCacheService(ExecutorService service)
    .setResizeService(ExecutorService service)
    .setDecodeFormat(DecodeFormat decodeFormat)
你可以看到,这个 GlideBuilder 对象给你访问了 Glide 重要的核心组件。在这个博客中使用的方法,你可以改变磁盘缓存,内存缓存等等!

我们稍后会看到更多进阶的组件,但是现在我们先挑一个相对加单的改变:.setDecodeFormat(DecodeFormat decodeFormat)。

使用实例:增加 Glide 的图片质量
在 Android 中有两个主要的方法对图片进行解码:ARGB8888 和 RGB565。前者为每个像素使用了 4 个字节,后者仅为每个像素使用了 2 个字节。ARGB8888 的优势是图像质量更高以及能存储一个 alpha 通道。Picasso 使用 ARGB8888,Glide 默认使用低质量的 RGB565。对于 Glide 使用者来说:你使用 Glide module 方法去改变解码规则。

你只需要实现一个 GlideModule,像我们上面给你显示的那样,然后使用正确的枚举值调用 builder.setDecodeFormat`(DecodeFormat.PREFER_ARGB_8888)。`

    public class SimpleGlideModule implements GlideModule {  
    @Override public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }
    
    @Override public void registerComponents(Context context, Glide glide) {
    // nothing to do here
    }
    }

如果你是正确的按照我们的步骤来的话,Glide 现在回用更高质量的图片解码。这种改变方式也遵循 Glide 的行为,适用于其他的模式。代码在 registerComponents() 会看起来有一点不同,我们很快会看到的。

这篇章节中,你已经学会了 Glide module 的基础。你应该能感觉到他们是有用的,以及你知道怎么实现它们了。如果你仍然需要更多的信息,看看下面的资源链接。特别是如果你需要更多明确的在项目库中如何去使用 Glide module 以及使用 ProGuard,避免混淆的情况,请去看看这些内容。

因为这是一个相当复杂的话题,我们将在下周通过一个实际的例子去实践:如何用一个 GlideModule 的 registerComponents() 方法去从服务端使用 self-signed HTTPS 认证(这不是默认实现的)的方式去接收图片。

 自签名SSL 

用 GlideModule 修改 Glide
在继续阅读前,请确保你已经阅读并理解了之前的博客 关于 GlideModule 的。我们不会在这个博客中继续说它的基础知识。相反,我们要跳过这个问题。所以确保你已经更新了你的 GlideModule 的基础知识。

你已经知道 GlideModule 提供给你两个方法去改变行为。上周,我们看了第一个方法 applyOptions()。这周我们会用另外一个方法 registerComponents(),去设置不同的网络库。默认情况下,Glide 内部使用了标准的 HTTPURLConnection 去下载图片。Glide 也提供了两个集合库。这三个都一个非常杨格的安全设置,这很好。唯一的缺点可能是当你的图片从服务端获取时,是使用 HTTPS,且是自签名的(self-signed)。这时 Glide 不会下载或显示图片了,因为自签名的证书被认为是一个安全的问题。

不安全的 OKHttpClient 
因此,你需要去实现自己的网络栈,它接受自签名证书。幸运的是,我们之前已经实现了一个“不安全” 的 OKHttpClient。我们主要复制粘贴这个类。因为它给了我们一个常规的 OkHttpClient,我们这样子来集成:

public class UnsafeOkHttpClient {  
    public static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient okHttpClient = new OkHttpClient();
            okHttpClient.setSslSocketFactory(sslSocketFactory);
            okHttpClient.setProtocols(Arrays.asList(Protocol.HTTP_1_1));
            okHttpClient.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

创建 OkHttpClient 禁用掉所有的 SSL 证书检查。

整合到 Glide
我们的优势是,OkHttp 整合库为 Glide 做了几乎相同的事情,所以我们可以跟着他们走。首先,我们需要在 GlideModule 中声明我们的定制。正如你所期待的,我们要在 registerComponents() 方法中去做适配。我们可以调用 .register() 方法去改变 Glide 的基本部件。Glide 使用一个 GlideLoader 去链接数据模型到一个具体的数据类型。在我们的实例中,我们要去创建一个 ModeLoader,连接传入的 URL,通过 GlideUrl 类来代表一个 InputStream。Glide 要能创建一个我们的新的 ModeLoader,所以我们要在 .register() 方法中传递一个工厂。

public class UnsafeOkHttpGlideModule implements GlideModule {  
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {

        }

        @Override
        public void registerComponents(Context context, Glide glide) {
            glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
        }
    }

前两个参数是 model 类,和连接的资源类。最后一个参数是 ModelLoaderFactory。因此,我们不能直接设置一个 UnsafeOkHttpClient 实例,我们需要去创建一个 ModelLoaderFactory,它用 UnsafeOkHttpClient 来提供了一个 URL 和输入流之前的连接。

**再说一次,在 OkHttp 整合库 中给了我们一个很好的模板:**

public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    /**
     * The default factory for {@link OkHttpUrlLoader}s.
     */
    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private static volatile OkHttpClient internalClient;
        private OkHttpClient client;

        private static OkHttpClient getInternalClient() {
            if (internalClient == null) {
                synchronized (Factory.class) {
                    if (internalClient == null) {
                        internalClient = UnsafeOkHttpClient.getUnsafeOkHttpClient();
                    }
                }
            }
            return internalClient;
        }

        /**
         * Constructor for a new Factory that runs requests using a static singleton client.
         */
        public Factory() {
            this(getInternalClient());
        }

        /**
         * Constructor for a new Factory that runs requests using given client.
         */
        public Factory(OkHttpClient client) {
            this.client = client;
        }

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new OkHttpUrlLoader(client);
        }

        @Override
        public void teardown() {
            // Do nothing, this instance doesn't own the client.
        }
    }

    private final OkHttpClient client;

    public OkHttpUrlLoader(OkHttpClient client) {
        this.client = client;
    }

    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new OkHttpStreamFetcher(client, model);
    }
}

在这个类中,你可以看到 ModelLoaderFactory 的内部构造是怎样的。对我们来说,重要的代码是创建 internalClient 对象:internalClient = UnsafeOkHttpClient.getUnsafeOkHttpClient();。

不幸的是,我们仍然需要用我们的不安全的 OKHttpClient 去连接 URL 激活输入流。因此,我们需要另外一个类去从一个 URL 中拉取返回的输入流:

    public class OkHttpStreamFetcher implements DataFetcher<InputStream> {  
    private final OkHttpClient client;
    private final GlideUrl url;
    private InputStream stream;
    private ResponseBody responseBody;
    
    public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
    this.client = client;
    this.url = url;
    }
    
    @Override
    public InputStream loadData(Priority priority) throws Exception {
    Request.Builder requestBuilder = new Request.Builder()
    .url(url.toStringUrl());
    
    for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
    String key = headerEntry.getKey();
    requestBuilder.addHeader(key, headerEntry.getValue());
    }
    
    Request request = requestBuilder.build();
    
    Response response = client.newCall(request).execute();
    responseBody = response.body();
    if (!response.isSuccessful()) {
    throw new IOException("Request failed with code: " + response.code());
    }
    
    long contentLength = responseBody.contentLength();
    stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
    return stream;
    }
    
    @Override
    public void cleanup() {
    if (stream != null) {
    try {
    stream.close();
    } catch (IOException e) {
    // Ignored
    }
    }
    if (responseBody != null) {
    try {
    responseBody.close();
    } catch (IOException e) {
    // Ignored.
    }
    }
    }
    
    @Override
    public String getId() {
    return url.getCacheKey();
    }
    
    @Override
    public void cancel() {
    // TODO: call cancel on the client when this method is called on a background thread. See #257
    }
    }

不需要知道在这个类中所有的细节。然而,你应该对于这个系统有一个大概的理解,Glide 能去替换内部的工厂组件。
 动态使用 Model Loader

自定义图像大小
提示:如果你还没读过我们之前的博客,现在去读一下。否则,下面这部分看起来很困难。

作为一个简短的回顾:通常 Glide 的请求是和 GlideUrl 类来使用的。上周我们已经向你展示了如何创建一个新的接口,来考虑增加宽度和高度。

public interface CustomImageSizeModel {  
String requestCustomSizeUrl(int width, int height);
}
我们创建了一个实现,它及案例额传递了图像的 URL 加上尺寸提交给了工作服务器。

    public static class CustomImageSizeModelFutureStudio implements CustomImageSizeModel {
    
    String baseImageUrl;
    
    public CustomImageSizeModelFutureStudio(String baseImageUrl) {
    this.baseImageUrl = baseImageUrl;
    }
    
    @Override
    public String requestCustomSizeUrl(int width, int height) {
    return baseImageUrl + "?w=" + width + "&h=" + height;
    }
    }
    最后,这并不是最重要的,我们必须创建一个 CustomImageSizeUrlLoader,它传了宽度和高度给了我们的 model 实现:
    
    public static class CustomImageSizeUrlLoader extends BaseGlideUrlLoader<CustomImageSizeModel> {  
    public CustomImageSizeUrlLoader(Context context) {
    super( context );
    }
    
    @Override
    protected String getUrl(CustomImageSizeModel model, int width, int height) {
    return model.requestCustomSizeUrl( width, height );
    }
    }
Model Loader 和 .using() 的动态使用
目前我们已经声明了 Glide module。Glide 会把它用在每一个请求。如果你不想这样,从 AndroidManifest.xml 中删除你的 Glide module。我们可以这么做是因为 Glide 提供了 .using() 方法去为单个的请求指定一个 model。

    String baseImageUrl = "https://futurestud.io/images/example.png";  
    CustomImageSizeModel customImageRequest = new CustomImageSizeModelFutureStudio( baseImageUrl );
    
    Glide  
    .with( context )
    .using( new CustomImageSizeUrlLoader( context ) )
    .load( customImageRequest )
    .into( imageView1 );

正如你看到的,我们正在创建一个 CustomImageSizeModelFutureStudio 对象来为我们的图像按照指定的大小加载。因为没有在 Glide module 中声明 CustomImageSizeModel 接口,我们必须指明这行代码 .using(new CustomImageSizeUrlLoader(context))。Glide 现在会只为这个请求用这个 model。对于其他的请求,即使它们有 CustomImageSizeModel 接口,也不会受影响。

总结

开始我们的 Glide 图片加载库系列以来已经过去相当长的时间了。随着时间的推移,这个短系列变得越来越长。我们很感谢你的持续关注和反馈。我们希望你能像我们一样学到很多。让我们回顾一下我们所涵盖的主题。我们强烈推荐你花上一分钟时间通过这个列表来确认你都从这个 Glide 系列中学到了些什么。

图像基础,用 Glide 加载 Gif 和 Video 
我们从简单介绍和演示了 Glide 加载图片,Gif 和本地视频的方式。这部分适用于每个刚开始用 Glide 的人。

开始! 原文:Getting Started & Simple Loading
加载进阶 原文:Advanced Loading
显示 Gif 和 Video 原文:Displaying Gifs & Videos

图像显示和占位符 
接下来,我们看了如何让 Glide 用在 ListView 或 GrideView 的适配器中。我们也向你展现了 Glide 的占位符实现和渐现动画。

- ListAdapter(ListView, GridView) 原文:ListAdapter (ListView, GridView)
- 占位符 和 渐现动画 原文:Placeholders & Fade Animations

图片大小重设和缩略图 
后来学了如何加载和显示图片,我们转义到了基本的图片处理。首先我们已经介绍了你对 Glide 的可能的选项来改变图像的大小和缩放。我也展示了你可以请求和利用缩略图。

图片重设大小 和 缩放 原文:Image Resizing & Scaling
缩略图 原文:Thumbnails

缓存和请求优先级 
Glide 就像任何 Android 中的图片加载库,它的缓存组件部分决定了这个库是活的还是死的。在缓存基础的文章中,我们已经呈现了 Glide 方法的构建预览。此外,我们展示了如何处理单个单个请求的缓存行为。在下面的博客中,我们呈现了你该如何去对请求做优先级排序,以及确保重要图片首先被加载和显示。

缓存基础 原文:Caching Basics
请求优先级 原文:Request Priorities

Glide Target 的回调 
在接下来的两篇博客中,我们一直假定你是加载图片到标准的 ImageView 中。在这两篇中,我们可以选择如何使用 Glide 去异步加载图片到不同的 target 中。如果你需要去加载图片到自定义的视图中,通知或应用小部件,就这些啦:

回调:SimpleTarget 和 ViewTarget 用于自定义视图类 原文:Callbacks: SimpleTarget and ViewTarget for Custom View Classes
加载图片到通知栏和应用小部件中 原文:Loading Images into Notifications and RemoteViews

异常 和 调试
当创建了一个新应用,在开发过程中不是所有的事情都是正常工作的。重要的是当你不知道一些东西为什么不工作的时候有什么样的方法可以处理。这就是为什么我们介绍了一篇特别的文章就是为了调试和错误处理。这可能听起来不是很有吸引力,但重要的是让你知道在未来碰到此类问题的时候,你可以如何处理:

异常:调试和错误处理 原文:Exceptions: Debugging and Error Handling
Glide 转换 
在向你展示了所有 Glide 基础之后,我们开始了更多的自定义功能。如果你需要在显示图片之前做处理,自定义转换的博客写给你看了:

自定义转换 原文:Custom Transformation
如何旋转图像 原文:How to Rotate Images
Glide 动画 
Glide 不仅可以转换动画,它还能控制图片的显示。如果你想要给你的图片增加一个 eye-popping 动画,可以读读这篇博客:

用 animate() 自定义动画 原文:Custom Animations with animate()

Glide Module
我们最后一个主题是 Glide module。Glide module 给了一个抽象的方式来自定义每个组件和 Glide 的行为。如果你正式在生产应用中使用 Glide 的话,确保你都看过这些了。这可能和一块宝石一样重要:

集成网络栈 原文:Integrating Network Stacks
用 Module 自定义 Glide 原文:Customize Glide with Modules
Module 实例:接受自签名证书的 HTTPS 原文:Glide Module Example: Self-Signed HTTPS Network Stack
Module 实例:自定义缓存 原文:Glide Module Example: Customize Caching
Module 实例:用自定义尺寸优化加载的图片 原文:Glide Module Example: Optimizing By Loading Images In Custom Sizes
动态使用 Model Loader 原文:Dynamically Use Model Loaders

原文: https://mrfu.me/2016/02/28/Glide_Series_Roundup/

Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战的相关教程结束。

《Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战.doc》

下载本文的Word格式文档,以方便收藏与打印。