剖析Framework面试—>>>冲击Android高级职位

作者:老余

就目前一线企业的app都是多线程和多进程的,而Android进程间通信机制就是Binder,原生的线程间通信则是Handler,Binder和Handler是了解安卓运行机制必须要掌握的一个知识点,更是一线企业面试必问的知识点!

所以framework面试在面试中,是会被常问及到的问题!往往有些技术点是拿到大厂offer的关键。

到目前为止,我在Android开发已经待了近六年时间,大大小小的公司进过不少;那么我接下来说说自己在面试中会被常常问及到的面试题。希望能给你们带来一些启发!
剖析Framework面试—>>>冲击Android高级职位_第1张图片

面试题总结如下:

binder

说说binder机制的原理

Binder由4部分组成:Server,Client,ServiceManager和Binder驱动。 Server:服务端。负责功能的具体实现; Client:客户端。功能的调用方; ServiceManager:类似于DNS,根据Client调用的服务名找到对应的Binder引用并返回给Client; Binder驱动:类似于路由,实现进程间通信,负责Binder线程管理等。

Binder通信分为如下几步:

  • Server端首先向ServiceManager注册,SMgr收到注册消息后,将其对应的名称与Binder引用存储于Map中。

  • Client向SMgr发起请求,获取Server的引用。SMgr根据收到的参数查找到对应的Server端引用返回给Client,Client收到后就建立了Client与Server间的通信通道

  • Client传入特定参数给Binder驱动,Binder驱动申请一定大小的内核内存空间,并与内核接收缓存及Server用户空间的接收缓存区进行内存映射。Client线程将参数传递给内核空间的接收缓存区后,线程就挂起。由于内存映射,相当于直接传递给了Server端,Server中的分配线程解析包并调用其中的方法,执行完后将结果封装到数据包中,根据内存映射传递回内核接收缓存区。Binder驱动唤醒Client线程,并传递返回的数据包,Client收到数据包后获取结果,至此完成IPC通信。

android跨进程通信了解吗?共享内存用过吗?binder怎么验证pid?binder驱动了解吗?

Binder数据传输时会将调用方的pid和uid封装到数据包中,Server可以通过getCallingPid()/getCallingUid()获取到调用方的pid或uid,然后进行特定的校验。 也可以通过Binder.clearCallingIndenity()清除调用方的uid/pid。 可以清除也可以通过Binder.restoreCallingIndentity(token)恢复调用方的uid/pid

binder进程间通信可以调用原进程方法吗?

可以,相当于Client已知Server端的引用,无需通过SMgr就有通信通道。

AIDL in out oneWay代表什么意思?
  • in:只允许数据向Server输入;

  • out:只允许数据从Server输出;

  • inout:数据可从Server输出也可输入;

  • oneway:代表方法是异步调用。

跨进程通信了解多少?管道了解吗?

IPC方式:Socket,信号量,管道,Binder,共享内存

SharedParence可以跨进程通信吗?如何改造成可以跨进程通信的.commit和apply的区别

默认是不可以跨进程通信,若需要跨进程通信可如下操作:

  • 需要通信的应用设置成相同的shareUID;

  • 获取数据的应用通过包名,拿的对应的context。然后通过该context拿到SharedPreference实例

commit和apply的区别:

  • 两者都是原子操作,但commit是将数据直接写到数据库中,而apply是写到内存中,然后再异步写入到数据库中。

  • commit有布尔返回值,apply没有。所以commit可以指定提交结果,而apply无法得知。
    剖析Framework面试—>>>冲击Android高级职位_第2张图片

Handler

Handler问题三连:是什么?有什么用?为什么要用Handler,不用行不行?

Handler是Android系统提供给开发者的一种线程间通信的方式。除了Handler,开发者也可以使用其他的线程间通信方式,只不过Google推荐优先使用handler的方式。

真的只能在主(UI)线程中更新UI吗?

禁止在子线程中更新UI,是因为子线程UI状态不确定,但可以更新在子线程中创建的UI,这样UI的状态子线程可随时观察到。 另,Android使用的是单线程模式,处理多线程更新UI造成的不可控情况。但可以在onCreate中子线程直接更新UI,但增加耗时则不行,因为在onCreate的时候,ViewRootImpl还未创建,在onResume之后才创建,创建后才会进行checkThread操作。

真的不能在主(UI)线程中执行网络操作吗?

可以在关闭StrictMode严苛模式的情况下在主线程执行网络操作

为什么建议使用Message.obtain()来创建Message实例?

因为通过Message.obtain()获取Message实例,是从消息池中复用消息实例,避免重复创建浪费资源。

为什么子线程中不可以直接new Handler()而主线程中可以?

因为使用Handler需要首先执行Looper.prepare()和Looper.loop()方法,若不执行这些操作是不可以使用Handler的。 主线程之所以可以直接创建并使用,是因为在ActivityThread中的main方法中已经执行过Looper.prepare()和Looper.loop()方法。

主线程给子线程的Handler发送消息怎么写?

handler.sendEmptyMessage()/postXXX()

HandlerThread实现的核心原理?

HandlerThread就是已经在子线程中执行了Looper.prepare()和Looper.loop()操作。 而无需开发者手动执行这些操作。

当你用Handler发送一个Message,发生了什么?

当用Handler发送消息时,是将该Message入MessageQueue,并同时唤醒Looper.next()阻塞遍历消息队列。

分发给Handler的消息是怎么处理的?

Handler将Message入队MessageQueue,入队后唤醒Looper,Looper遍历消息取出消息,然后回调给Handler的handleMessage()接口。

IdleHandler是什么?

当MessageQueue中无可处理的消息时会调用IdleHandler。用于处理View的相关事务后的一些额外操作,不会阻塞主线程。如View绘制完成后进行一些额外的处理,如获取宽高,打印log等操作

Looper在主线程中死循环,为啥不会ANR?

主线程的各个生命周期也是作为MessageQueue中的消息执行。其本身也是作为消息处理,本身一直处于阻塞中,所以不会ANR。

Handler泄露的原因及正确写法

Handler泄漏,是因为Handler持有Activity的引用,而Handler被MessageQueue中的Message所持有,而Message被MessageQueue所持有,所以当执行长时间的消息时,Activity退出由于被持有链中的MessageQueue所持有,仍然是可达的,所以无法被回收掉,造成泄漏。 此时,可以将Handler作为static静态变量,或者将Activity用弱引用WeakReference包装后再使用。

Handler中的同步屏障机制

同步屏障机制其作用是当遍历消息队列时,遇到屏障则会跳过后续的同步消息,找到异步消息并执行。故其可保证异步消息及时执行。同步屏障消息是target为null的消息,但用普通方式入队时会根据target是否等于null的条件给排除掉,所以同步屏障消息入队有特殊的接口postSyncBarrier,使用完后可通过removeSyncBarrier移除屏障消息。

Handler休眠是怎样的?epoll的原理是什么?如何实现延时消息,如果移除一个延时消息会解除休眠吗?

Handler的休眠唤醒机制,是因为Looper是不断循环遍历队列,但若当前无消息需要执行时那么遍历就会浪费资源。故在无消息需要处理时,就阻塞Looper,待到指定时间或有消息需要处理时再唤醒Looper获取消息并处理。 Handler的休眠唤醒机制是利用linux里的epoll机制,通过epoll_create创建初始化,epoll_ctl注册事件,文件被指定事件触发时则回调,epoll_wait待超过指定时间后执行回调。 MessageQueue中的消息是根据时间顺序排队的,越早需要执行的消息排在越前。然后调用nativePollOnce方法传入延时时间,此时就会阻塞在此,当阻塞时间到达延时时间时就会被唤醒,取出消息执行。 移除延时消息时会唤醒线程再遍历一次,重新计算是否要休眠。故之前的休眠会被解除。
剖析Framework面试—>>>冲击Android高级职位_第3张图片

四大组件及Fragment的启动相关

Activity启动模式,以及各启动模式生命周期问题
Activity怎么启动Service,Activity与Service交互,Service与Thread的区别
Launcher启动App的流程,中间有几种跨进程通信(socket)
A Activity打开B Activity的生命周期变化,会有什么方法打断吗?

Fragment hide show生命周期变化

Fragment replace生命周期变化

AMS交互调用生命周期是顺序的吗?

登陆功能,登陆成功然后跳转到一个新Activity,中间涉及什么?从事件传递,网络请求,AMS交互角度分析

广播与RxBus的区别,全局广播与局部广播区别

说说App的启动过程,在ActivityThread的main方法里面做了什么事,什么时候启动第一个Activity?

onCreate,onResume,onStart里面,什么地方可以获得宽高

attachToWindow什么时候调用?

剖析Framework面试—>>>冲击Android高级职位_第4张图片

绘制机制相关

onMeasure,onLayout,onDraw关系

onMeasure,onLayout和onDraw是绘制流程过程中的三大核心过程。

onMeasure代表测量过程,测量View的大小;

onLayout代表布局过程,布置View所要放的位置;

onDraw代表绘制内容,绘制View所包含的内容。

3个步骤共构成了View的绘制流程中的核心部分。

说说你对屏幕刷新机制的了解,双重缓冲,三重缓冲,黄油模型

双重缓冲:图像写数据写入一个buffer(back buffer),屏幕获取图像数据放入另一个buffer(frame buffer)。当有信号到达时,交换2者Buffer。

黄油模型:Android4.1引入。收到Vsync信号后,交换2个buffer后,cpu/gpu立刻开始计算,然后把数据写入buffer。此时cpu/gpu有完整的16.6ms时间计算。

三重缓冲:在双重缓冲基础上,增加一个buffer(Graphic Buffer)。为了处理一帧的数据16ms不够的情况。

自定义LinearLayout,怎么测量子View宽高
invalidate和postInvalidate的区别及使用

invalidate是通过系统进行触发一次绘制流程;

postInvalidate,是将绘制消息作为消息发给ActivityThread,等待其消息队列分发执行

Requestlayout,onlayout,onDraw,DrawChild区别与联系

requestLayout内部会走到scheduleTraversal,进行绘制的流程发起通过performTraversal开始,经performMeasure,performLayout和performDraw进行一次绘制流程。

onLayout是每个子View真正执行布局的地方。

drawChild是绘制子View,View真正执行draw的操作是通过onDraw执行的。

View绘制流程

从ViewRootImpl的performTraversal()开始,依次经过performMeasure测量,performLayout布局和performDraw绘制流程,完成View的绘制。

经过performMeasure,交给根ViewGroup,依次遍历子View,通过子View的measure方法测量子View的宽高,子View的measure测量真正执行在onMeasure()中,遍历完后最终得到根布局的宽高;

经过performLayout也是遍历子View,通过子View的layout方法进行布局,依次布置子View的位置,同样真正执行布局的接口是onLayout,不过一般是空实现,进而得到根布局的位置;

最后经过performDraw()绘制子View内容。通常View的绘制需要经过如下6大内容:

  • 画背景

  • 画内容

  • 分发给子View进行绘制

  • 画渐变效果

  • 画前景装饰

事件分发

自定义View,事件分发机制讲一讲

触摸屏幕的事件会被记录在文件中,系统通过EventHub去文件取出事件,封装成InputEvent。然后由InputReader交给InputDispatcher,InputDispatcher将事件对象转化成MotionEvent然后开始分发。经一系列的InputStage交给ViewRootImpl。ViewRootImpl最终会交给ViewGroup,然后从ViewGroup开始进行事件分发。

首先当识别到按下事件时需要进行一些初始化和置位操作。然后会判断ViewGroup是否需要拦截该事件,拦截的判断首先会判断是否设置了DISPATCH_ALLOW_INTERCEPT标记未,若设置了则不会拦截;否则需要判断onIntercept()返回true还是false,若返回true则需要拦截,否则也不会拦截。

判断完是否需要拦截后,则会查找到子View中当前未在播放动画,且触摸区域在View区域范围内的View,找到满足条件的View后会判断View是否需要消耗事件,若不需要的话则继续往下传递,否则就将事件交给该View。且同一事件序列的后续事件也将交给该View处理。

若最终找不到需要消耗事件的子View的时候,事件将会回头向上传递,最终由Activity消耗该事件。

dispatchTouchEvent,onInterceptEvent,onTouchEvent顺序,关系

事件首先会交给ViewGroup的dispatchTouchEvent,在dispatchTouchEvent会根据onInterceptEvent的返回结果判断是否需要拦截事件。最终事件若要消耗,则会在onTouchEvent中消耗。

说说事件分发机制,怎么写一个不能滑动的ViewPager

重写其onInterceptTouchEvent(),直接返回false,即不拦截事件,则无法处理滑动。

ViewGroup在ACTION_MOVE时onIntercept返回true,事件怎么传递

这里需要区分ACTION_DOWN事件ViewGroup是否会拦截:

  • 拦截。如果DOWN事件拦截的话,那么后续的事件都会交给ViewGroup;

  • 不拦截。如果不拦截,且子View有要拦截的话,此时ViewGroup要拦截move事件,那么会给子View发送一个cancel事件。然后事件也会发给自己或其子View。

Java

反射、动态代理和注解
反射是什么,在哪里用到,怎么利用反射创建一个对象
代理模式与装饰模式的区别,手写一个静态代理,一个动态代理
说说你对注解的了解,是怎么解析的
泛型是怎么解析的,比如在retrofit中的泛型是怎么解析的
编译时注解与运行时注解,为什么retrofit要使用运行时注解?什么时候用运行时注解?
泛型有什么优点?
动态代理有什么作用?
编译期注解处理的是字节码还是java文件
泛型为什么要擦除?kotlin的泛型了解吗?泛型的pecs原则
反射可以反射final修饰的字段吗?

JVM

对象加载的过程,属性先加载还是方法先加载
垃圾回收机制与jvm结构
静态方法,静态对象为什么不能继承
PathClassLoader与DexClassLoader有什么区别
Jvm的内存结构,Jvm的垃圾回收,方法区有什么东西?
拉圾回收的GCRoot是什么?
说说Java的内存分区
讲讲你对垃圾回收机制的了解,老年代有什么算法?
说说你对类加载机制的了解?DexClassLoader与PathClassLoader的区别
JVM类加载机制了解吗,类什么时候会被加载?类加载的过程具体生命周期是怎样的?
class文件的组成?常量池里面有什么内容?

内容太多以至于有一些答案没附上,这只是面试题的小部分。刷这些当然是不够用的;文章篇幅有限,于是我把面试Android开发岗位的面试题整理好了,以便于自己以后刷题。如阅读者在Android开发的,想跳槽,找工作,我可以把它分享出来;点击获取面试大纲PDF方式。我就介绍这么多,关键是大家有没有用心刷题哈!
剖析Framework面试—>>>冲击Android高级职位_第5张图片

###文末
简历上写的东西,一定要先搞懂,特别是简历上的专业技能。面试收到的最多反馈就是:基础知识不够扎实,技术深度不够。现在Android开发大部分需要懂点C++和Linux知识,大厂也需要刷算法面试题,在后面的学习过程中,我将从以上几个方面着手。当然也必须把基础知识学牢固,技术深度搞深入点。

最后祝大家都能拿到心仪的offer!

你可能感兴趣的:(framework,Android面试,android,面试,java,framework,it)