字节跳动大神一行代码:完美讲解内存缓存LruCache实现原理

//加入新创建的对象之后需要重新计算size大小
size += safeSizeOf(key, createdValue);
}
}

if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
//每次新加入对象都需要调用trimToSize方法看是否需要回收
trimToSize(maxSize);
return createdValue;
}
}

/**

  • Caches {@code value} for {@code key}. The value is moved to the head of
  • the queue.
  • @return the previous value mapped by {@code key}.
    */
    public final V put(K key, V value) {
    if (key == null || value == null) {
    throw new NullPointerException(“key == null || value == null”);
    }

V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value); //size加上预put对象的大小
previous = map.put(key, value);
if (previous != null) {
//如果之前存在键为key的对象,则size应该减去原来对象的大小
size -= safeSizeOf(key, previous);
}
}

if (previous != null) {
entryRemoved(false, key, previous, value);
}
//每次新加入对象都需要调用trimToSize方法看是否需要回收
trimToSize(maxSize);
return previous;
}

/**

  • @param maxSize the maximum size of the cache before returning. May be -1
  • to evict even 0-sized elements.
    
  • 此方法根据maxSize来调整内存cache的大小,如果maxSize传入-1,则清空缓存中的所有对象
    */
    private 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!”);
    }
    //如果当前size小于maxSize或者map没有任何对象,则结束循环
    if (size <= maxSize || map.isEmpty()) {
    break;
    }
    //移除链表头部的元素,并进入下一次循环
    Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
    key = toEvict.getKey();
    value = toEvict.getValue();
    map.remove(key);
    size -= safeSizeOf(key, value);
    evictionCount++; //回收次数+1
    }

entryRemoved(true, key, value, null);
}
}

/**

  • Removes the entry for {@code key} if it exists.
  • @return the previous value mapped by {@code key}.
  • 从内存缓存中根据key值移除某个对象并返回该对象
    */
    public final V remove(K key) {
    if (key == null) {
    throw new NullPointerException(“key == null”);
    }

V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}

if (previous != null) {
entryRemoved(false, key, previous, null);
}

return previous;
}

/**

  • Called for entries that have been evicted or removed. This method is
  • invoked when a value is evicted to make space, removed by a call to
  • {@link #remove}, or replaced by a call to {@link #put}. The default
  • implementation does nothing.
  • The method is called without synchronization: other threads may

  • access the cache while this method is executing.
  • @param evicted true if the entry is being removed to make space, false
  • if the removal was caused by a {@link #put} or {@link #remove}.
    
  • @param newValue the new value for {@code key}, if it exists. If non-null,
  • this removal was caused by a {@link #put}. Otherwise it was caused by
    
  • an eviction or a {@link #remove}.
    

*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

/**

  • Called after a cache miss to compute a value for the corresponding key.
  • Returns the computed value or null if no value can be computed. The
  • default implementation returns null.
  • The method is called without synchronization: other threads may

  • access the cache while this method is executing.
  • If a value for {@code key} exists in the cache when this method

  • returns, the created value will be released with {@link #entryRemoved}
  • and discarded. This can occur when multiple threads request the same key
  • at the same time (causing multiple values to be created), or when one
  • thread calls {@link #put} while another is creating a value for the same
  • key.
    */
    protected V create(K key) {
    return null;
    }

private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + “=” + value);
}
return result;
}

/**

  • Returns the size of the entry for {@code key} and {@code value} in
  • user-defined units. The default implementation returns 1 so that size
  • is the number of entries and max size is the maximum number of entries.
  • An entry's size must not change while it is in the cache.

  • 用来计算单个对象的大小,这里默认返回1,一般需要重写该方法来计算对象的大小
  • xUtils中创建LruMemoryCache时就重写了sizeOf方法来计算bitmap的大小
  • mMemoryCache = new LruMemoryCache<MemoryCacheKey, Bitmap>(globalConfig.getMemoryCacheSize()) {
  •   @Override
    
  •   protected int sizeOf(MemoryCacheKey key, Bitmap bitmap) {
    
  •       if (bitmap == null) return 0;
    
  •       return bitmap.getRowBytes() * bitmap.getHeight();
    
  •   }
    
  • };

*/
protected int sizeOf(K key, V value) {
return 1;
}

/**

  • Clear the cache, calling {@link #entryRemoved} on each removed entry.
  • 清空内存缓存
    */
    public final void evictAll() {
    trimToSize(-1); // -1 will evict 0-sized elements
    }

/**

  • For caches that do not override {@link #sizeOf}, this returns the number
  • of entries in the cache. For all other caches, this returns the sum of
  • the sizes of the entries in this cache.
    */
    public synchronized final int size() {
    return size;
    }

/**

  • For caches that do not override {@link #sizeOf}, this returns the maximum
  • number of entries in the cache. For all other caches, this returns the
  • maximum sum of the sizes of the entries in this cache.
    */
    public synchronized final int maxSize() {
    return maxSize;
    }

/**

  • Returns the number of times {@link #get} returned a value.
    */
    public synchronized final int hitCount() {
    return hitCount;
    }

/**

  • Returns the number of times {@link #get} returned null or required a new
  • value to be created.
    */
    public synchronized final int missCount() {
    return missCount;
    }

/**

  • Returns the number of times {@link #create(Object)} returned a value.
    */
    public synchronized final int createCount() {
    return createCount;
    }

/**

  • Returns the number of times {@link #put} was called.
    */
    public synchronized final int putCount() {
    return putCount;
    }

/**

  • Returns the number of values that have been evicted.
    */
    public synchronized final int evictionCount() {
    return evictionCount;
    }

/**

  • Returns a copy of the current contents of the cache, ordered from least
  • recently accessed to most recently accessed.
    */
    public synchronized final Map<K, V> snapshot() {
    return new LinkedHashMap<K, V>(map);
    }

@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format(“LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]”,
maxSize, hitCount, missCount, hitPercent);
}
}

看完代码是不是觉得内存缓存的实现其实很简单?

由于文章篇幅问题复制链接查看详细文章以及获取学习笔记链接:https://shimo.im/docs/QVGDhCjVKvQ6r6TJ
或者可以查看我的【Github】里可以查看
Android看起来知识点就那么多,但是如果你把它全部细化出来,你会看到一张这样的图片(部分):

是不是很吓人,但是如果我们按模块每天学习一点,几个月你是可以拿下的!学完上面70%的知识60w年薪可以做到!(思维脑图可以在我的【Github】里可以查看)我把上面的学习思维导图分为4个部分:

1.Java语言进阶与Android相关技术核

Android应用是由Java语言进行开发的,SDK也是由Java语言编写,对于Android来说,只要SDK没有用Kotlin重写,那么Java语言是都需要学习的。而且Android APK的后台服务器程序大概率是Java语言构建,所以掌握Java也是一种必然,这就是为什么BAT面试为什么死抠你的Java水平。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.APP开发框架体系

APP开发这块知识是现今使用者最多的,并且大多都是CV工程师,程序员界的2-8定律:80%的问题只需要使用20%的知识就可以解决,Android开发也不例外。因而,我们大部分人已经逐步变成了代码搬运工而自己却不知道。代码容易搬运,架构体系却难以复制,要成为架构师,你必须自己亲自去项目实战,读源码,研究原理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.性能调优

我们不仅仅对项目要运筹帷幄,还要能解决一切性能问题。只有具备深厚的代码功底,深入学习源码原理以及使用工具进行测试和检查调优,才能达到知其然,知其所以然的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.移动架构师专题项目实战

架构师不是天生的,是在项目中磨练起来的,所以,我们学了技术就需要结合项目进行实战训练,那么在Android里面最常用的架构无外乎 MVC,MVP,MVVM,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-rDbcuKB4-1715251410904)]

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值