字节跳动大神一行代码:完美讲解内存缓存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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值