Android基础之其他
1. Android中的事件传递机制?
答:当我们的手指触碰到屏幕,事件是按照Activity->ViewGroup->View这样的流程到达最终响应触摸事件的View的。而在事件分发过程中,涉及到三个最重要的方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。我们的手指触摸到屏幕的时候,会触发一个Action_Down类型的事件,当前页面的Activity会首先做出相应,也就是说会走到Activity的dispatchTouchEvent()方法内。在这个方法内部有下面两个逻辑:
调用getWindow.superDispatchTouchEvent()。
如果上一步返回true,则直接返回true;否则return自己的onTouchEvent()。显然,当getWindow.superDispatchTouchEvent()返回true,表示当前事件已经被消费掉,无需调用onTouchEvent;否则代表事件并没有被处理,因此需要调用Activity的onTouchEvent进行处理。
我们都知道,getWindow()返回的是PhoneWindow,因此这句代码本质上调用了PhoneWindow中的superDispatchTouchEvent()。而后者实际上调用了mDecor.superDispatchTouchEvent(event)。这个mDecor也就是DecorView,它是FrameLayout的一个子类。在DecorView中的superDispatchTouchEvent(event)中调用的是super.dispatchTouchEvent()。因此,本质上调用的是ViewGroup的dispatchTouchEvent()。
到这里,事件已经从Activity传递到ViewGroup了。接下来我们分析ViewGroup。
在ViewGroup的dispatchTouchEvent()中逻辑大致如下:
通过onInterceptTouchEvent()判断当前ViewGroup是否拦截,默认的ViewGroup都是不拦截的;
如果拦截,则return自己的onTouchEvent();
如果不拦截,则根据child.dispatchTouchEvent()的返回值判断。如果返回true,则return true;否则return自身的onTouchEvent(),在这里实现了未处理事件的向上传递。
通常情况下,ViewGroup 的 onInterceptTouchEvent() 都返回 false,表示不拦截。这里需要注意的是事件序列,比如Down事件、Move事件…Up事件,从 Down到 Up 是一个完整的事件序列,对应着手指从按下到抬起这一系列事件,如果ViewGroup 拦截了 Down 事件,那么后续事件都会交给这个 ViewGroup 的onTouchEvent。如果 ViewGroup 拦截的不是 Down 事件,那么会给之前处理这个Down 事件的 View发送一个Action_Cancel 类型的事件,通知子View这个后续的事件序列已经被 ViewGroup 接管了,子 View 恢复之前的状态即可。
这里举一个常见的例子:在一个 Recyclerview 中有很多的 Button,我们首先按下了一个 button,然后滑动一段距离再松开,这时候 Recyclerview 会跟着滑动,并不会触发这个 button 的点击事件。这个例子中,当我们按下 button 时,这个 button 接收到了 Action_Down 事件,正常情况下后续的事件序列应该由这个 button处理。但我们滑动了一段距离,这时 Recyclerview 察觉到这是一个滑动操作,拦截了这个事件序列,走了自身的 onTouchEvent()方法,反映在屏幕上就是列表的滑动。而这时 button 仍然处于按下的状态,所以在拦截的时候需要发送一个 Action_Cancel 来通知 button 恢复之前状态。
事件分发最终会走到View的dispatchTouchEvent()中。在View的dispatchTouchEvent()中没有onInterceptTouchEvent(),这里很容易理解,View没有child,也就不存在拦截。View的dispatchTouchEvent()直接return了自己的onTouchEvent()。如果onTouchEvent()返回true代表事件被消费,否则未消费的事件会向上传递,直到有View处理了事件或一直没有消费,最终回到Activity的onTouchEvent()终止。
有时候会有人混淆onTouchEvent和onTouch。首先,这两个方法都在View的dispatchTouchEvent()中:
如果touchListener不为null,并且这个View是enable的,而且onTouch返回true,都满足时直接return true,走不到onTouchEvent()方法。
否则,就会触发onTouchEvent()。因此onTouch优先于onTouchEvent获得事件处理权。
最后附上流程图总结:
touch事件传递流程
参考:https://juejin.im/entry/58df5b33570c35005798493c
https://juejin.im/post/5b8f15e26fb9a01a031b12d9#heading-3
2. Handler的原理?
答:与Handler密切相关的还有Message、MessageQueue、Looper。
Message。Message有两个关键的成员变量:target、callback:
(1) target。就是发送消息的Handler
(2) callback。调用Handler.post(Runnable)时传入的Runnable类型的任务。post事件的本质也是创建了一个Message,将我们传入的这个runnable赋值给创建的Message的callback这个成员变量。
MessageQueue。消息队列用于存放消息,其中重点关注next()方法,它会返回下一个待处理的消息。
Looper。Looper消息轮询器其实是连接Handler和消息队列的核心。想要在一个线程中创建一个Handler,首先要通过Looper.prepare()创建Looper,之后还得调用Looper.loop()开启轮询。
(1) prepare()。这个方法做了两件事:首先通过ThreadLocal.get()获取当前线程中的Looper,如果不为空则抛出RuntimeException。否则创建Looper,并通过ThreadLocal.set(looper)将当前线程与刚刚创建的Looper绑定。值得注意的是,上面的消息队列的创建其实就是发生在Looper的构造函数中。
(2)loop()。这个方法开启了整个事件机制的轮询。其本质是开启一个死循环,不断地通过MessageQueue的next()方法获取消息msg。拿到消息后会调用msg.target.dispatchMessage()来做处理。综上也就是调用handler.dispatchMessage()。
Handler。Handler重点在于发送消息和处理消息。
(1)发送消息。其实发送消息除了 sendMessage 之外还有 sendMessageDelayed 和 post 以及 postDelayed 等等不同的方式。但它们的本质都是调用了 sendMessageAtTime。在 sendMessageAtTime 这个方法中调用了 enqueueMessage。在 enqueueMessage 这个方法中做了两件事:通过 msg.target = this 实现了消息与当前 handler 的绑定。然后通过 queue.enqueueMessage 实现了消息入队。
(2)处理消息。 消息处理的核心其实就是dispatchMessage()这个方法。这个方法里面的逻辑很简单,先判断 msg.callback 是否为 null,如果不为空则执行这个 runnable。如果为空则会执行我们的handleMessage方法。
3. ANR出现的情况有几种? 怎么分析解决ANR问题?
答:ANR(Application Not responding)。Android中,主线程(UI线程)如果在规定时内没有处理完相应工作,就会出现ANR。具体来说,ANR会在以下几种情况中出现:
(1) 输入事件(按键和触摸事件)5s内没被处理
(2) BroadcastReceiver的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s)
(3) service 前台20s后台200s未完成启动
(4) ContentProvider的publish在10s内没进行完
分析ANR问题,需要结合Log以及trace文件。具体分析流程,可参照以下两篇文章:
https://www.jianshu.com/p/fa962a5fd939
https://blog.csdn.net/droyon/article/details/51099826
4. 内存泄露的场景有哪些?内存泄漏分析工具使用方法?
答:常见的内存泄露有:
单例模式引起的内存泄露。
静态变量导致的内存泄露。
非静态内部类引起的内存泄露。
使用资源时,未及时关闭引起内存泄露。
使用属性动画引起的内存泄露。
Webview导致的内存泄露。
而对于内存泄露的检测,常用的工具有LeakCanary、MAT(Memory Analyer Tools)、Android Studio自带的Profiler。关于用法,网上教程很多,可自行查阅,下面两个经供参考:
三种用法、MAT
同时附上官方Android Profiler教程
5. 常用的设计模式有哪些?是否了解责任链模式?
答:单例模式,观察者模式,工厂模式,建造者模式,构造者模式,中间者模式,桥接模式,适配器模式等等。