网易有道Android实习面试题+答案

题来自牛客网https://www.nowcoder.com/discuss/66548

一面:

内部类里面可以写静态方法么

handler handler内存泄露

线程通信方法

loop可以停止么

事件分发

view绘制流程

线程的停止方法

activity的生命周期

singletask的应用

activity的启动模式

下拉刷新与事件分发结合

内部类为什么可以引用外部类 


下面给出完美解答,自己辛辛苦苦通过查阅源码得出的,并非百度得来(有1、2个太过简单的题,没写解答)

Handler内存泄漏
Looper->message->Handler->Activity


线程通信方法
synchronized锁住对象或类
wait/notify,例子:集合元素个数一开始为0,要求是5才行,这个时候obj.wait。另一个线程对增加集合元素,在集合是5的时候,obj.notify,但是另一个线程的资源也必须释放,不然第一个线程还是执行不了。在第一个线程重新获取资源后,开始做事。


volatile
强制让某个变量被其他线程,是在主内存读取的
非原子性,比如i++,用AutomicInteger才行。
不会被重排序


事件分发
view group的dispatchTouchEvent
先判断intercept标记位,down事件或者不是down事件,但是mFirstTouchTarget不为空,会判断onInterceptTouchEvent。在这之前还有一个disAllowIntercept的标记位,默认不打断。如果标记位代表着打断,那么onInterceptTouchEvent就得不到执行。intercept标记位就是false了,就会进入下面循环取孩子的阶段。当然了,正常情况下intercept也是false。如果你的disAllowIntercept标记位child没进行设置,那么默认disAllowIntercept就是false。就会执行到onInterceptTouchEvent,父类默认也是不拦截的。这里这一系列就是解决滑动冲突的核心了。onInterceptTouchEvent默认是false即不拦截的。如果它返回true,那么下面循环取孩子的流程就不会再执行了,这也意味着事件child已经拿不到了。还有一种情况,如果你不是down事件,而且mFirstTouchTarget为空,也就意味着,你的down事件之前没有找到孩子分发,这也意味着,你前面onInterceptTouchEvent进行了一个拦截,或者disAllowIntercept为false,move和up被拦截了。这也意味着,你前面一系列拦截都没有,你down事件成功地进入了循环取孩子,但是你的手指落点不在任何一个孩子的区域内或者你落到了某个孩子的区域内,调用了孩子的dispatchTransformedTouchEvent,这个参数的返回值取决于孩子的dispatchTouchEvent的返回值,dispatchTouchEvent的返回值,如果同时ENABLE和设置了OnTouchListener且OnTouchListener返回true这个时候dispatchTouchEvent就会返回true。而如果没有OnTouchListener或者OnTouchListener有但是返回了false,这个时候view的onTouchEvent就会得到执行了。onTouchEvent的返回值取决于是否clickable。当然了,还有一个TouchDelegate会在事先进行一个拦截。回到上面的层次,如果调用了孩子的dispatchTransformedTouchEvent后,发现最终还是无人接受,(不考虑奇怪的情况,如果ENABLE==false),即OnTouchListener不接受,TouchDelegate也不接受,还有比如TextView默认是不可点击的,即不消费。Button默认是可以点击id,即消费。最终无人接受,mFirstTouchTarget就会是空,那么就执行view group自身的逻辑了。上面仅仅是down事件。接下来如果有move、up事件来临,那么在拦截那里会进行判断,1.不是down事件2.mFirstTouchTarget==null,事件无child消费,那么会默认直接拦截,intercept==true,再也不会进入循环取孩子的逻辑了。所以这也意味着,down事件child拒绝了,后面的事件他也无福消受了。


view绘制流程
measure其实就是进行一个父容器的measure spec(顶层是窗口大小)+view的layout params得出view的measure spec。measure spec包含的mode和size。mode有EXACTLY和AT_MOST。wrap_content对应AT_MOST,match_parent和固定尺寸对应EXACTLY。那么根据父容器的measure spec+view的layout params得出view的measure spec是怎样的情况?父容器EXACTLY,那么view如果是match_parent的,那么他的size就是和父容器一样,他的mode是EXACTLY。而view如果是固定尺寸,那么他的size就是view自身的大小,mode也是EXACTLY。如果view是wrap_conent的,那么view的mode就是AT_MOST,尺寸也是父容器尺寸。如果父容器是AT_MOST的,view是match_parent的,那么view的mode也是AT_MOST,尺寸就不说了。如果view是固定尺寸,那么他的mode是EXACTLY,如果view是wrap_content的,那么他就是AT_MOST。其实这一些都可以用经验得出。比较值得注意的一点是,wrap_content的情况,如果你没有做任何处理,那默认是AT_MOST,也就是充满了屏幕。所以你需要手写,指定一个最小值。(宽高是分立的,都有measure spec,别忘了,宽高都要进行处理)。比如TextView指定了wrap_content,那么一般的操作是进行文字宽高的计算,然后设定宽高,代替widthMeasureSpec.getSize()。这个measure过程如果是view的onMeasure,会接受宽高的measure spec。这个时候已经是父容器替你计算好了的。然而如果是view group的onMeasure,源码中没有说明。以LinearLayout举例,他会先遍历计算child,linear layout会有一个方向,在那个方向上会进行一个计算的累加。这个累加包括,padding,margin,控件在这个方向上的长度。接下来会根据累加的和,再根据linear layout自身的measure spec进行一个计算。比如他是wrap_content的话,那么linear layout的高度就变成了这个方向上的计算累加和。这是measure。至于layout情形,会根据一系列margin,来进行位置的计算。此外,父容器会先递归一遍子元素。在递归每个子元素的时候,又会对子元素进行一个onLayout的调用,这就是一个递归的layout。measure自然也会进行一个递归的measure。最后draw,就是一系列绘制。有个方法值得注意,setWillNotDraw,这个标记位如果过不设置,那么view group的onDraw默认是不被调用的。


线程的停止方法
stop、interrupt、标记位


activity的生命周期
有几个注意点:
数据恢复,view自身也有数据保存恢复的操作
dialog activity,透明主题活动,只会回调上一个活动的pause
打开B活动的时候,A活动会回调pause,然后就是B的生命周期,然后才是A的stop


下拉刷新和事件分发结合
就是手撸一个下拉刷新的控件咯?你只需要给recycler view加一个onScrollListener,通过总和或者用它的api可以判断出偏移距离。如果是0,而且这个时候你的手势是上滑的,那么事件就给父view group了。忘了说了,这里是自定义view group包裹的recycler view。然后进入下拉状态(至于隐藏header是采用-margin的形式)。等你手松开的时候,会进行一个属性动画,复原,即修改margin,复原是代表进入一个恢复状态。这个恢复状态你可以选择隔绝点击事件,也可以选择再次接收。不过这个情况,反正是肯定要拦截住的。


内部类为什么可以引用外部类
在new一个内部类的时候,构造函数会隐式传入外部类类型的实例对象,而在使用外部类的时候,会隐式执行外部类的这个方法,即外部类.xxx方法。不过外部类.你替你隐藏了。

你可能感兴趣的:(Android)