Glide使用

图片所占内存的大小如何计算?
比如图片是rgb_565格式的,一个像素点占2个字节,所占的内存大小为:

width*height*2

如果是argb_8888格式的,一个像素点占4个字节,那所占的内存大小为:

width*height*4

Glide 使用简明的流式语法API,大多数情况下,可能完成图片的设置只需要:

Glide.with(activity) .load(url) .into(imageView);

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

  • 活动资源 (Active Resources)
  • 内存缓存 (Memory Cache)
  • 资源类型(Resource Disk Cache)
  • 原始数据 (Data Disk Cache)

活动资源:如果当前对应的图片资源正在使用,则这个图片会被Glide放入活动缓存。
内存缓存:如果图片最近被加载过,并且当前没有使用这个图片,则会被放入内存中。
资源类型: 被解码后的图片写入磁盘文件中,解码的过程可能修改了图片的参数(如:inSampleSize、inPreferredConfig)。
原始数据: 图片原始数据在磁盘中的缓存(从网络、文件中直接获得的原始数据)。

在调用into之后,Glide会首先从Active Resources查找当前是否有对应的活跃图片,没有则查找内存缓存,没有则查找资源类型,没有则查找数据来源。


相较于常见的内存+磁盘缓存,Glide将其缓存分成了4层。

第一层 活动资源
当需要加载某张图片能够从内存缓存中获得的时候,在图片加载时主动将对应图片从内存缓存中移除,加入到活动资源中。
这样也可以避免因为达到内存缓存最大值或者系统内存压力导致的内存缓存清理,从而释放掉活动资源中的图片(recycle)。
活动资源中是一个”引用计数"的图片资源的弱引用集合。
因为同一张图片可能在多个地方被同时使用,每一次使用都会将引用计数+1,而当引用计数为0时候,则表示这个图片没有被使用也就是没有强引用了。这样则会将图片从活动资源中移除,并加入内存缓存。

第二层 内存缓存
内存缓存默认使用LRU(缓存淘汰算法/最近最少使用算法),当资源从活动资源移除的时候,会加入此缓存。使用图片的时候会主动从此缓存移除,加入活动资源。LRU在Android support-v4中提供了LruCache工具类。

最近最少原则:
比如设定maxSize=2,我们先缓存图片1,这时LinkedHashMap队列中数据为图片1,这时又缓存了图片2,那这时LinkedHashMap中为图片2,图片1。当用到图片1的时候,这时LinkedHashMap中的队列为图片1,图片2。当有图片3加入进来的时候,这时会把图片2移除出去,那LinkedHashMap的队列为图片3,图片1。

构造LinkedHashMap的accessOrder设置为true。在使用的此map的时候,自动进行排序(每次get/put,会将使用的value放入链表header头部)。LruCache会在每次get/put的时候判断数据如果达到了maxSize,则会优先删除tail尾端的数据。


磁盘缓存同样使用LRU算法。

第三、四层 磁盘缓存
Resource缓存的是经过解码后的图片,如果再使用就不需要再去进行解码配置(BitmapFactory.Options),加快获得图片速度。比如原图是一个100x100的ARGB_8888图片,在首次使用的时候需要的是50x50的RGB_565图片,那么Resource将50x50 RGB_565缓存下来,再次使用此图片的时候就可以从 Resource 获得。不需要去计算inSampleSize(缩放因子)。
Data 缓存的则是图像原始数据。

Glide中的内存缓存用的是弱引用,来记录下弱引用。

String a = new String("hello");

//引用队列作用:只是通知,通知引用被回收了
final ReferenceQueue<String> queue = new ReferenceQueue<>();
new Thread() {
    @Override
    public void run() {
           super.run();
           try {
                Reference<? extends String> reference = queue.remove();
                Log.i("log", "" + reference);
           } catch (InterruptedException e) {
               e.printStackTrace();
          }
    }}.start();
WeakReference<String> weakReference = new WeakReference<>(a, queue);
Log.i("log", "" + weakReference.get());
a = null;
System.gc();
Log.i("log", "" + weakReference.get());
try {
     Thread.sleep(10_000);
} catch (InterruptedException e) {
     e.printStackTrace();
}

Bitmap复用

如果缓存都不存在,那么会从源地址获得图片(网络/文件)。而在解析图片的时候会需要可以获得BitmapPool(复用池),达到复用的效果。


复用前
复用后

复用效果如上。在未使用复用的情况下,每张图片都需要一块内存。而使用复用的时候,如果存在能被复用的图片会重复使用该图片的内存。
所以复用并不能减少程序正在使用的内存大小。Bitmap复用,解决的是减少频繁申请内存带来的性能(抖动、碎片)问题。

https://developer.android.google.cn/topic/performance/graphics/manage-memory.html

设置Bitmap可复用

获得可复用Bitmap

检查Bitmap是否可复用

当在4.4以上手机复用的时候 需要通过此函数获得占用内存

 return value.getBitmap().getAllocationByteCount();

4.4以下

return value.getBitmap().getByteCount();

Google给出的案例可以看出:
使用方式为在解析的时候设置Options的inBitmap属性。

  • Bitmap的inMutable需要为true。*
  • Android 4.4及以上只需要被复用的Bitmap的内存必须大于等于需要新获得Bitmap的内存,则允许复用此Bitmap。*
  • 4.4以下(3.0以上)则被复用的Bitmap与使用复用的Bitmap必须宽、高相等并且使用复用的Bitmap解码时设置的inSampleSize为1,才允许复用。

BitmapPool是Glide中的Bitmap复用池,同样适用LRU来进行管理。
当一个Bitmap从内存缓存 被动 的被移除(内存紧张、达到maxSize)的时候并不会被recycle。而是加入这个BitmapPool,只有从这个BitmapPool 主动被移除的时候,Bitmap的内存才会真正被recycle释放。

备注

  • 活动资源也在内存中,但是不是用的Lru算法。
  • 磁盘缓存的两种形式:为了使用解码后的图片(比如进行了缩放)和原始的图片。
  • 为什么有活动资源和内存缓存?
    加入内存中只能缓存2个图片,展示了3张,第一张肯定从内存缓存中移除,但是这个时候释放了又不太好。
  • 获取的图片地址有网络类型和本地图片等等,怎样判断使用哪一个Loader?
    使用注册机。
  • ModelLoader,它是Glide加载图片的一套机制,是个接口
  • 一张图片只能在活动资源或者内存缓存中,不能同时在两个中。
  • 内存缓存中的复用池:正常操作都是主动操作,活动资源和内存缓存相互移动。
    被动操作:内存缓存中达到了maxsize,加入到复用池中。在复用池中判断是否可复用,不可复用就recycle,可复用就加入lru复用缓存。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342