Android 面试:事件分发8连问,阿里牛逼

1.1 硬件与内核部分

当我们触摸屏幕或者按键操作时,首先触发的是硬件驱动

驱动收到事件后,将相应事件写入到输入设备节点,这便产生了最原生态的内核事件

当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下

这样做的目的是将输入事件封装为通用的Event,供后续处理

1.2 SystemServer部分

我们知道,当系统启动时,在SystemServer进程会启动一系列系统服务,如AMS,WMS

其中还有一个就是我们管理事件输入的InputManagerService

这个服务就是用来负责与硬件通信,接受屏幕输入事件。

在其内部,会启动一个读线程,也就是InputReader,它会从系统也就是/dev/input/目录拿到任务,并且分发给InputDispatcher线程,然后进行统一的事件分发调度。

1.3 跨进程通信传递给App

现在系统进程已经拿到输入事件了,但还需要传递给App进程,这就涉及到跨进程通信的部分

我们的App中的WindowInputManagerService之间的通信实际上使用的InputChannel

InputChannel是一个pipe,底层实际是通过socket进行通信。

我们知道在Activity启动时会调用ViewRootImpl.setView()

ViewRootImpl.setView()过程中,也会同时注册InputChannel

public final class ViewRootImpl {

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

requestLayout();

// …

// 创建InputChannel

mInputChannel = new InputChannel();

// 通过Binder在SystemServer进程中完成InputChannel的注册

mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

getHostVisibility(), mDisplay.getDisplayId(),

mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,

mAttachInfo.mOutsets, mInputChannel);

}

}

这里涉及到了WindowManagerServiceBinder跨进程通信,读者不需要纠结于详细的细节

只需了解最终在SystemServer进程中,WindowManagerService根据当前的Window创建了SocketPair用于跨进程通信,同时并对App进程中传过来的InputChannel进行了注册

这之后,ViewRootImpl里的InputChannel就指向了正确的InputChannel, 作为Client端,其fdSystemServer进程中Server端的fd组成SocketPair, 它们就可以双向通信了。

然后我们App进程的主线程就会监听socket客户端,当收到消息(输入事件)后,回调NativeInputEventReceiver.handleEvent()方法,最终会走到InputEventReceiver.dispachInputEvent方法。

经过以上操作App终于拿到输入事件了,接下来就是传递到对应页面

1.4小结

关于内核处理输入事件与跨进程通信的部分一般来说不是应用开发者最关注的部分,也不是本文的重点,所以只做了概述

想要了解细节的同学可参考:Input系统—事件处理全过程

2.Touch事件到达App后怎么传递到对应页面


现在我们已经在App进程中拿到输入事件了,接下来看看事件如何分发到页面

我们接下来跟一下源码

2.1 事件回传到ViewRootImpl

//InputEventReceiver.java

private void dispatchInputEvent(int seq, InputEvent event) {

mSeqMap.put(event.getSequenceNumber(), seq);

onInputEvent(event);

}

//ViewRootImpl.java ::WindowInputEventReceiver

final class WindowInputEventReceiver extends InputEventReceiver {

public void onInputEvent(InputEvent event) {

enqueueInputEvent(event, this, 0, true);

}

}

//ViewRootImpl.java

void enqueueInputEvent(InputEvent event,

InputEventReceiver receiver, int flags, boolean processImmediately) {

adjustInputEventForCompatibility(event);

QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

QueuedInputEvent last = mPendingInputEventTail;

if (last == null) {

mPendingInputEventHead = q;

mPendingInputEventTail = q;

} else {

last.mNext = q;

mPendingInputEventTail = q;

}

mPendingInputEventCount += 1;

if (processImmediately) {

doProcessInputEvents();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值