1 Input Framework概述
2014-12-17 14:54
162 查看
1 Input Framework概述
Android输入系统在整个图形系统框架中扮演了很重要的角色,主要负责用户消息的管理,具体职责包括以下几个方面:1、 从底层驱动中获取各种原始的用户消息,包括按键、触摸屏、鼠标、滚迹球等用户事件消息。
2、 最原始消息进行预处理,包括两个方面:一方面,将消息转化成系统可以处理的消息事件;另一方面,处理一些特殊的事件,比如HOME、MENU、POWER键等处理。
3、 将处理后的消息事件分发到各个应用进程,这个需要使用IPC机制,Android系统使用管道来进行消息的传递。
Android系统使用InputManager类来管理消息,而具体的功能则是通过InputReaderThread和InputDispatcherThread两个线程来实现。其中InputReaderThread线程负责消息的读取,而
InputDispatcherThread则负责消息的预处理和分发到各个应用进程中。输入系统的整体框架如下图所示:
从框图中可以看出,Android输入系统通过EventHub收集输入设备的原始数据,InputReader调用接口读取EventHub中获取的数据,然后通知InputDispatcher数据已经准备好,InputDispatcher获得数据回调InputManager的接口间接回调WMS 中的InputMonitor对输入消息进行处理,如果WMS没有消耗掉该消息,则InputDispatcher会将该消息通过管道的方式,直接发送到应用进程中,当前焦点应用的ViewrootImpl会收到该消息,并对消息进行分发处理,最终将其发送到对应的View对象中进行界面响应。
1 Native InputManager初始化
在WindowManagerService构造函数中,经过JNI调用完成了Native层InputManager的初始化,初始化工作有如下几点。
1.1 调用时序图
1.2 类图对象关系
1.3在Native层注册java层的CallBacks回调接口
在InputManagerService类中定义了一个Callback接口:public interface Callbacks { public void notifyConfigurationChanged(); public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen); public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle); public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle); public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn); public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags); public long interceptKeyBeforeDispatching(InputWindowHandle focus, KeyEvent event, int policyFlags); public KeyEvent dispatchUnhandledKey(InputWindowHandle focus, KeyEvent event, int policyFlags); public int getPointerLayer(); } |
class InputMonitor implements InputManagerService.Callbacks{ …… } private WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { final InputMonitor mInputMonitor = new InputMonitor(this); mInputManager = new InputManagerService(context, mInputMonitor); } |
1.4 创建InputDispatcher和InputReader线程
在InputManagerService的构造函数中,调用了
Frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp中的nativeInit()方法。
首先在JNI方法中创建了一个NativeInputManager对象,该对象内部构造函数中又创建了一个InputManager对象。注意这里的InputManager是native的。关键在于InputManager的构造函数中,创建了两个非常重要的对象,InputDispatcher和InputReader,前者是作为消息派发者,后者是input消息的读取者。然后在initialize()方法中,将前面创建的两对象作为参数,创建了对应的两个线程,分别是InputReaderThead和InputDispatchThread。
2 消息传送
2.1 创建InputChannel
前面也简单说过,InputDispatch和客户端之间是通过Pipe传递消息的,Pipe是linux系统调用的一部分,我们需要关注的是Pipe所包含的读写描述符,而为了程序设计的便利,Android增加了一个InputChannel类,有两个作用,一个是保存消息端口对应的Pipe的读写描述符,另一个是通过使用InputChannel所提供的函数创建底层的Pipe对象,。Pipe为管道的意思。
2.1.1 时序图(创建和在wms中注册)
2.1.2 创建InputChannel流程
在上面时序图看出,创建InputChannel是从添加窗口开始的,当客户需要添加窗口的时候,会创建ViewRootImpl对象,并调用它的setView()方法,通过IPC通信调用Session的addWindow()方法,其中就包含了一个InputChannel对象,里面没有数据的空壳,然后调用到WMS的addWindow()方法,希望Wms创建真正的InputChannel。Wms的addWindow():
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { …… if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel,win.mInputWindowHandle); } …… } |
1) 在InputChannel.java的静态方法openInputChannelPai()中,调用了JNI
frameworks/base/coar/jni/android_Inputview_Channel.cpp的
android_view_InputChannel_nativeOpenInputChannelPair()方法
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, jclass clazz, jstring nameObj) { …… sp<InputChannel> serverChannel; sp<InputChannel> clientChannel; status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); …… jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); if (env->ExceptionCheck()) { return NULL; } jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(serverChannel)); if (env->ExceptionCheck()) { return NULL; } jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(clientChannel)); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(channelPair, 0, serverChannelObj); env->SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair; } |
2) Native的InputChannel类是定义在InputTrasport.cpp文件中,openInputChannelPair()方法才是真正的创建管道,然后赋值给两个空壳。
status_t InputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.string(), errno); outServerChannel.clear(); outClientChannel.clear(); return result; } int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); String8 serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; } |
3) 创建好两个native层的InputChannel对象后,存在channelPair数组中,然后通过JNI返回到JAVA环境中。
JNI:
env->SetObjectArrayElement(channelPair, 0, serverChannelObj); env->SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair; |
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); |
2.2 WMS中注册InputChannel
在上面一节介绍了创建InputChannel的过程,其实的创建了两个InputChannel对象,一个作为服务端,需要Wms对其进行注册,另一个作为客户端,需要客户进程对其进行注册。这一节我们看Wms中注册InputChannel。在2.1.1的时序图中我们可以看到,在Wms的addWindow()方法中,调用InputChannel.openInputChannelPair(name);返回一对InputChannel对象,里面包含了管道的描述符等信息,然后调用InputManagerService.registerInputChannel进行注册,即时序图中第八步开始,。流程图如下:通过上面可以看到,经过InputManagerService的调用,最终到达JNInativeRegisterInputChannel方法中,首先通过参数获取到Native层的InputChannel对象,还记得在Native
InputManager初始化中,在JNI创建了一个NativeInputManager对象。这里就获取到该对象,然后调用NativeInputManager::
registerInputChannel()开始注册:
status_t NativeInputManager::registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { return mInputManager->getDispatcher()->registerInputChannel( inputChannel, inputWindowHandle, monitor); } |
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { …… sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); if (monitor) { mMonitoringChannels.push(inputChannel); } …… mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock return OK; } |
2.3客户进程注册InputChannel
Wms中创建了一对InputChannel,其中serverChannel被注册到了InputDispatcher线程中,另一个clientChannel则需要注册到客户进程中,从而使得客户进程可以直接接收到InputDispatcher发送的用户消息。客户端注册InputChannel和在InputManager中注册InputChannel的本质是相同的,即告诉所在进程的native
looper对象,让它监控指定的文件描述符即可。客户端的InputChannel来源于调用Wms的addWindow()时,最后一个参数是一个InputChannel类型的输出参数。下面看调用的时序图
2.3.1 时序图
2.3.2 注册流程
在2.3.1时序图中,我们看到setView()方法,该方法是在客户创建新的窗口时候调用,前面我们也看到了,在这个方法中通过IPC通信调用了Wms.addWindow(),创建了一对管道InputChannel,并在Wms中注册了serverChannel,完成了这两步之后,那么就需要对客户进程注册clientChannel了。1) 客户进程是创建的新的进程,那么为改进程创建一个用于接收消息的WindowInputEventReceiver对象,它继承于InputEventReceiver类。在InputEventReceive构造函数中,调用了JNI方法nativeInit()
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, jobject inputChannelObj, jobject messageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); …… sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverObj, inputChannel, messageQueue); status_t status = receiver->initialize(); …… return reinterpret_cast<jint>(receiver.get()); } |
3) 在initialize()方法中,把文件描述符添加到内部的接收描述符列表中,使得客户进程窗口可以接收到发往该文件描述符的消息
status_t NativeInputEventReceiver::initialize() { int receiveFd = mInputConsumer.getChannel()->getFd(); mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); return OK; } |
2.4 Wms中获取Input消息
用户消息可以分为两类,一个是Key消息,另一个是Motion消息。对于Motion消息,InputDispatcher是通过pipe直接把消息发往客户窗口的,Wms不能对这些消息进行任何的前置处理,而对于Key消息,则会先回调Wms中的Key消息处理函数,在Wms中不处理的消息,才会把消息发往客户端。一般情况下,wms中仅仅处理一些系统的Key消息,比如”Home”键、音量键等。
2.4.1 时序图(Wms获取Key消息)
2.4.2 流程分析
在InputDispatcher中,收到InputReader发送过来的Event消息,最终调用到InputDispatcher::dispatchKeyLocked()方法,开始派发按键消息,接着消息传到NativeInputManager中。在第一节 NativeInputManager初始化中,我们向JNI注册了java层的一些回调接口,这时候就用到了的。
nsecs_t NativeInputManager::interceptKeyBeforeDispatching( const sp<InputWindowHandle>& inputWindowHandle, const KeyEvent* keyEvent, uint32_t policyFlags) { …… if (keyEventObj) { jlong delayMillis = env->CallLongMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeDispatching, inputWindowHandleObj, keyEventObj, policyFlags); …… return result; } |
2.5客户窗口获取Input消息
2.5.1时序图(以Key消息分析)
2.5.2 流程分析
无论是Key消息还是Motion消息,都是通过Pipe管道传递到客户进程窗口的。所有的客户进程都有一个主线程,即ActivityThread类,该类每次开始的时候就会进入一个Looper循环中,然后就不断的从MessageQueue中读取消息,如果没有消息,则进入wait状态,直到下一个消息。在InputDispatcher中,获取了InputReader的Key消息,经过一步步处理,调用到startDispatchCycleLocked()方法:
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { …… case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); // Publish the key event. status = connection->inputPublisher.publishKeyEvent(……); break; } …… } |
status_t InputPublisher::publishKeyEvent( …. return mChannel->sendMessage(&msg); } status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; do { nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); ……. return OK; } |
status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); …… return OK; } |
相关文章推荐
- 设计模式概述
- SQL学习重点概述
- Linux IPC实践(1) -- 概述
- 添物零基础到大型全栈架构师 移动和服务端架构完整(全栈篇)- 概述
- 机器学习方法篇(5)------神经网络概述
- PPP验证概述
- Active Contour Models 主动轮廓模型概述
- android<application>标签概述
- Kakfa揭秘 Day3 Kafka源码概述
- Java面试题之概述
- DCOM概述(一)
- C#接口-接口概述
- 网络安全概述
- DevExpress ASPxGridView 使用文档一:概述
- java框架之spring模块概述
- 大型网站架构概述
- InnoDB 中文参考手册 --- InnoDB Tables 概述
- 差分分析(differential cryptanalysis)概述
- Linux进程间通信(一)---进程间通信概述及其种类
- Java NIO系列教程(一) Java NIO 概述