1. Visão Geral
No artigo anterior, o princípio de funcionamento da View analisou como a interface funciona após o início da Activity, e como realizar o processo de medição, layout e desenho da View para exibi-la na tela. o App já pode interagir. A interação com o aplicativo é muitas vezes acionada pelo toque na tela do celular, o que envolve os princípios de acionamento e distribuição dos eventos de toque na tela.
2. Processo de distribuição
O processo de acionamento e distribuição de todo o evento envolve muitos pontos de conhecimento relacionados, como comunicação entre processos, Binder, Socket e muitos processos relacionados ao código nativo (c++). O artigo analisa principalmente os processos relevantes no código Java, e os processos no Nativo serão geralmente tomados.
2.1Serviço do Gerenciador de Entrada
O processo de tocar na tela do celular para fazer o driver da camada de hardware receber o sinal de toque para enviar o evento de toque pela camada do kernel Linux não será analisado aqui. Vamos começar com InputManagerService. InputManagerService é iniciado com a inicialização do processo system_server. Se você estiver interessado na inicialização do system_server, você pode ler o artigo SystemServer Workflow .
public InputManagerService(Context context) {
//上下文
this.mContext = context;
//接收事件的Handler,运行在线程"android.display"
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
....
//绑定Native对象并获取地址mPtr
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
....
//注册服务到ServiceManager
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
复制代码
Desde que o método de construção seja criar um Handler que manipule eventos e vincule-o ao Looper do thread "android.display", DisplayThread é na verdade um objeto HandlerThread singleton. Em seguida, vincule o objeto Native para obter as informações de endereço do objeto Native.
public void start() {
//绑定Native对象
nativeStart(mPtr);
....
//注册几种观察者
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//更新设置
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
//更新设置
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
复制代码
可以看到InputManagerService的start方法最核心的地方是跟Native做绑定,整体InputManagerService的工作流程大部分逻辑其实是native层来实现的,包括事件的处理线程、事件管理等方面。这篇文章有详细的流程上的分析Input系统—启动篇,这里需要提一下InputManagerService的registerInputChannel方法,下文中的流程会涉及到这里。
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
//绑定InputChannel
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
复制代码
2.2ViewRootImpl
事件分发这里又涉及到了ViewRootImpl,可见ViewRootImpl不单单负责View的测量、布局和绘制,触摸事件的分发流程也有参与。
public ViewRootImpl(Context context, Display display) {
....
//触摸事件相关流程的关键代码,获取IWindowSession的代理类
mWindowSession = WindowManagerGlobal.getWindowSession();
....
}
WindowManagerGlobal#getWindowSession
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
//获取IMS的代理类
InputMethodManager imm = InputMethodManager.getInstance();
//获取WMS的代理类
IWindowManager windowManager = getWindowManagerService();
//通过Binder调用获取Session代理对象
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
WindowManagerService#openSession
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
....
//创建Session对象
Session session = new Session(this, callback, client, inputContext);
return session;
}
复制代码
在ViewRootImpl的构造方法中获取了WindowManagerService
的session
代理对象。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
....
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//创建InputChannel
mInputChannel = new InputChannel();
}
....
try {
....
// 通过Binder在SystemServer进程中完成InputChannel的注册
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
}
....
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//创建WindowInputEventReceiver对象
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
....
//组装处理事件的责任链
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
....
}
}
}
复制代码
setView方法的流程里创建了InputChannel
对象然后将InputChannel
对象注册给WMS,最后创建WindowInputEventReceiver
来接收处理事件。Session的addToDisplay方法最终会调用到WindowManagerService
的addWindow
方法。
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
....
//创建WindowState对象
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
....
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
//WindowState对象绑定InputChannel
win.openInputChannel(outInputChannel);
}
....
}
WindowState#openInputChannel
void openInputChannel(InputChannel outInputChannel) {
....
//根据WindowState的HashCode以及Tag来生成InputChannel名称
String name = getName();
//创建一对InputChannel[见小节2.6]
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//第一个是服务端Channel
mInputChannel = inputChannels[0];
//第二个是客户端Channel
mClientChannel = inputChannels[1];
//将socket服务端Channel保存到WindowState的mInputChannel
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
//socket客户端Channel传递给outInputChannel
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
....
}
//利用socket服务端Channel作为参数注册到IMS
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
复制代码
addWindow方法会创建WindowState
,然后InputChannel
会根据WindowState
生成的name创建两个InputChannel
对象,一个作为客户端、一个作为服务端。服务端保存到WindowState
的mInputChannel并注册到IMS,客户端传递给outInputChannel,最终传递给ViewRootImpl
的mInputChannel,触摸事件就可以通过mInputChannel来到ViewRootImpl
的WindowInputEventReceiver
中处理了。整体的流程可以归纳为两部分:
1)创建socket pair,作为InputChannel,一个作为服务端,一个作为客户端。
2) IMS.registerInputChannel()注册InputChannel,监听socket服务端。
2.3WindowInputEventReceiver
在前面ViewRootImpl的setView方法中创建了WindowInputEventReceiver对象,WindowInputEventReceiver用来接收事件和分发事件。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
//构造方法传入了客户端InputChannel
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
//InputEvent事件会回调到onInputEvent方法,此处将输入事件加入队列,最后会调用到ViewRootImpl的enqueueInputEvent方法
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
ViewRootImpl#enqueueInputEvent
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
....
if (processImmediately) {
//方法会调用到这里
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
ViewRootImpl#doProcessInputEvents
void doProcessInputEvents() {
....
//关键代码
deliverInputEvent(q);
....
}
ViewRootImpl#deliverInputEvent
private void deliverInputEvent(QueuedInputEvent q) {
....
InputStage stage; //处理输入事件
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q); //事件的责任链调用
} else {
finishInputEvent(q);
}
}
复制代码
InputStage
是处理输入的责任链,在调用deliver
时会遍历责任链传递事件,事件分发完成后会调用finishInputEvent
,告知SystemServer
进程的InputDispatcher
线程,最终将该事件移除,完成此次事件的分发消费。InputStage有多个处理类型的子类,这里主要看ViewPostImeInputStage
类,这个是处理事件分发到View的相关逻辑。
SyntheticInputStage
。综合处理事件阶段,比如处理导航面板、操作杆等事件。ViewPostImeInputStage
。视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view
事件分发就发生在这个阶段。NativePostImeInputStage
。本地方法处理阶段,主要构建了可延迟的队列。EarlyPostImeInputStage
。输入法早期处理阶段。ImeInputStage
。输入法事件处理阶段,处理输入法字符。ViewPreImeInputStage
。视图预处理输入法事件阶段,调用视图view
的dispatchKeyEventPreIme
方法。NativePreImeInputStage
。本地方法预处理输入法事件阶段。
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
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);
}
}
}
```
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
....
//mView就是DecorView,流程就真正来到了View的dispatchPointerEvent
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
}
View#dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
//触摸事件分发,调用到DecorView的方法
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
DecorView#dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//activity就是DecorView的callBack
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
复制代码
至此为止,屏幕触摸事件就彻底来到了Activity的DecorView中,之后的流程就是常见的事件分发流程了。
3.总结
整个触摸事件的分发流程可以大体分为几个部分:
1)触摸手机屏幕使硬件层驱动接收到触摸信号,由Linux内核层发送触摸事件的Event
。
2)InputManagerService
通过InputReader
线程接收硬件层的Event
,并采用 InputDispatcher
线程进行分发(InputReader
和InputDispatcher
都是native线程)。
3)InputManagerService
分发出的事件交给InputChannel
进行分发,分发的过程涉及进程间的通信,采用的是Socket
,这个过程中又经过了WindowManagerService
。
4)ViewRootImpl
通过WindowInputEventReceiver
接收InputChannel
传过来的InputEvent
。
5)ViewRootImpl
将接收的InputEvent
发送给DecorView
。
6)DecorView
将事件发送给Activity
,Activity
将事件发送给PhoneWindow
,PhoneWindow
又将事件发送回DecorView
。
经过上述6个步骤事件就来到了我们App的界面层了,之后的事件分发就是我们常见的ViewGroup
->View
的流程了。