字节跳动大神一行代码:完美讲解内存缓存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,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。

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

最后

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
715891605659)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值