mTotalSize -= e.size;
} else {
//print log
}
iterator.remove();
prunedFiles++;
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
break;
}
}
}
其中mMaxCacheSizeInBytes
是构造方法传入的一个缓存文件夹的大小,如果不传默认是5M的大小。
通过这个方法可以发现,每当被调用时会传入一个neededSpace
,也就是需要申请的磁盘大小(即要新缓存的那个文件所需大小)。首先会判断如果这个neededSpace
申请成功以后是否会超过最大可用容量,如果会超过,则通过遍历本地已经保存的缓存文件的header(header中包含了缓存文件的缓存有效期、占用大小等信息)去删除文件,直到可用容量不大于声明的缓存文件夹的大小。
其中HYSTERESIS_FACTOR
是一个值为0.9的常量,应该是为了防止误差的存在吧(我猜的)。
Volley缓存命中率的优化
如果让你去设计Volley的缓存功能,你要如何增大它的命中率。
可惜了,如果上面的缓存功能是昨天看的,今天的面试这个问题就能说出来了。
还是上面的代码,在缓存内容可能超过缓存文件夹的大小时,删除的逻辑是直接遍历header删除。这个时候删除的文件有可能是我们上一次请求时刚刚保存下来的,屁股都还没坐稳呢,现在立即删掉,有点舍不得啊。
如果遍历的时候,判断一下,首先删除超过缓存有效期的(过期缓存),其次按照LRU算法,删除最久未使用的,岂不是更合适?
Volley缓存文件名的计算
这个是我一直没弄懂的问题。
如下代码:
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
为什么会要把一个key分成两部分,分别求hashCode,最后又做拼接。
这个问题之前在stackoverflow上问过 #链接
原谅我,别人的回答我最初并没有看懂。直到最近被问到,如果让你设计一个HashMap,如何避免value被覆盖,我才想到原因。
先来看一下 String#hashCode()
的实现:
@Override public int hashCode() {
int hash = hashCode;
if (hash == 0) {
if (count == 0) {
return 0;
}
final int end = count + offset;
final char[] chars = value;
for (int i = offset; i < end; ++i) {
hash = 31*hash + chars[i];
}
hashCode = hash;
}
return hash;
}
从上面的实现可以看到,String的hashcode是根据字符数组中每个位置的字母的int值再加上上次hash值乘以31,这种算法求出来的,至于为什么是31,我也不清楚。
但是可以肯定一点,hashcode并不是唯一的。不信你运行下面这两个输出:
System.out.print(“" + “vFrKiaNHfF7t[9::E[XsX?L7xPp3DZSteIZvdRT8CX:w6d;v<KZnhsM^dqoppe”.hashCode());
System.out.print("” + “hI4pFxGOfS@suhVUd:mTo_begImJPB@Fl[6WJ?ai=RXfIx^=Aix@9M;;?Vdj_Zsi”.hashCode());
这两个字符串是根据hashcode的算法逆向出来的,他们的hashcode都是12345。逆向算法请见这里
再回到我们的问题,为什么会要把一个key分成两部分。现在可以肯定的答出,目的是为了尽可能避免hashcode重复造成的文件名重复(求两次hash两次都与另一个url重复的概率总要比一次重复的概率小吧)。
顺带再提一点,就像上面说的,概率小并不代表不存在。但是Java计算hashcode的速度是很快的,应该是在效率和安全性上取舍的结果吧。
推送心跳包是TCP包还是UDP包或者HTTP包
其实聊起这个问题是因为最近看到 @睡不着起不来的万宵 同学写的一篇文章《Android推送技术研究》结果就产生了这个没回答出来的问题(妈蛋,自己给自己挖坑 - -)
最后看了这篇文章(好像是转的,没找到原地址)android 心跳的分析
原来心跳包的实现是调用了socket.sendUrgentData(0xFF)
这句代码实现的,所以,当然是TCP包。
ARGB_8888占用内存大小
首先说说本题的答案,是4byte,即ARGB各占用8个比特来描述。当时回答错了,详细解答看这里你的 Bitmap 究竟占多大内存
但是——
这个问题引出了一个大大的闹剧,请听我慢慢道来。😂😂😂
不知道怎么就聊到 Bitmap 压缩上了,他说他们的Bitmap居然都是不压缩的😂😂😂
还是直接甩代码吧。。。。
public static Bitmap create(byte[] bytes, int maxWidth, int maxHeight) {
//上面的省略了
option.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, option);
int actualWidth = option.outWidth;
int actualHeight = option.outHeight;
// 计算出图片应该显示的宽高
int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight);
int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth);
option.inJustDecodeBounds = false;
option.inSampleSize = findBestSampleSize(actualWidth, actualHeight,
desiredWidth, desiredHeight);
Bitmap tempBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, option);
// 做缩放
if (tempBitmap != null
&& (tempBitmap.getWidth() > desiredWidth || tempBitmap
.getHeight() > desiredHeight)) {
bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth,
desiredHeight, true);
tempBitmap.recycle();
} else {
bitmap = tempBitmap;
}
}
return bitmap;
}
你这么做,decodeByteArray两次不是更占内存吗?😂😂😂
第一次设置inJustDecodeBounds = true 时候是不占内存的,因为返回的是null
一脸不相信我的说:噢,这地方我下去再看看。
吓得我回来了以后赶紧又看了看,还好没有记错,见源码注释
/**
- If set to true, the decoder will return null (no bitmap), but
- the out… fields will still be set, allowing the caller to query
- the bitmap without having to allocate the memory for its pixels.
*/
public boolean inJustDecodeBounds;
Activity中类似onCreate、onStart运用了哪种设计模式,优点是什么
这个回答的太多了,我当时说的是代理模式,因为AppCompatActivity
中的确是使用的代理模式。这一点还要感谢@代码家 当时说让我看看AppCompatDelegate
类的设计。其主要目的就是通过使用组合来替代继承,降低了耦合。
不过回家后再想一想,对方想听到的应该是模板方法模式吧。在父类中实现一个算法不变的部分,并将可变的行为留给子类来实现。生命周期方法原本就是在基类中做出了Activity不同状态时回调的一系列方法,而这些方法具体需要做的可变部分交给子类去完成。
HashMap的底层实现
HashMap内部是通过数组实现的,而数组的每一位都是Entry
的对象,这个Entry
实际上是一个链表的节点。诶,大学时候数据结构有讲过啊,都忘记了。根据hash算法,求出当前key应该存放在数组的那个index处,如果有值了,则存在index所在Entry
所在链表相邻的下一个位置。
根据如果自己实现HashMap如何防止value覆盖。同上面 Volley 中讲到的。
Atomic、volatile、synchronized区别
面Java基础的时候遇上了这个问题,说如果只有一个i++;的时候,volatile
和synchronized
能否互换。当时也不知道,感觉volatile
作为修饰变量的时候,变量自加会出现加到一半发生线程调度。再看看当时蒙对了。
volatile
可以保证在一个线程的工作内存中修改了该变量的值,该变量的值立即能回显到主内存中,从而保证所有的线程看到这个变量的值是一致的。但是有个前提,因为它不具有操作的原子性,也就是它不适合在对该变量的写操作依赖于变量本身自己。就比如i++、i+=1;这种。但是可以改为num=i+1;如果i是一个 volatile
类型,那么num就是安全的,总之就是不能作用于自身。
synchronized
是基于代码块的,只要包含在synchronized
块中,就是线程安全的。
既然都说了线程安全,就多了解几个:
AtomicInteger
,一个轻量级的synchronized
。使用的并不是同步代码块,而是Lock-Free算法(我也不懂,看代码就是一个死循环调用了底层的比较方法直到相同后才退出循环)。最终的结果就是在高并发的时候,或者说竞争激烈的时候效率比synchronized
高一些。
ThreadLocal
,线程中私有数据。主要用于线程改变内部的数据时不影响其他线程,使用时需要注意static
。
详细分析见这篇文章 。
再补一个,才学到的。利用clone()
方法,如果是一个类的多个对象想共用对象内部的一个变量,而又不想这个变量static,可以使用浅复制方式。(查看设计模式原型模式)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
尾声
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
5ecb71ac0)
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算