笔者是面霸,面试500+场 当过考官:面过别人500+场 去过500强,也呆过初创公司。
斩获腾讯、华为、字节跳动,蚂蚁金服,OPPO,美团,安卓岗offer!我有一套速通大厂技巧分享给你!
12年毕业,专科生,做安卓9年了。横扫深圳各大互联网公司,从开始的4k工资到现在的4万。我经历了什么?
本人也是一个屌丝,刚毕业的时候一直沉迷泡妞,我一直换,换了9个女朋友,然后在去年深圳买房安定下来了。然后开始写博客。免费分享给大家!
本系列一共10套面试真题,适合快速找工作的人准备,薪资和公司分别如下。
搞定前3套,可以去二线大厂,秒杀70%公司
搞定前6套,可以去二线和一线大厂,秒杀80%公司 ,面试官在你面前都是弟弟
搞定前10套,一线大厂,秒杀100%公司,中国互联网任你挑 ,横扫北上广深,吊打面试官
第一套平安科技 价值22k+, 难易程度:简单 看完你能进这些公司:平安,珍爱网,步步高,优必选科技,柔宇科技,迅雷,芒果网,吉比特,传音控股,海能达,金蝶,有赞
第二套京东 价值22k+, 难易程度:简单 看完你能进这些公司:京东 ,联想,旷视,优酷,58同城,贝壳找房,作业帮,创新工场,金山,唯品会,猎豹,科大讯飞,格力
第三套顺丰 价值25k+, 难易程度:中等 看完你能进这些公司:顺丰,网易 ,海康威视,斗鱼,小红书,去哪儿,喜马拉雅,创维,涂鸦智能,51信用卡
第四套Shopee 价值27k+, 难易程度:中等 看完你能进这些公司:Shopee,京东, 深信服,TCL,平安,荣耀, 美的,一加,随手记,中兴,虎牙
第五套美团 价值28k+, 难易程度:中等 看完你能进这些公司:美团,大疆,顺丰,恒大,携程,货拉拉,知乎,爱奇艺
第六套OPPO 价值28k+, 难易程度:中等 看完你能进这些公司:Oppo,VIVO,360,新浪,拼多多,携程,微博,哔哩哔哩
第七套大疆 价值30k+, 难易程度:难 看完你能进这些公司:大疆,京东, 美团,爱奇艺,小米,拼多多,恒大,万科
第八套 腾讯 价值35k+, 难易程度:难 看完你能进这些公司:腾讯,阿里巴巴,抖音,快手, 百度,美团,华为, 滴滴 等国内所有企业
第九套 字节跳动 价值38k+, 难易程度:难 看完你能进这些公司: 腾讯,蚂蚁金服,今日头条,快手,百度,华为, 美团,滴滴 包含以上所有企业
第十 套 蚂蚁金服 价值40k+, 难易程度:难 看完你能进这些公司:腾讯,支付宝,字节跳动,快手,华为,滴滴,美团,百度 包含以上所有企业
强调:一定要搞的非常清晰,细节一个都不能放过,要非常深入,仅仅10题而已
1.插件化的原理,startActivity hook了哪个方法
2. kotlin 调用Java和java调用kotlin有什么坑?kotlin lazy使用,lazy viewmodel
3.ARouter的原理, ARouter怎么实现接口调用,ARouter怎么实现页面拦截
4. 讲一下RecyclerView的缓存机制,滑动10个,再滑回去,会有几个执行onBindView
5.IdleHandler应用场景
6.在项目中有直接使用tcp,socket来发送消息吗?简单描述下socker通信?简述一下多路复用的原理
7.AMS交互调用生命周期是顺序的吗?
8.binder进程间通信可以调用原进程方法吗?
9.MAT工具怎么分析内存
10.算法题:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
1.插件化的原理,startActivity hook了哪个方法
答:第一次:本来启动插件的,因为没有注册,所以要替换成宿主的Activity。让AMS检查通过。通过动态代理+反射 。iActivityManager,代码对象
第二次:因为插件化,真正启动的是插件Activity。所以又要替换下。消息机制:添加callback和不添加。自己处理消息。把跳转修改
不同的版本要做不同的适配
2. kotlin 调用Java和java调用kotlin有什么坑?kotlin lazy使用,lazy viewmodel
答:1).当Kotlin函数可能被Java调用时, 必须将参数声明为可空类型, 即添加问号后缀。
2). 类型转换。多了一个继承关系
3) .kotlin和java中泛型的差异问题
UploadDbHelp.getInstance().getAllTaskSyn(new UploadDbHelp.DbCallback() { @Override public void getResult(List extends UploadInfo> list) { if (list != null && !list.isEmpty()) { addTaskList((List) list, false); } } });
fun getAllTaskSyn(dbCallback: DbCallback): List? {
var list: List? = null
GlobalScope.launch {
withContext(Dispatchers.IO) {
BNLog.d(TAG, "getAllTaskSyn before")
list = getAllTask()
BNLog.d(TAG, "getAllTaskSyn after get 线程 ing :" + Thread.currentThread().name + "大小:" + list?.size)
}
withContext(Dispatchers.Main) {
BNLog.d(TAG, "getAllTaskSyn main " + Thread.currentThread().name)
dbCallback.getResult(list)
}
}
BNLog.d(TAG, "getAllTaskSyn return")
return list
}
lazy:委托函数
lazy使用:lateinit 只用于变量 var,而 lazy 只用于常量 val
lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行。
val transferDB by lazy { Room.databaseBuilder( CloudServiceApp.getInstance(), TransferDatabase::class.java, "transfer.db" ) .fallbackToDestructiveMigration() .allowMainThreadQueries().build() }
object UserInfoLiveData {
val loginEntity: MutableLiveData by lazy {
MutableLiveData()
}
}
3.ARouter的原理, ARouter怎么实现接口调用,ARouter怎么实现页面拦截
答:原理:1).编译时候通过APT,注解处理器,javapoat生成.java文件
2).启动的时候,初始化,把生成路由表,通过map集合实现。存放:modele。路径。一一的映射关系
3).跳转的时候,通过key,找到对应的路径,然后进行跳转
ARouter实现接口调用:使用通过实现接口iprovider.通过接口+实现的方式
ARouter怎么实现页面拦截:AOP,动态代理的方式
4. 讲一下RecyclerView的缓存机制,滑动10个,再滑回去,会有几个执行onBindView
答:
1)首先说下RecycleView的缓存结构:
Recycleview有四级缓存,分别是mAttachedScrap(屏幕内),mCacheViews(屏幕外),mViewCacheExtension(自定义缓存),mRecyclerPool(缓存池)
mAttachedScrap(屏幕内)
,用于屏幕内itemview快速重用,不需要重新createView和bindView
mCacheViews(屏幕外)
,保存最近移出屏幕的ViewHolder,包含数据和position信息,复用时必须是相同位置的ViewHolder才能复用,应用场景在那些需要来回滑动的列表中,当往回滑动时,能直接复用ViewHolder数据,不需要重新bindView。
mViewCacheExtension(自定义缓存)
,不直接使用,需要用户自定义实现,默认不实现。
mRecyclerPool(缓存池)
,当cacheView满了后或者adapter被更换,将cacheView中移出的ViewHolder放到Pool中,放之前会把ViewHolder数据清除掉,所以复用时需要重新bindView。
2)四级缓存按照顺序需要依次读取。所以完整缓存流程是:
- 保存缓存流程:
- 插入或是删除
itemView
时,先把屏幕内的ViewHolder保存至AttachedScrap
中
- 滑动屏幕的时候,先消失的itemview会保存到
CacheView
,CacheView大小默认是2,超过数量的话按照先入先出原则,移出头部的itemview保存到RecyclerPool缓存池
(如果有自定义缓存就会保存到自定义缓存里),RecyclerPool缓存池会按照itemview的itemtype
进行保存,每个itemType缓存个数为5个,超过就会被回收。
- 获取缓存流程:
- AttachedScrap中获取,通过pos匹配holder——>获取失败,从
CacheView
中获取,也是通过pos获取holder缓存
——>获取失败,从自定义缓存
中获取缓存——>获取失败,从mRecyclerPool
中获取
——>获取失败,重新创建viewholder
——createViewHolder并bindview。
3)了解了缓存结构和缓存流程,我们再来看看具体的问题
滑动10个,再滑回去,会有几个执行onBindView?
- 由之前的缓存结构可知,需要重新执行
onBindView
的只有一种缓存区,就是缓存池mRecyclerPool
。
所以我们假设从加载RecyclView
开始盘的话(页面假设可以容纳7条数据):
- 首先,7条数据会依次调用
onCreateViewHolder
和onBindViewHolder
。
- 往下滑一条(position=7),那么会把position=0的数据放到
mCacheViews
中。此时mCacheViews
缓存区数量为1,mRecyclerPool
数量为0。然后新出现的position=7的数据通过postion在mCacheViews
中找不到对应的ViewHolder
,通过itemtype
也在mRecyclerPool
中找不到对应的数据,所以会调用onCreateViewHolder
和onBindViewHolder
方法。
- 再往下滑一条数据(position=8),如上。
- 再往下滑一条数据(position=9),position=2的数据会放到
mCacheViews
中,但是由于mCacheViews
缓存区默认容量为2,所以position=0的数据会被清空数据然后放到mRecyclerPool
缓存池中。而新出现的position=9数据由于在mRecyclerPool
中还是找不到相应type的ViewHolder,所以还是会走onCreateViewHolder
和onBindViewHolder
方法。所以此时mCacheViews
缓存区数量为2,mRecyclerPool
数量为1。
- 再往下滑一条数据(position=10),这时候由于可以在
mRecyclerPool
中找到相同viewtype的ViewHolder了。所以就直接复用了,并调用onBindViewHolder
方法绑定数据。
- 后面依次类推,刚消失的两条数据会被放到
mCacheViews
中,再出现的时候是不会调用onBindViewHolder方法,而复用的第三条数据是从mRecyclerPool
中取得,就会调用onBindViewHolder
方法了。
4)所以这个问题就得出结论了(假设mCacheViews
容量为默认值2):
- 如果一开始滑动的是新数据,那么滑动10个,就会走10个
bindview
方法。然后滑回去,会走10-2个bindview
方法。一共18次调用。
- 如果一开始滑动的是老数据,那么滑动10-2个,就会走8个
bindview
方法。然后滑回去,会走10-2个bindview
方法。一共16次调用。
但是但是,实际情况又有点不一样。因为Recycleview
在v25版本引入了一个新的机制,预取机制
。
预取机制
,就是在滑动过程中,会把将要展示的一个元素提前缓存到mCachedViews
中,所以滑动10个元素的时候,第11个元素也会被创建,也就多走了一次bindview
方法。但是滑回去的时候不影响,因为就算提前取了一个缓存数据,只是把bindview
方法提前了,并不影响总的绑定item数量。
所以滑动的是新数据的情况下就会多一次调用bindview
方法。
5)总结,问题怎么答呢?
- 四级缓存和流程说一下。
- 滑动10个,再滑回去,
bindview
可以是19次调用,可以是16次调用。
- 缓存的其实就是缓存item的view,在Recycleview中就是
viewholder
。
cachedView
就是mCacheViews
缓存区中的view,是不需要重新绑定数据的。
5.IdleHandler应用场景
答:启动优化,空闲时候调用
IdleHandler 是 MessageQueue 的静态内部接口。
IdleHandler,这是一种在只有当消息队列没有消息时或者是队列中的消息还没有到执行时间时才会执行的 IdleHandler,它存放在mPendingIdleHandlers队列中。
平时是如何使用的:
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle(){
//空闲时处理逻辑
return false;
}
});
从之前的类定义可以看出,返回 false 表示执行后就将该回调移除掉,返回 true 表示该 IdleHandler 一直处于活跃状态,只要空闲就会被回调。
6.在项目中有直接使用tcp,socket来发送消息吗?简单描述下socker通信?简述一下多路复用的原理
答:
7.AMS交互调用生命周期是顺序的吗?
答:ActivityThread里面回调生命周期
onCreate():通过AMS--handleLaunchActivity--performLaunchActivity() ======= mInstrumentation.callActivityOnCreate(activity, r.state);
onResume()----handleResumeActivity----performResumeActivity()==== r.activity.performResume(r.startsNotResumed, reason);
其他也是一样的。
handleResumeActivity和performLaunchActivity()。怎么如何被调用的?
通过发消息和ams
8.binder进程间通信可以调用原进程方法吗?
答:可以,通过代理的形式调用
在使用 Binder 时基本都是调用 framework 层封装好的方法,AIDL 就是 framework 层提供的傻瓜式是使用方式。假设服务已经注册完,客户端怎么执行服务端的方法:
首先通过 ServiceManager 获取到服务端的 BinderProxy 代理对象,通过调用 BinderProxy 将参数,方法标识(例如:TRANSACTION_test,AIDL中自动生成)传给 ServiceManager,同时客户端线程进入等待状态。
ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通讯结束
————————————————
9.MAT工具怎么分析内存
答:1).通过as得到转化之后的.hprof文件
2)。首页中通过饼图Retained Size值最大的几个值
3)。histogram视图:通过类名或者包名排序
4)。Dominator tree视图 ,查看Shallow Heap和Retained Heap的大小
4)。Leaks suspects视图 查看引用关系
使用Android Studio和MAT进行内存泄漏分析 - 知乎
10.算法题,给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
答:class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length;
while (left < right) {
if (nums[left] == val) {
nums[left] = nums[right - 1];
right--;
} else {
left++;
}
}
return left;
}
}
总结:都是设计,灵活性比较高的题目。没有那种死记的题目
腾讯必问的题目:EventBus改良,Binder机制
关于作者:
曾经入职的公司:
2013年 快播公司 因为涉黄被查了
2014年 华强集团 深圳北最大的电子公司
2015年 TCL公司 深圳传统电子公司
2016年 顺丰科技 深圳快递老大
2017年 招商银行 深圳本地银行老大
2018年 字节跳动 深圳后海,抖音头条
2019年 VIVO 深圳手机厂上梅林
2020年 腾讯音乐 深圳滨海大厦
2021年 蚂蚁金服 深圳分公司
从月薪3000到年薪60万。从专科生到深圳一线大厂。关注我就能达到大师级水平,这话我终于敢说了, 年薪60万不是梦!