text 滑翔滑翔Android(视频,图片)加载和缓存类库https://muyangmin.github.io/glide-docs-cn/
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了text 滑翔滑翔Android(视频,图片)加载和缓存类库https://muyangmin.github.io/glide-docs-cn/相关的知识,希望对你有一定的参考价值。
Android(视频、图片)加载和缓存类库Glide
2015年05月14日 17:36:01 迦南之地 阅读数:3842
https://muyangmin.github.io/glide-docs-cn/ Glide v4 快速高效的Android图片加载库
转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103),谢谢支持!
前言
在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。
它的成功让我非常感兴趣。我花了一整晚的时间把玩,决定分享一些自己的经验。在开始之前我想说,Glide和Picasso有90%的相似度,准确的说,就是Picasso的克隆版本。但是在细节上还是有不少区别的。
导入库
Picasso和Glide都在jcenter上。在项目中添加依赖非常简单:
Picasso
1
2
3
dependencies {
compile 'com.squareup.picasso:picasso:2.5.1'
}
Glide
1
2
3
4
dependencies {
compile 'com.github.bumptech.glide:glide:3.5.2'
compile 'com.android.support:support-v4:22.0.0'
}
Glide需要依赖Support Library v4,别忘了。其实Support Library v4已经是应用程序的标配了,这不是什么问题。
基础
就如我所说的Glide和Picasso非常相似,Glide加载图片的方法和Picasso如出一辙。
Picasso
1
2
3
Picasso.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ivImg);
Glide
1
2
3
Glide.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ivImg);
虽然两者看起来一样,但是Glide更易用,因为Glide的with方法不光接受Context,还接受Activity 和 Fragment,Context会自动的从他们获取。
with
同 时将Activity/Fragment作为with()参数的好处是:图片加载会和Activity/Fragment的生命周期保持一致,比如 Paused状态在暂停加载,在Resumed的时候又自动重新加载。所以我建议传参的时候传递Activity 和 Fragment给Glide,而不是Context。
默认Bitmap格式是RGB_565
下面是加载图片时和Picasso的比较(1920x1080 像素的图片加载到768x432的ImageView中)
firstload
可以看到Glide加载的图片质量要差于Picasso(ps:我看不出来哈),为什么?这是因为Glide默认的Bitmap格式是RGB_565 ,比ARGB_8888格式的内存开销要小一半。下面是Picasso在ARGB8888下与Glide在RGB565下的内存开销图(应用自身占用了8m,因此以8为基准线比较):
ram1_1
如果你对默认的RGB_565效果还比较满意,可以不做任何事,但是如果你觉得难以接受,可以创建一个新的GlideModule将Bitmap格式转换到ARGB_8888:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class GlideConfiguration implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Apply options to the builder here.
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override
public void registerComponents(Context context, Glide glide) {
// register ModelLoaders here.
}
}
同时在AndroidManifest.xml中将GlideModule定义为meta-data
1
2
<meta-data android:name="com.inthecheesefactory.lab.glidepicasso.GlideConfiguration"
android:value="GlideModule"/>
quality2
这样看起来就会好很多。
我们再来看看内存开销图,这次貌似Glide花费了两倍于上次的内存,但是Picasso的内存开销仍然远大于Glide。
ram2_1
原因在于Picasso是加载了全尺寸的图片到内存,然后让GPU来实时重绘大小。而Glide加载的大小和ImageView的大小是一致的,因此更小。当然,Picasso也可以指定加载的图片大小的:
1
2
3
4
Picasso.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.resize(768, 432)
.into(ivImgPicasso);
但是问题在于你需要主动计算ImageView的大小,或者说你的ImageView大小是具体的值(而不是wrap_content),你也可以这样:
1
2
3
4
5
Picasso.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.fit()
.centerCrop()
.into(ivImgPicasso);
现在Picasso的内存开销就和Glide差不多了。
memory3
虽然内存开销差距不到,但是在这个问题上Glide完胜Picasso。因为Glide可以自动计算出任意情况下的ImageView大小。
Image质量的细节
这是将ImageView还原到真实大小时的比较。
quality3
你可以看到,Glide加载的图片没有Picasso那么平滑,我还没有找到一个可以直观改变图片大小调整算法的方法。
但是这并不算什么坏事,因为很难察觉。
磁盘缓存
Picasso和Glide在磁盘缓存策略上有很大的不同。Picasso缓存的是全尺寸的,而Glide缓存的是跟ImageView尺寸相同的。
cache
上面提到的平滑度的问题依然存在,而且如果加载的是RGB565图片,那么缓存中的图片也是RGB565。
我 尝试将ImageView调整成不同大小,但不管大小如何Picasso只缓存一个全尺寸的。Glide则不同,它会为每种大小的ImageView缓存 一次。尽管一张图片已经缓存了一次,但是假如你要在另外一个地方再次以不同尺寸显示,需要重新下载,调整成新尺寸的大小,然后将这个尺寸的也缓存起来。
具体说来就是:假如在第一个页面有一个200x200的ImageView,在第二个页面有一个100x100的ImageView,这两个ImageView本来是要显示同一张图片,却需要下载两次。
不过,你可以改变这种行为,让Glide既缓存全尺寸又缓存其他尺寸:
1
2
3
4
Glide.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(ivImgGlide);
下次在任何ImageView中加载图片的时候,全尺寸的图片将从缓存中取出,重新调整大小,然后缓存。
Glide的这种方式优点是加载显示非常快。而Picasso的方式则因为需要在显示之前重新调整大小而导致一些延迟,即便你添加了这段代码来让其立即显示:
1
2
//Picasso
.noFade();
loading3
Picasso和Glide各有所长,你根据自己的需求选择合适的。
对我而言,我更喜欢Glide,因为它远比Picasso快,虽然需要更大的空间来缓存。
特性
你可以做到几乎和Picasso一样多的事情,代码也几乎一样。
Image Resizing
1
2
3
4
5
// Picasso
.resize(300, 200);
// Glide
.override(300, 200);
Center Cropping
1
2
3
4
5
// Picasso
.centerCrop();
// Glide
.centerCrop();
Transforming
1
2
3
4
5
// Picasso
.transform(new CircleTransform())
// Glide
.transform(new CircleTransform(context))
设置占位图或者加载错误图:
1
2
3
4
5
6
7
// Picasso
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
// Glide
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
几乎和Picasso一样,从Picasso转换到Glide对你来说就是小菜一碟。
有什么Glide可以做而Picasso 做不到
Glide可以加载GIF动态图,而Picasso不能。
gifanimation2
同时因为Glide和Activity/Fragment的生命周期是一致的,因此gif的动画也会自动的随着Activity/Fragment的状态暂停、重放。Glide 的缓存在gif这里也是一样,调整大小然后缓存。
但是从我的一次测试结果来看Glide 动画会消费太多的内存,因此谨慎使用。
除了gif动画之外,Glide还可以将任何的本地视频解码成一张静态图片。
还有一个特性是你可以配置图片显示的动画,而Picasso只有一种动画:fading in。
最后一个是可以使用thumbnail()产生一个你所加载图片的thumbnail。
其实还有一些特性,不过不是非常重要,比如将图像转换成字节数组等。
配置
有许多可以配置的选项,比如大小,缓存的磁盘位置,最大缓存空间,位图格式等等。可以在这个页面查看这些配置 Configuration 。
库的大小
Picasso (v2.5.1)的大小约118kb,而Glide (v3.5.2)的大小约430kb。
librarysize
Anyway 312KB difference might not be that significant.
不过312kb的差距并不是很重要。
Picasso和Glide的方法个数分别是840和2678个。
methodcount
必须指出,对于DEX文件65535个方法的限制来说,2678是一个相当大的数字了。建议在使用Glide的时候开启ProGuard。
总结
Glide和Picasso都是非常完美的库。Glide加载图像以及磁盘缓存的方式都要优于Picasso,速度更快,并且Glide更有利于减少OutOfMemoryError的发生,GIF动画是Glide的杀手锏。不过Picasso的图片质量更高。你更喜欢哪个呢?
虽然我使用了很长时间的Picasso,但是我得承认现在我更喜欢Glide。我的建议是使用Glide,但是将Bitmap格式换成 ARGB_8888、让Glide缓存同时缓存全尺寸和改变尺寸两种。
补充内容:
Glide的首要目标是尽可能的使任意类型的图片列表快速流畅的滚动,另一个目标是高效的获取、显示远程图片以及调整远程图片的大小。
特性:
1、GIF动画解码:通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片,使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片;
2、本地视频文件解码:通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频文件的加载和展示;
3、支持缩略图:为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图;
4、集成Activity生命周期:当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求;
5、支持转码:Glide的toBytes() 和transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式;
6、支持动画:新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能;
7、支持OkHttp和Volley:默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈;
8、其他功能:如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能。
相关资源
- Glide 3.0: a media management library for Android
- Glide Wiki
- Android Picasso vs Glide
- Android: Image loading libraries Picasso vs Glide
英文原文 Introduction to Glide, Image Loader Library for Android, recommended by Google
Glide填坑指南
一、前言:再优秀的开源库都有坑要填
手上的项目使用的图片加载框架是:Universal-Image-Loader+业务需要定制化的一些代码。Universal-Image-Loader 这个框架是一个非常经典好用的框架,唯一的问题是是作者很久之前就不再更新了。所以综合考虑下,确定使用Glide+封装代替当前的图片加载框架。
二、困惑:
在没有真正使用 Glide 之前,我所看到的文章基本都是赞美这个库的功能强大,加载流畅。然而,当我用上了以后,才发现并不完美。遇到了不少的坑,需要自己填。
2.1 Glide 配合 OKHttp 使用的坑:
需要在Gradle中引入:
compile "com.github.bumptech.glide:glide:3.7.0"
compile "com.github.bumptech.glide:okhttp3-integration:1.4.0@aar"
这里就有一个坑,如果你用到自定义的 GlideModule,这里的可能会失效,被com.github.bumptech.glide:okhttp3-integration:1.4.0@aar默认的替换
解决方法是升级版本号:
compile "com.github.bumptech.glide:okhttp3-integration:1.4.0@aar" -》 compile "com.github.bumptech.glide:okhttp3-integration:1.5.0" 注意,没有@arr
2.2 OKHttpClient 超时设置导致图片无法加载坑:
因为Glide本身只负责图片加载,网络请求图片数据由网络框架决定。网络请求一般会有超时的问题,坑的是OKHttp默认的超时时间太短了,如果不修改,网络状态比较差
就很容易请求超时,图片自然就加载不出来。我设置的参数是60,60,30这个可以自己根据实际情况确定。
//这个是源码里面的,默认超时时间,都是10s,10000ms
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
//手动设置超时时间
OkHttpClient client=new OkHttpClient.Builder()
.connectTimeout(HTTP_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(HTTP_READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(HTTP_WRITE_TIMEOUT, TimeUnit.SECONDS)
.build;
2.3 Glide 查看 log 的坑:
如果你使用 Glide 经常出现图片加载不出来或者加载有问题,你需要查看 Glide 本身的 log,不过这个必须通过 adb 命令开启,详情百度,需要注意如果是请求图片问题,关注请求的 log,图片加载||转换的问题,关注图片加载||转换的log。
2.4 Glide 加载的图片内存占用巨多的坑:
在使用 Glide 的第一个版本,OOM 问题一下子爆炸了,查看内存占用,使用 Universal-Image-Loader 的旧版本,App 占用50m~80m内存,
而使用 Glide 加载列表大图的时候,突然猛增到120M+,低端机器自然很容易就 OOM 了。
Glide 有一个优点被很多人称赞,就是它会根据图片控件的大小对 Bitmap 进程缩放处理,适应控件的大小。
但是,如果是一个控件,在高分屏下,它的控件大小往往比实际图片尺寸大很多,举例一个控件:
长宽:1080400,图片原始尺寸540200,如果不做任何设置,Glide 会把 bitmap 放大到控件大小,那么占用的内存就变成了原始大小四倍。。。
这个是 Glide 的特性,暂时没有找到的方法修改。
临时方案:为了避免Glide自动把bitmap放大,使用在加载图片的时候,使用 .override(width, height) 限制图片的宽高
2.5 Glide 使用过渡动画造成图片变形的 bug
Glide 默认会加载图片的时候会有一个过渡效果,其原理是采用TransitionDrawable实现的。
但是这个和 placeHolder,一起使用,尤其是你的playHolder的尺寸比你加载的图片要大,这个时候就会出现,你加载出来的图片变形的问题。
很多人推荐使用 .dontAnimate() 解决问题,即去掉过渡动画。
但是,如果产品一定需要加入过渡动画,官方其实没有提供完美的解决方案,Glide的作者之一在stackoverflow有回答并且给出了 github 的代码。
http://stackoverflow.com/questions/32235413/glide-load-drawable-but-dont-scale-placeholder
https://github.com/TWiStErRob/glide-support/tree/master/src/glide4/java/com/bumptech/glide/supportapp
注意:我在使用的过程中发现,如果你的ImageView的type是center_crop 的话,那么必须确保你的place_holder默认图片,长宽小于你加载的图片,不然即使使用的了作者的代码,任然会有变形的问题。
如何调试Glide加载图片
96 倾城_之泪
2015.12.01 15:23* 字数 646 阅读 10120评论 2喜欢 9
前言
与其他图片加载库不同,在Glide加载图片的过程中默认是没有任何log输出的。这样使得加载失败的原因难以调试。到底是网络错误还是图片根本就不存在亦或者解码出错,我们不得而知。当然官方也给出了调试的方法,这篇文章就来介绍下如何调试Glide加载图片,内容主要是对官方wiki的翻译。
正文
在Glide加载图片过程中出现异常时,默认是没有log输出的。但是Glide给开发者提供了两种方法来查看或者响应这些异常。
调试
为了在异常发生时可以看到它们,你可以打开Glide中处理所有媒体加载响应的类GenericRequest的log开关。很简单,在命令行运行下面的指令即可:
adb shell setprop log.tag.GenericRequest DEBUG
如果你将DEBUG替换为VERBOSE,还可以看到详细的请求时间日志。
如果你想禁用log输出,执行:
adb shell setprop log.tag.GenericRequest ERROR
调试工作流
Glide workflow.png
为了查看Glide内部引擎是何时、如何加载图片的,你可以启用这些log:
adb shell setprop log.tag.Engine VERBOSE
adb shell setprop log.tag.EngineJob VERBOSE
adb shell setprop log.tag.DecodeJob VERBOSE
启用这些log可以帮助你了解到为什么某些资源没有从内存中加载?为什么从外部url加载要重新下载数据?同时这也有助于了解在磁盘缓存时哪些参数需要配置。启用DecodeJob log还可以帮助你了解自定义变换/解码器/编码器等相关问题。
请求监听器
虽然启动调试日志很简单,但前提是你可以访问到设备。为了完善Glide已存在或更复杂的错误日志系统,你可以使用RequestListener类。当加载请求失败时onException()方法就会被调用,并给出造成失败的异常或者null(在解码器无法从它获取到的数据中解码出任何有用的东西时)。你可以通过listener() API为每一个请求添加一个监听器。
确保onException()方法的返回值为false,避免覆盖Glide默认的错误处理(比如加载失败的错误图片占位)。
这里有一个实现快速调试的列子:
// 示例: .listener(new LoggingListener<String, GlideDrawable>())
public class LoggingListener<T, R> implements RequestListener<T, R> {
@Override public boolean onException(Exception e, Object model, Target target, boolean isFirstResource) {
android.util.Log.d("GLIDE", String.format(Locale.ROOT,
"onException(%s, %s, %s, %s)", e, model, target, isFirstResource), e);
return false;
}
@Override public boolean onResourceReady(Object resource, Object model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
android.util.Log.d("GLIDE", String.format(Locale.ROOT,
"onResourceReady(%s, %s, %s, %s, %s)", resource, model, target, isFromMemoryCache, isFirstResource));
return false;
}
}
确保在发布应用时移除掉所有的调试log!
更多的log指令
这个列表是基于Glide 3.6.0版本的,并不完整。
cd .../android-sdk/platform-tools
adb shell setprop log.tag.AnimatedGifEncoder VERBOSE
adb shell setprop log.tag.AssetUriFetcher VERBOSE
adb shell setprop log.tag.BitmapEncoder VERBOSE
adb shell setprop log.tag.BufferedIs VERBOSE
adb shell setprop log.tag.ByteArrayPool VERBOSE
adb shell setprop log.tag.CacheLoader VERBOSE
adb shell setprop log.tag.ContentLengthStream VERBOSE
adb shell setprop log.tag.DecodeJob VERBOSE
adb shell setprop log.tag.DiskLruCacheWrapper VERBOSE
adb shell setprop log.tag.Downsampler VERBOSE
adb shell setprop log.tag.Engine VERBOSE
adb shell setprop log.tag.EngineRunnable VERBOSE
adb shell setprop log.tag.GenericRequest VERBOSE
adb shell setprop log.tag.GifDecoder VERBOSE
adb shell setprop log.tag.GifEncoder VERBOSE
adb shell setprop log.tag.GifHeaderParser VERBOSE
adb shell setprop log.tag.GifResourceDecoder VERBOSE
adb shell setprop log.tag.Glide VERBOSE
adb shell setprop log.tag.ImageHeaderParser VERBOSE
adb shell setprop log.tag.ImageVideoDecoder VERBOSE
adb shell setprop log.tag.IVML VERBOSE
adb shell setprop log.tag.LocalUriFetcher VERBOSE
adb shell setprop log.tag.LruBitmapPool VERBOSE
adb shell setprop log.tag.MediaStoreThumbFetcher VERBOSE
adb shell setprop log.tag.MemorySizeCalculator VERBOSE
adb shell setprop log.tag.PreFillRunner VERBOSE
adb shell setprop log.tag.ResourceLoader VERBOSE
adb shell setprop log.tag.RMRetriever VERBOSE
adb shell setprop log.tag.StreamEncoder VERBOSE
adb shell setprop log.tag.TransformationUtils VERBOSE
Glide使用及踩坑日记
2018年01月14日 14:05:59 永远的红姐 阅读数:564
Glide使用及踩坑日记
背景
项目中使用的是之前的ImageLoader,这个图片加载库很久没有维护了,决定使用Glide替换掉ImageLoader
使用Glide
Glide的优点
1.Glide的API使用非常的方便,支持链式调用,支持各种形式源的图片
2.默认使用RGB-565,内存使用更小,滑动更加平滑
3. Glide支持和Glide的生命周期同步
4.可以支持很多特性,比如GIF,动画,可以对图片做各种变换,实现各种效果
5.自己高度自定义,比如:
支持缓存大小,缓存目录
支持自定义模块,网络加载可以使用volley,OKhttp
支持对加密图片的显示
1
2
3
4
5
6
7
8
Glide还有很多优点,Glide可以设置回调函数显示加密图片特别符合我们的实际项目情况
缺点
下面讲一讲Glide的缺点
1.Glide支持很多特性,造成使用方法较复杂
2.由于Glide其功能强大,所以使用的方法非常多,其源码也相对的复杂
3.包较大
1
2
3
使用
关于Glide的基本使用可以看下面的博客
https://mrfu.me/2016/02/27/Glide_Getting_Started/
1
这个博客基本上已经够用了
使用Glide的自定义StreamModelLoader,显示加密图片
我们项目的图片都是加密的,显示之前都需要解密,对于每次显示都去解密把
结果传递给Glide感觉很挫,Glide应该会支持类似的接口!
Glide支持多种形式的数据加载,通过实现StreamModelLoader的接口就可以加载加密的数据!
下面是定义步骤:
1.定义GlideParam
public class GlideParam {
。。。
private InputStream dataInputStream;
public String getFileKey() {
public void setDataInputStream(InputStream inputStream) {
this.dataInputStream = inputStream;
}
public InputStream getDataInputStream() {
return dataInputStream;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
}
2.定义StreamModelLoader
public class DecryptModdelLoader implements StreamModelLoader<GlideParam> {
private GlideParam params;
public DecryptModdelLoader(GlideParam params){
this.params = params;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideParam glideParam, int i, int i1) {
return new DecryptDataFetcher(params);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
}
3.定义DataFetcher
class DecryptDataFetcher implements DataFetcher<InputStream> {
private GlideParam glideParam;
public DecryptDataFetcher(GlideParam glideParam) {
this.glideParam = glideParam;
}
private InputStream mInputStream;
private volatile boolean mIsCanceled;
@Override
public InputStream loadData(Priority priority) throws Exception {
if (mIsCanceled) {
return null;
}
mInputStream = glideParam.getDataInputStream();
//解密操作!!
return mInputStream;
}
@Override
public void cleanup() {
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
} finally {
mInputStream = null;
}
}
}
@Override
public String getId() {
return glideParam.getFileUuid();
}
@Override
public void cancel() {
mIsCanceled = true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
loadData最终将解密后的inputStream传递给了Glide!
4.使用
GlideParam param = new GlideParam();
param.set DataInputStream(new DecryptCallback().getFileInputStream(param));
Glide.with(ApplicationExt.mContext).using(new DecryptModdelLoader(param)).load(param)....
1
2
3
DecryptCallback就是自定义的解密的接口,将解密后的inputStream传递给GlideParam,GlideParam对象在 将流传给到Glide对应的加载器里面!
自己的踩坑
1.图片闪烁问题
进入界面显示图片,退出后在进入图片会闪一下,这个问题困扰了我好久,
以为是内存不够,Glide缓存失效。。。网上说的各种可能的原因都去试过,
都没有什么卵用。。。过了大概一个星期吧,脑子突然开窍了, 突然想到Glide支持和Activity或者Fragment的生命周期同步,
Glide.with(Context)里面的context如果是Activity或者Fragment的,
那界面destroy了,图片内存缓存就没有了,下次的重新去加载,出现闪烁那就正常不过了
所以Context必须传全局的context(劳资真的想打自己两耳屎)
1
2
3
4
5
6
2.使用Signature,优先去网络加载图片
由于我们的下载头像的接口的URL是固定不变的,如果用户更新了头像,我们在点击到联系人详情的时候,
Glide入门教程——12.异常: 调试和报错处理
96 签到钱就到
2016.05.16 19:47* 字数 832 阅读 3267评论 1喜欢 2
Glide — 异常: 调试和报错处理
原文:Exceptions: Debugging and Error Handling
作者:Norman Peitek
翻译:Dexter0218
介绍完了Glide各种概念,我们要转移到一个开发话题上。在这篇文章中,我们将展示给你一些有用的方法去debug你在使用Glide过程中所遇到的问题。
Glide 系列概览
入门简介
高级加载
适配器(ListView, GridView)
占位图& 淡入淡出动画
图片大小 & 缩放
播放GIF & 视频
缓存基础
请求优先级
缩略图
回调:定制view中使用SimpleTarget和ViewTarget
通知栏和桌面小控件的图片加载
异常: 调试和报错处理
自定义变换
用animate()定制动画
整合网络协议栈
用Modules定制Glide
Glide Module 案例: 接受自签名HTTPS证书
Glide Module 案例: 自定义缓存
Glide Module 案例: 通过加载自定义大小图片优化
动态使用 Model Loaders
如何旋转图片
系列综述
本地调试
Glide的常规请求里提供了一个方法设置Log的层级。不幸地是,你没法轻易在产品使用中轻易获取。但,也有一个很简单的方法获得Glide的调试log。你只要通过adb shell,打开terminal,然后使用下面的命令行:
adb shell setprop log.tag.GenericRequest DEBUG
最后一部分DEBUG来自标准Android的log常量。因此,作为参数的递增优先级的选项如下:
VERBOSE
DEBUG
INFO
WARN
ERROR
当图片不存在时,会输出下面的日志:
io.futurestud.tutorials.glide D/GenericRequest: load failed
io.futurestud.tutorials.glide D/GenericRequest: java.io.IOException: Request failed 404: Not Found
...
你已经想到了,这只能在你有个真机连接到电脑上,并且正在调试你的应用时才能用。为了在你的app中生成日志,你需要另外一个不同的方式。方案又是用回调,我们会在后续的小节中介绍。
基本的异常日志
Glide不提供直接获取常规请求的日志,但是你可以在请求出错时抓取异常的日志。例如,如果图片不存在,Glide会(静静地)抛出一个异常,并显示出你.erroer()里指定的图片。如果你明确想要知道异常,创建一个listener,然后传递给Glide的.listener()方法。
首先,创建一个listener作为一个字段对象,避免被垃圾回收:
private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
// todo log exception
// important to return false so the error placeholder can be placed
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
};
在onException方法中,你可以抓取问题,并决定你需要做什么,比如记录日志。如果Glide应当处理这个后果,比如显示一个出错占位图,在onException方法中返回false是很重要的。
你可以在Glide中的构造方法里设置listener:
Glide
.with( context )
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.listener( requestListener )
.error( R.drawable.cupcake )
.into( imageViewPlaceholder );
.error()是否设置不影响日志正常工作。但只有在listener的onException方法里返回false,R.drawable.cupcake才会显示出来。
展望
这篇文章中,你已经学会了在使用Glide过程中如何debug和记录异常。根据你的需求选择适合你的方法,如果有疑问,可以在评论中提出!
在下一篇文章中,我们会继续更高级的主题。下次是自定义变换。
Android之Glide获取图片Path和Glide获取图片Bitmap
2017年12月11日 16:43:38 Sunny_hxn 阅读数:2362
今天主要研究了Glide获取图片Path、Bitmap用法,相信也困扰了大家很久,我在网上也找了很久,基本没有,后来研究了下,也参考了下api文档,总结了以下几个方式:
1. 获取Bitmap:
1)在图片下载缓存好之后获取
[java] view plain copy
Glide.with(mContext).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
image.setImageBitmap(resource);
}
}); //方法中设置asBitmap可以设置回调类型
上面是简单方法,下面有全面的方法,可以完美控制:
[java] view plain copy
Glide.with(mContext).load(url).asBitmap().into(new Target<Bitmap>() {
@Override
public void onLoadStarted(Drawable placeholder) {
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
//TODO set bitmap
}
@Override
public void onLoadCleared(Drawable placeholder) {
}
@Override
public void getSize(SizeReadyCallback cb) {
}
@Override
public void setRequest(Request request) {
}
@Override
public Request getRequest() {
return null;
}
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
});
2)通过url获取
[java] view plain copy
Bitmap myBitmap = Glide.with(applicationContext)
.load(yourUrl)
.asBitmap() //必须
.centerCrop()
.into(500, 500)
.get()
2. 获取图片缓存路径
[java] view plain copy
FutureTarget<File> future = Glide.with(mContext)
.load("url")
.downloadOnly(500, 500);
try {
File cacheFile = future.get();
String path = cacheFile.getAbsolutePath();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
注意:这段代码需要在线程中执行,否则会报错。
Glide使用总结
96 吾乃韩小呆
0.4 2018.07.02 16:39* 字数 869 阅读 16254评论 4喜欢 9
首先,添加依赖
implementation 'com.github.bumptech.glide:glide:4.5.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
之后添加访问网络权限
<uses-permission android:name="android.permission.INTERNET" />
一、常用的方法
1、加载图片到imageView
Glide.with(Context context).load(Strint url).into(ImageView imageView);
2、各种形式的图片加载到ImageView
// 加载本地图片
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对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
3、加载带有占位图
Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView);
占位图目的为在目的图片还未加载出来的时候,提前展示给用户的一张图片;
4、加载失败 放置占位符
Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制
.into(imageView);
//DiskCacheStrategy.NONE: 表示不缓存任何内容。
//DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
//DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
5、加载指定格式的图片--指定为静止图片
Glide.with(this)
.load(url)
.asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
6、加载动态图片
Glide.with(this)
.load(url)
.asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
7、加载指定大小的图片
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)//指定图片大小
.into(imageView);
8、关闭框架的内存缓存机制
Glide.with(this)
.load(url)
.skipMemoryCache(true) //传入参数为false时,则关闭内存缓存。
.into(imageView);
9、关闭硬盘的缓存
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE) //关闭硬盘缓存操作
.into(imageView);
//其他参数表示:
//DiskCacheStrategy.NONE: 表示不缓存任何内容。
//DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
//DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
10、当引用的 url 存在 token 时解决方法-->重写 Glide 的 GlideUrl 方法
public class MyGlideUrl extends GlideUrl {
private String mUrl;
public MyGlideUrl(String url) {
super(url);
mUrl = url;
}
@Override
public String getCacheKey() {
return mUrl.replace(findTokenParam(), "");
}
private String findTokenParam() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
然后加载图片的方式为:
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);
11、利用Glide将图片加载到不同控件或加载成不同使用方式
(1)、拿到图片实例
//1、通过自己构造 target 可以获取到图片实例
SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
imageView.setImageDrawable(resource);
}
};
//2、将图片实例记载到指定的imageview上,也可以做其他的事情
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(simpleTarget);
}
(2)、将图片加载到任何位置
/*
*将图片加载为控件背景
*/
public class MyLayout extends LinearLayout {
private ViewTarget<MyLayout, GlideDrawable> viewTarget;
public MyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
MyLayout myLayout = getView();
myLayout.setImageAsBackground(resource);
}
};
}
public ViewTarget<MyLayout, GlideDrawable> getTarget() {
return viewTarget;
}
public void setImageAsBackground(GlideDrawable resource) {
setBackground(resource);
}
}
//引用图片到指定控件作为背景
public class MainActivity extends AppCompatActivity {
MyLayout myLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myLayout = (MyLayout) findViewById(R.id.background);
}
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(myLayout.getTarget());
}
}
12、Glide 实现预加载
//a、预加载代码
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.preload();
//preload() 有两种重载
// 1、带有参数的重载,参数作用是设置预加载的图片大小;
//2、不带参数的表示加载的图片为原始尺寸;
//b、使用预加载的图片
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
切记:diskCacheStrategy() 方法内必须设置参数为:“ DiskCacheStrategy.SOURCE ”,否则可能预加载失败,导致显示图片时,需要重新加载。
13、Glide 实现图片下载
使用 downloadOnly(int width, int height) 或 downloadOnly(Y target) 方法替代 into(view) 方法。
public void downloadImage(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
final Context context = getApplicationContext();
FutureTarget<File> target = Glide.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
final File imageFile = target.get();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
(1)、有两个参数的 downloadOnly(int width, int height) 方法表示指定下载尺寸,用于在子线程内进行下载;
(2)、一个参数的 downloadOnly(Y target) 方法 在主线程内进行下载
(3)、target.get() 方法可以获取到下载文件保存路径;
使用下载完的图片的方式
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}
注意: diskCacheStrategy() 方法的参数应该为 DiskCacheStrategy.SOURCE 或者 DiskCacheStrategy.ALL否则可能导致加载图片到控件的时候,需要重新加载。
13、监听 Glide 加载的状态
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target,
boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model,
Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(imageView);
}
(1)、onException() 方法表示加载失败,onResourceReady() 表示加载成功;
(2)、 每个方法都有一个 boolean 的返回值,false表示未处理、true 表示处理。
14、Glide 的图形变换功能
(1)、禁用图形变换功能
Glide.with(this)
.load(url)
.dontTransform()
.into(imageView);
这个方法时全局的,导致其他地方的图片也不可进行图形变换了。
修改方法
Glide.with(this)
.load(url)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(imageView);
通过 override() 方法设置大小
(2)、简单的图形变换
Glide.with(this)
.load(url)
.centerCrop()
.into(imageView);
Glide.with(this)
.load(url)
.fitCenter()
.into(imageView);
通过 centerCrop()方法 按照原始的长宽比充满全屏和 fitCenter() 方法 对原图的中心区域进行裁剪对图片进行相关设置。
(3)、override() 方法与 centerCrop() 方法配合使用
String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
Glide.with(this)
.load(url)
.override(500, 500)
.centerCrop()
.into(imageView);
样图
(4)、复杂的图像变换
首先需要再引入一个 第三方框架 。
dependencies {
implementation 'jp.wasabeef:glide-transformations:3.3.0'
// If you want to use the GPU Filters
implementation 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
}
其次,部分样例:
图片虚化
Glide.with(this)
.load(url)
.bitmapTransform(new BlurTransformation(this))
.into(imageView);
图片黑白化
Glide.with(this)
.load(url)
.bitmapTransform(new GrayscaleTransformation(this))
.into(imageView);
多个属性同时使用
Glide.with(this)
.load(url)
.bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))
.into(imageView);
还有更多的好玩的属性,请到框架官网查看:https://github.com/wasabeef/glide-transformations
15、探究Glide的自定义模块功能
请查看 郭神 的文章 《Android图片加载框架最全解析(六),探究Glide的自定义模块功能》 。
16、带进度的Glide图片加载功能
请查看 郭神 的文章Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能 。
17、Glide 4用法
请查看 郭神 的文章Android图片加载框架最全解析(八),带你全面了解Glide 4的用法 。
注意: 本文的大量方法摘抄在郭神的博客,若想更加全面的了解Glide请查看郭神的博客:
郭神的 《Glide最全解析》
Glide 知识梳理(5) - 自定义GlideModule
96 泽毛
2017.03.13 21:02* 字数 1879 阅读 8360评论 2喜欢 10
一、概述
前面说的都是如何使用Glide提供的接口来展示图片资源,今天这篇,我们来讲一下如何改变Glide的配置。
二、定义GlideModule
2.1 步骤
首先,我们需要一个实现了GlideModule接口的类,重写其中的方法来改变Glide的配置,然后让Glide在构造实例的过程中,读取这个类中的配置信息。
第一步:实现GlideModule接口
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通过builder.setXXX进行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通过glide.register进行配置.
}
}
第二步:在AndroidManifest.xml中的<application>标签下定义<meta-data>,这样Glide才能知道我们定义了这么一个类,其中android:name是我们自定义的GlideModule的完整路径,而android:value就固定写死GlideModule。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.example.lizejun.repoglidelearn.CustomGlideModule"
android:value="GlideModule"/>
</application>
2.2 源码分析
上面2.1所做的工作都是为了在Glide创建时可以读取我们在两个回调中配置的信息,我们来看一下Glide是如何使用这个自定义的类的,它的整个流程在Glide的get方法中:
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
//第一步
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
//第二步
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
//在builder构造出glide之前,读取使用者自定义的配置.
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
//第三步
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
可以看到,整个实例化Glide的过程分为三步:
第一步:去AndroidManifest中查找meta-data为GlideModule的类,然后通过反射实例化它。
第二步:之后Glide会新建一个GlideBuilder对象,它会先调用我们自定义的GlideModule的applyOptions方法,并把自己传进去,这样,自定义的GlideModule就可以通过GlideBuilder提供的接口来设置它内部的参数,在builder.createGlide()的过程中就会根据它内部的参数来构建Glide,假如我们没有设置相应的参数,那么在createGlide时,就会采取默认的实现,下面就是memoryCache的例子。
//我们在applyOptions中,可以通过GlideBuilder的这个方法来设定自己的memoryCache.
public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
this.memoryCache = memoryCache;
return this;
}
Glide createGlide() {
//如果builder中没有设定memoryCache,那么采用默认的实现.
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
第三步:在Glide实例化完毕之后,调用自定义GlideModule的registerComponents,并传入当前的Glide实例来让使用者注册自己的组件,其实在Glide实例化的过程中已经注册了默认的组件,如果用户定义了相同的组件,那么就会替换之前的。
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//Glide默认注册的组件.
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
}
通俗的来说,注册组件的目的就是告诉Glide,当我们调用load(xxxx)方法时,应该用什么方式来获取这个xxxx所指向的资源。因此,我们可以看到register的第一个参数就是我们load(xxxx)的类型,第二个参数是对应的输入流,而第三个参数就是定义获取资源的方式。
我们也就分两个部分,在第三、四节,我们分两部分讨论这两个回调函数的用法:applyOptions/registerComponents。
2.3 注意事项
由于Glide是通过反射的方法来实例化GlideModule对象的,因此自定义的GlideModule只能有一个无参的构造方法。
可以看到,上面是支持配置多个GlideModule的,但是GlideModule的读取顺序并不能保证,因此,不要在多个GlideModule对同一个属性进行不同的配置。
三、applyOptions(Context context, GlideBuilder builder)方法详解
在第二节中,我们已经解释过,这个回调方法的目的就是为了让使用者能通过builder定义自己的配置,而所支持的配置也就是GlideBuilder的setXXX方法,它们包括:
setBitmapPool(BitmapPool bitmapPool)
设置Bitmap的缓存池,用来重用Bitmap,需要实现BitmapPool接口,它的默认实现是LruBitmapPool
setMemoryCache(MemoryCache memoryCache)
设置内存缓存,需要实现MemoryCache接口,默认实现是LruResourceCache。
setDiskCache(DiskCache.Factory diskCacheFactory)
设置磁盘缓存,需要实现DiskCache.Factory,默认实现是InternalCacheDiskCacheFactory
setResizeService(ExecutorService service)
当资源不在缓存中时,需要通过这个Executor发起请求,默认是实现是FifoPriorityThreadPoolExecutor。
setDiskCacheService(ExecutorService service)
读取磁盘缓存的服务,默认实现是FifoPriorityThreadPoolExecutor。
setDecodeFormat(DecodeFormat decodeFormat)
用于控制Bitmap解码的清晰度,DecodeFormat可选的值有PREFER_ARGB_8888/PREFER_RGB_565,默认为PREFER_RGB_565。
四、registerComponents(Context context, Glide glide)方法详解
registerComponents相对来说就复杂了很多,它主要和三个接口有关:
ModelLoaderFactory
ModelLoader
DataFetcher
为了便于理解,我们先通过它内部一个默认Module的实现来看一下源码是如何实现的。
我们选取是通用的加载普通图片的url的例子,它对应的注册方法是下面这句:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//注册加载网络url的组件.
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
}
4.1 源码分析
4.1.1 HttpUrlGlideUrlLoader.Factory
首先看一下HttpUrlGlideUrlLoader的内部工厂类,它实现了ModelLoaderFactory<T, Y>接口
public interface ModelLoaderFactory<T, Y> {
ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories);
void teardown();
}
它要求我们返回一个ModelLoader,我们看一下HttpUrlGlideUrlLoader.Factory是怎么做的,可以看到,它返回了HttpUrlGlideUrlLoader,而它的两个泛型参数就是我们register中指定的前两个参数类型。
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new HttpUrlGlideUrlLoader(modelCache);
}
@Override
public void teardown() {}
}
4.1.2 HttpUrlGlideUrlLoader
HttpUrlGlideUrlLoader实现了ModelLoader接口:
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}
ModelLoader提供了一个DataFetcher,它会去请求这个抽象模型所表示的数据:
T:模型的类型。
Y:一个可以被ResourceDecoder解析出数据的表示。
GlideUrl的实现如下:
public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache;
public HttpUrlGlideUrlLoader() {
this(null);
}
public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
this.modelCache = modelCache;
}
//最主要的方法,它决定了我们获取数据的方式.
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
return new HttpUrlFetcher(url);
}
}
4.1.3 HttpUrlFetcher
DataFetcher就是我们读取数据的方式,它的关键方法是loadData,该loadData的返回值就是我们register的第二个参数:
public interface DataFetcher<T> {
T loadData(Priority priority) throws Exception;
void cleanup();
String getId();
void cancel();
}
HttpUrlFetcher实现了DataFetcher接口,在它的loadData方法中,通过传入的url发起请求,最终返回一个InputStream。
public class HttpUrlFetcher implements DataFetcher<InputStream> {
private static final String TAG = "HttpUrlFetcher";
private static final int MAXIMUM_REDIRECTS = 5;
private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
private final GlideUrl glideUrl;
private final HttpUrlConnectionFactory connectionFactory;
private HttpURLConnection urlConnection;
private InputStream stream;
private volatile boolean isCancelled;
public HttpUrlFetcher(GlideUrl glideUrl) {
this(glideUrl, DEFAULT_CONNECTION_FACTORY);
}
HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.connectionFactory = connectionFactory;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
//就是调用HttpUrlConnection请求.
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
@Override
public String getId() {
return glideUrl.getCacheKey();
}
@Override
public void cancel() {
isCancelled = true;
}
}
4.1.4 小结
对上面做个总结,整个流程如下:通过register传入一个ModelLoaderFactory<T, Y>工厂类,该工厂生产的是ModelLoader<T, Y>,而这个ModelLoader会根据T返回一个DataFetcher<Y>,在DataFetcher<Y>中,我们去获取数据。(在上面的例子中T就是GlideUrl,Y就是InputStream)
4.2 自定义ModuleLoader示例:用OkHttpClient替换HttpURLConnection
下面的例子来自于这篇文章:
https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates
第一步:定义ModelLoader和ModelLoader.Factory
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpGlideUrlLoader(getOkHttpClient());
}
@Override
public void teardown() {}
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpGlideUrlFetcher(mOkHttpClient, model);
}
}
第二步:ModelLoader的getResourceFetcher返回一个DataFetcher,我们给它传入一个OkHttpClient实例,让它通过OkHttpClient发起请求。
public class OkHttpGlideUrlFetcher implements DataFetcher<InputStream> {
public OkHttpGlideUrlFetcher(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;
}
}
第三步:在CustomGlideModule中注册这个组件:
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通过builder.setXXX进行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通过glide.register进行配置.
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
}
}
接着我们发起一次请求,通过断点可以发现,调用的是OkHttpClient来进行数据的拉取:
4.3 自定义处理的Module
上面我们分析了如何定义ModuleLoader来关联已有的Module和最终的数据类型,下面我们介绍一些如何定义自己的Model,也就是前面在基础介绍中,所说的load(Module)方法。
第一步:定义Module的接口
public interface AutoSizeModel {
String requestSizeUrl(int width, int height);
}
第二步:实现Module接口
public class AutoSizeModelImpl implements AutoSizeModel {
String mUrl;
public AutoSizeModelImpl(String url) {
mUrl = url;
}
@Override
public String requestSizeUrl(int width, int height) {
return mUrl;
}
}
第三步:定义ModuleLoader和ModuleLoader.Factory
public class AutoSizeModelLoader extends BaseGlideUrlLoader<AutoSizeModel> {
public static class Factory implements ModelLoaderFactory<AutoSizeModel, InputStream> {
@Override
public ModelLoader<AutoSizeModel, InputStream> build(Context context, GenericLoaderFactory factories) {
return new AutoSizeModelLoader(context);
}
@Override
public void teardown() {}
}
public AutoSizeModelLoader(Context context) {
super(context);
}
@Override
protected String getUrl(AutoSizeModel model, int width, int height) {
return model.requestSizeUrl(width, height);
}
}
第四步:在CustomGlideModule中进行关联:
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通过builder.setXXX进行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通过glide.register进行配置.
glide.register(AutoSizeModel.class, InputStream.class, new AutoSizeModelLoader.Factory());
}
}
第五步:调用
public void loadCustomModule(View view) {
AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
Glide.with(this)
.load(autoSizeModel)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(mImageView);
}
4.4 动态指定ModelLoader
在上面的例子中,我们是在自定义的CustomGlideModule中指定了Model和ModuleLoader的关联,当然,我们也可以采用动态指定ModelLoader的方法,也就是说,我们去掉4.3中的第四步,并把第五步改成下面这样:
public void loadDynamicModule(View view) {
AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
AutoSizeModelLoader autoSizeModelLoader = new AutoSizeModelLoader(this);
Glide.with(this)
.using(autoSizeModelLoader)
.load(autoSizeModel)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(mImageView);
}
使用using方法,我们就可以在运行时根据情况为同一个Module选择不同类型的ModuleLoader了。
五、小结
这也是我们Glide学习的最后一章,所有的源码都可以从下面的链接中找到:
https://github.com/imZeJun/RepoGlideLearn
以上是关于text 滑翔滑翔Android(视频,图片)加载和缓存类库https://muyangmin.github.io/glide-docs-cn/的主要内容,如果未能解决你的问题,请参考以下文章