Android视图加载流程(3)之ViewRootImpl的UI刷新机制
前三篇文章分别整理了View,ViewGroup和Activity的事件分发过程,我们今天来讲最后一篇ViewRootImpl(PS:ViewRootImpl也是视图加载很关键的类)
ViewRootImpl事件分发
前几篇我们是介绍了View,ViewGroup和Activtiy的事件分发,很多人以为就此结束了,肯定不是!毕竟触摸事件最先触发的是底层硬件,而不是我们的Activity。
一个触摸行为先通过底层硬件来传递捕获,交给ViewRootImpl,接着事件传递给DecorView,DecorView交给PhoneWindow,PhoneWindow在交给Activity,然后才是我们上几篇讲的事件分发了。
硬件->ViewRootImpl->DecorView->PhoneWindow->Activity
源码解读:
Step1 ViewRootImpl
WMS(Window Manager Service)接到消息后,会调用以下方法
public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = event;
args.arg2 = receiver;
Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
此方法传入两个参数:InputEvent和InputEventReceiver
- InputEvent:输入事件的基类,它有两子类:KeyEvent(键盘输入事件),MotionEvent(屏幕触摸事件)
- InputEventReceiver:为应用程序提供一个接收者来接收输入事件。也就是用来接收输入事件->交给ViewRootImpl的dispatchInputEvent去分发
最后为Hanlder发送标志为MSG_DISPATCH_INPUT_EVENT消息~
Step2 ViewRootImpl
现ViewRootImpl在UI线程中。
final class ViewRootHandler extends Handler {
...
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_DISPATCH_INPUT_EVENT: {
SomeArgs args = (SomeArgs)msg.obj;
InputEvent event = (InputEvent)args.arg1;
InputEventReceiver receiver = (InputEventReceiver)args.arg2;
enqueueInputEvent(event, receiver, 0, true);
args.recycle();
}
break;
...
}
}
}
接收消息的时候后拆分下数据,传给enqueueInputEvent
Step3 ViewRootImpl
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
//part01
//将当前输入事件加入队列中排列等候执行
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//输入事件添加进队列后,加入输入事件的默认尾部
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
//队列计数
mPendingInputEventCount += 1;
...
//part02
//processImmediately则是判断是同步还是异步,前面我们在handler中调用的,因为是在UI线程,肯定是同步的,所以传递了参数是true,如果是异步,则调用到scheduleProcessInputEvents()
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
传入的InputEvent很快的就加入了一个队列QueueInputEvent中,且QueueInputEvent相当于一个链表。
part01
QueueInputEvent
private static final class QueuedInputEvent {
...
public QueuedInputEvent mNext;//指向下一个事件
public InputEvent mEvent;
public InputEventReceiver mReceiver;
...
}
接着我们看方法obtainQueuedInputEvent
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
InputEventReceiver receiver, int flags) {
QueuedInputEvent q = mQueuedInputEventPool;
if (q != null) {
mQueuedInputEventPoolSize -= 1;
mQueuedInputEventPool = q.mNext;
q.mNext = null;
} else {
q = new QueuedInputEvent();
}
q.mEvent = event;
q.mReceiver = receiver;
q.mFlags = flags;
return q;
}
上面的代码简单理解就是将QueuedInputEvent接到队列的尾部
part02
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
processImmediately是判断是否为同步异步,我们此时正在UI线程中,就是同步,即为true。我们先看下异步处理的情况,调用scheduleProcessInputEvents()
private void scheduleProcessInputEvents() {
if (!mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = true;
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
final class ViewRootHandler extends Handler {
...
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_PROCESS_INPUT_EVENTS: {
mProcessInputEventsScheduled = false;
doProcessInputEvents();
}
break;
...
}
}
}
Hanlder发送标志为MSG_PROCESS_INPUT_EVENTS消息~。
我们可以发现,异步最终调用的方法与同步相同 都是调用doProcessInputEvents();
Step3 ViewRootImpl
void doProcessInputEvents() {
//循环取出队列中的输入事件
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
...
mPendingInputEventCount -= 1;
...
//分发处理
deliverInputEvent(q);
}
//处理完所有输入事件,清除标志
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
可以看到该方法是用来循环获取队列中的输入事件
接着我们调用方法deliverInputEvent(q)
private void deliverInputEvent(QueuedInputEvent q) {
//校验输入事件
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);//调用处理
} else {
finishInputEvent(q);
}
}
这里InputStage则是一个实现处理输入事件责任的阶段,它是一个基类,也就是说InputStage提供一系列处理输入事件的方法,也可以转发给其他事件处理,而具体的处理则是看它的实现类。每种InputStage可以处理一定的事件类型,比如AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理。
InputStage的处理情况为,会先调用deliver开始处理
理解图
最终的事件分发处理则是在apply方法里的onProcess方法。
Step4 ViewRootImpl
对于点击事件来说,InputState的子类ViewPostImeInputStage可以处理它,我们看下ViewPostImeInputStage的onProcess
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
// If delivering a new non-key event, make sure the window is
// now allowed to start updating.
handleDispatchWindowAnimationStopped();
final int source = q.mEvent.getSource();
//判断是键盘事件还是触摸事件
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);//触摸事件
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
我们只看下触摸事件,它调用方法processPointerEvent(q)
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
//mView即为DecorView
boolean handled = mView.dispatchPointerEvent(event);
...
return handled ? FINISH_HANDLED : FORWARD;
}
mView(DecorView)继承自FrameLayout。我们发现DecorView,FrameLayout,ViewGroup并没有dispatchPointerEvent方法,所以此方法肯定在View中。
Step5 View
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
传入的MotionEvent经过判断是否为触摸事件~触摸即调用dispatchTouchEvent(event)
Step6 DecorView
DecorView有重写dispatchTouchEvent(event)
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
如果有Callback则用Callback的dispatchTouchEvent(ev)否则直接使用super.dispatchTouchEvent(ev);
这个Callback从何而来呢?Callback为Window里的一个接口
public interface Callback {
...
public boolean dispatchKeyEvent(KeyEvent event);
...
public boolean dispatchTouchEvent(MotionEvent event);
}
有调用必定有实现的地方!
Step7 Activity
public class Activity extends ContextThemeWrapper
implements Window.Callback {
final void attach(...) {
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
}
}
果然Activity实现了Window下的Callback,且在attach方法中,新建PhoneWindow的同时也设置好了Callback!
若想详细了解attach此步,请进
Android视图加载流程(1)之SetContent( )的Step3
从以上我们也可以看出cb.dispatchTouchEvent也就是Activty的dispatchTouchEvent。刚好可以跟上一章文章对应起来~
总结 Summary
PS:本文
整理
自以下文章,若有发现问题请致邮 [email protected]
Hohohong ViewRootImpl源码分析事件分发