三层缓存机制-新知识点,Android常见的内存缓存算法(LruCache原理等),对象池

图片的三级缓存,图片缓存原理。

-- Android缓存机制- 一般存储实现- https://www.jianshu.com/p/41b98118decc
- - Android缓存机制
Android缓存分为内存缓存和文件缓存(磁盘缓存)。在早期,各大图片缓存框架流行之前,常用的内存缓存方式是软引用(SoftReference)和弱引用(WeakReference),如大部分的使用方式:HashMap> imageCache;这种形式。从Android 2.3(Level 9)开始,垃圾回收器更倾向于回收SoftReference或WeakReference对象,这使得SoftReference和WeakReference变得不是那么实用有效。同时,到了Android 3.0(Level 11)之后,图片数据Bitmap被放置到了内存的堆区域,而堆区域的内存是由GC管理的,开发者也就不需要进行图片资源的释放工作,但这也使得图片数据的释放无法预知,增加了造成OOM的可能。因此,在Android3.1以后,Android推出了LruCache这个内存缓存类,LruCache中的对象是强引用的。
 - 二级缓存工作机制
所谓二级缓存实际上并不复杂,当Android端需要获得数据时比如获取网络中的图片,我们首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取;当获得来自网络的数据,就以key-value对的方式先缓存到内存(一级缓存),同时缓存到文件或sqlite中(二级缓存)。注意:内存缓存会造成堆内存泄露,所有一级缓存通常要严格控制缓存的大小,一般控制在系统内存的1/4。
 - 三、离线缓存
离线缓存就是在网络畅通的情况下将从服务器收到的数据保存到本地,当网络断开之后直接读取本地文件中的数据。本质就是要控制好文件的存储、读取。

> Android常见的内存缓存算法
 1.LRU即Least RecentlyUsed,近期最少使用算法:当内存缓存达到设定的最大值时将内存缓存中近期最少使用的对象移除,有效的避免了OOM的出现
 2.Least Frequently Used(LFU):
对每个缓存对象计算他们被使用的频率。把最不常用的缓存对象换走
 3.First in First out(FIFO):
这是一个低负载的算法,并且对缓存对象的管理要不高。通过一个队列去跟踪所有的缓存对象,最近最常用的缓存对象放在后面,而更早的缓存对象放在前面,当缓存容量满时,排在前面的缓存对象会被踢走,然后把新的缓存对象加进去
 4.LargestLimitedMemoryCache:超过指定缓存的话,每次移除栈最大内存的缓存的对象

-- 常规三层缓存机制, 三级缓存的流程:强引用->软引用->硬盘缓存
内存缓存即强引用->软引用; 内存缓存-硬盘缓存-网络

-- 什么是三级缓存?
1.内存缓存,优先加载,速度最快
2.本地缓存,次优先加载,速度快
3.网络缓存,最后加载,速度慢,浪费流量

为什么要进行三级缓存:
三级缓存策略,最实在的意义就是 减少不必要的流量消耗,增加加载速度 。

-- 浅析LruCache原理
 1.用LruCache来取代原来强引用和软引用实现内存缓存
 2.LruCache使用一个LinkedHashMap简单的实现内存的缓存,没有软引用,都是强引用。如果添加的数据大于设置的最大值,就删除最先缓存的数据来调整内存。
 3.maxSize是通过构造方法初始化的值,他表示这个缓存能缓存的最大值是多少。size在添加和移除缓存都被更新值,他通过safeSizeOf这个方法更新值。safeSizeOf默认返回1,但一般我们会根据maxSize重写这个方法,比如认为maxSize代表是KB的话,那么就以KB为单位返回该项所占的内存大小。
 4.除异常外首先会判断size是否超过maxSize,,如果超过了就取出最先插入的缓存,如果不为空就删掉(一般来说只要map不为空都不会返回null,因为他是个双休链表),并把size减去该项所占的大小。这个操作将一直循环下去,直到size比maxSize小或者缓存为空。
 public void trimToSize(int maxSize) {
while (true) {
    K key;
    V value;
    synchronized (this) {
        if (size < 0 || (map.isEmpty() && size != 0)) {
            throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
        }
        if (size <= maxSize) {
            break;
        }
        Map.Entry toEvict = map.eldest();
        if (toEvict == null) {
            break;
        }
        key = toEvict.getKey();
        value = toEvict.getValue();
        map.remove(key);
        size -= safeSizeOf(key, value);
        evictionCount++;
    }
    entryRemoved(true, key, value, null);
  }
}

-- 对象池优化内存
Android 性能优化系列 - 03 使用对象池优化内存- https://www.jianshu.com/p/083f8f6ef0c9
 有时候 UI 卡顿是因为发生了频繁的 GC 造成的,频繁的 GC 其实对应的内存情况就是内存抖动,而发生内存抖动一般都是因为在循环里面或者频繁被调用的方法(比如 View.onDraw() 方法)里面创建对象造成的,面对这种情况,一般有两种做法:
 1.避免在循环里面或者频繁被调用的方法里面创建对象,在循环或者被频繁调用的方法之外创建对象
 2.使用对象池缓存对象,在使用对象的时候,直接从对象池中取对象即可,避免频繁的创建对象
一般来讲,实现对象池有三种方法,对应的具体的数据结构即是数组、链表和 Map

从数据结构的角度,分别介绍了对象池的实现方式,数组、链表、Map 都可以实现对象池,可以根据不同的应用场景灵活地使用具体的数据结构来实现对象池。在 Glide 中应用的可以说是淋漓尽致,单说 BitmapPool 的实现就非常让人称赞了。
对于对象池,个人认为有以下几点是非常需要注意的
  1.对象池的大小。对象池的目的是用于在内存中缓存对象,但是不能所有的对象都缓存,如果所有的对象都缓存,无疑对内存也是一个非常巨大的损耗。所以每个对象池都需要设置一个大小,从上面三个例子中也可以看到每个对象池都有默认的大小的,或者需要在调用构建方法的时候就设置大小
  2.抽象 & 泛型。为了通用性,对象池一般都会先定义一个接口或者抽象基类,而且是泛型的接口或者基类,上面介绍的 Pool、LruPoolStrategy、BaseKeyPool 就是最好的例证,这两点有什么好处呢?接口或者抽象基类有利于扩展,实现不同的存放、获取的方法,而泛型则可以存储不同的对象
  3.抽象方法定义。对象池一般有三个动作存放 put、获取 get 以及新建 create,所以在接口或者抽象基类中一般包含这三个方法即可

-- LRU
  LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法。
  LruCache是Android 3.1所提供的一个缓存类,所以在Android中可以直接使用LruCache实现内存缓存。而DisLruCache目前在Android 还不是Android SDK的一部分,但Android官方文档推荐使用该算法来实现硬盘缓存。
  LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
  LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。
  LinkedHashMap是由数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的对按照一定顺序排列起来。可见LruCache巧妙实现,就是利用了LinkedHashMap的这种数据结构。

你可能感兴趣的:(热点(hot)技术,Android工作机制,原理及进阶,(高级)算法和数据结构)