当前位置: 首页 > news >正文

网站建设方案封面网站设置flash插件

网站建设方案封面,网站设置flash插件,可以做微信推文的网站,互联网公司花名大全男0. 前言 有人问到#xff1a;“通过TouchEvent#xff0c;你可以获得到当前的触点#xff0c;它更新的频率和屏幕刷新的频率一样吗#xff1f;”。听到这个问题的时候我感到很诧异#xff0c;我们知道Android是事件驱动机制的设计#xff0c;可以从多种服务中通过IPC通信…0. 前言 有人问到“通过TouchEvent你可以获得到当前的触点它更新的频率和屏幕刷新的频率一样吗”。听到这个问题的时候我感到很诧异我们知道Android是事件驱动机制的设计可以从多种服务中通过IPC通信获取通知许多功能并不能混为一谈。 所以我今天想跟踪一下Input系统是怎么一个逻辑。结合多个优秀博客与源码跟踪分析记录分享。 本文基于 Android 8.0 源码高版本可能会有不同仅供参考。本文的图片均来自参考博客他们已经画的很完善了。 Window相关的知识点欢迎参考 Android从屏幕刷新到View的绘制一之 Window、WindowManager和WindowManagerService之间的关系 1. InputChannel输入事件的消息通道——Socket 1.1 Window与Input输入事件的关系 我们猜想也知道一个输入系统想要分发触摸事件首先就要知道触摸在哪个Window上而APP进程中与Window紧密关联的就是ViewRootImpl与WindowManagerGlobal。果然我们发现在ViewRootImpl的setView()中有输入事件的注册我们来看看: //ViewRootImpl public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {//1.实例化一个InputChannelif ((mWindowAttributes.inputFeatures WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) 0) {//实例化一个InputChannelmInputChannel new InputChannel();}//2.向WMS添加window同时交出自己的InputChannelres mWindowSession.addToDisplay(...,mInputChanel);//向WMS添加window//...//3.创建WindowInputEventReceiver接受Input事件if (mInputChannel ! null) {if (mInputQueueCallback ! null) {mInputQueue new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}//创建了一个WindowInputEventReceiver对象看名字就才到这是输入事件的监听者我们着重关注它是如何监听输入事件的。mInputEventReceiver new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}}正如我们分析所知输入事件与Window是紧密联系的我们接下来做两件事 添加window的时候为Input系统做了哪些铺垫注册了Input事件的监听者它如何接受Input事件的 解决第一个问题我们先来看向WMS添加windows的方法 mWindowSession.addToDisplay()它最终回来到WMS的addWindow() //WindowManagerService public int addWindow(Session session,IWindow client,...,InputChannel outInputChannel) {//...//1. 创建WindowState与Window一一对应进行管理WIndowState win new WindowState(this,session,client,...);//...//2. 开启输入通道final boolean openInputChannels (outInputChannel ! null (attrs.inputFeatures INPUT_FEATURE_NO_INPUT_CHANNEL) 0);if (openInputChannels) {win.openInputChannel(outInputChannel);}}接下来就进入到WindowState.openInputChannel(outInputChannel): //WindowState // Input channel and input window handle used by the input dispatcher. //没错这里的handle就是binder通信中的handle引用 final InputWindowHandle mInputWindowHandle; InputChannel mInputChannel; private InputChannel mClientChannel;void openInputChannel(InputChannel outInputChannel){//1. 开启一个channelInputChannel[] inputChannels InputChannel.openInputChannelPair(name);//2.返回channel引用//服务端的socketmInputChannel inputChannels[0];//客户端的socketmClientChannel inputChannels[1];//将服务端的socket引用交给客户端mInputWindowHandle.inputChannel inputChannels[0];//3. 让WMS中的InputManager注册这个InputChannelmService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle); }这是在WindowState类中的操作mInputWindowHandle是对与WindowState对应的Window的handle引用WindowState又持有WMS可以告知WMS让IMS注册这个InputChannel输入通道。其实到这里我们按InputChannel的名字就能猜测到这里的IPC通信用的不是Binder而是其他的如管道或者socket我们再往后看看。 InputManagerService与WMS、AMS类似的都是系统服务都将自己的binder引用交给了ServiceManager的svclist中。服务中相互调用方法也是常见的。这里WMS持有IMS的引用调用了它的registerInputChannel方法。这个引用在SystemServer启动服务实例化WMS的时候就传入了我们简单看一下就略过 //SystemServer private void startOtherServices(){inputManager new InputManagerService(context);//这里将IMS交给了WMSwm WindowManagerService.main(context, inputManager,mFactoryTestMode ! FactoryTest.FACTORY_TEST_LOW_LEVEL,!mFirstBoot, mOnlyCore, new PhoneWindowManager());//向ServiceManager注册服务ServiceManager.addService(Context.WINDOW_SERVICE, wm);ServiceManager.addService(Context.INPUT_SERVICE, inputManager); }//InputManagerService public InputManagerService(Context context) {this.mContext context;//也是有一个looper在跑this.mHandler new InputManagerHandler(DisplayThread.get().getLooper());//这里需要注意看后文解释LocalServices.addService(InputManagerInternal.class, new LocalService()); }LocalService这个类和ServiceManager的功能类似可以用于注册服务与获取服务只不过这里注册的服务不是Binder实体只能在同一个进程中使用不用跨进程通信例如WMS与IMS都在SystemServer进程之下。顾名思义LocalService为本地服务与远程服务的区别就是远程服务在不同的进程例如AMS对于APP进程来说就是远程服务而WMS对于AMS来说就是本地服务都在SystemServer进程之下。但它们又都开了Binder线程池所以也同时都注册到了ServiceManager的svclist之中。这里就点到为止本文主要目的还是探讨输入事件。 1.2 InputChannel的创建与注册 我们接下来关注两个点 InputChannel是如何打开的如何向InputManagerService注册这个inputChannel 先来看到InputChannel //InputChannel public static InputChannel[] openInputChannelPair(String name) {//走了native方法return nativeOpenInputChannelPair(name); }来到native层 //android_view_InputChannel static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,jclass clazz, jstring nameObj) {//1. 服务端channel和客户端channelspInputChannel serverChannel;spInputChannel clientChannel;//2.openInputChannelPair()来开启两个channelstatus_t result InputChannel::openInputChannelPair(name, serverChannel, clientChannel);//...jobjectArray channelPair env-NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);//...//将两个channel放到channelPair中//...return channelPair; }来到InputTransport.cpp: //InputTransport.cpp status_t InputChannel::openInputChannelPair(const String8 name,spInputChannel outServerChannel, spInputChannel outClientChannel) {int sockets[2];//从名字就可以看出来使用的是socket了if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {//errorreturn result;}//2.处理两个socketint 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));//给两个socket命名放到InputChannel之中String8 serverChannelName name;serverChannelName.append( (server));//实例化一个InputChannel引用回传outServerChannel new InputChannel(serverChannelName, sockets[0]);String8 clientChannelName name;clientChannelName.append( (client));outClientChannel new InputChannel(clientChannelName, sockets[1]);return OK; }跟到这里我们发现这个InputChannel使用的是socket构建InputChannel对象时传入的第一个参数是name第二个参数是fd文件描述符。源码注释是这样解释的InputChannel输入通道是由本地Unix的socket组成的用于跨进程发送、接收消息。每个通道都有一个用于调试的描述性名称也就是第一个参数name。 这下就清楚了创建InputChannel就是给客户端、服务端都开启了一个socket用于进行IPC通信。换句话说openInputChannelPair生成了两个socket的fd文件描述符代表一个双向通道的两端。初始化了两端的包括Native层和Java层的InputChannel对象native层的InputChannel封装了name和fd。 解决完InputChannel是如何打开的问题后我们来看IMS如何注册这个InputChannel的 来到InputManagerService的registerInputChannel(): //InputManagerService public void registerInputChannel(InputChannel inputChannel,InputWindowHandle inputWindowHandle) {nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false); } private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,InputWindowHandle inputWindowHandle, boolean monitor);它调用到了native方法 //com_android_server_input_InputManagerService.cpp static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {//1. NativeInputManagerNativeInputManager* im reinterpret_castNativeInputManager*(ptr);//2. 传入参数可以看到//inputChannelObj是服务端channel//inputWindowHandleObj是客户端channel//...//3. 来到NativeInputManager的registerInputChannelstatus_t status im-registerInputChannel(env, inputChannel, inputWindowHandle, monitor);//... }注册InputChannel输入通道的任务又交给了native层来做来到乐NativeInputManagerService.registerInputChannel() //com_android_server_input_InputManagerService.cpp status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,const spInputChannel inputChannel,const spInputWindowHandle inputWindowHandle, bool monitor) {return mInputManager-getDispatcher()-registerInputChannel(inputChannel, inputWindowHandle, monitor); }其中mInputManager是在初始化NativeInputManager的时候初始化的 //com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const spLooper looper) : mLooper(looper), mInteractive(true) {JNIEnv* env jniEnv();//...spEventHub eventHub new EventHub();mInputManager new InputManager(eventHub, this, this); }这里的EventHub叫做事件集线器时间总线。顾名思义可以获取到各种事件。我们后面再讨论先来看一下mInputManager-getDispatcher()-registerInputChannel() //InputDispatcher.cpp status_t InputDispatcher::registerInputChannel(const spInputChannel inputChannel,const spInputWindowHandle inputWindowHandle, bool monitor) {// acquire lockAutoMutex _l(mLock);//1.判断这个连接connection是否已经建立过了if (getConnectionIndexLocked(inputChannel) 0) {ALOGW(Attempted to register already registered input channel %s,inputChannel-getName().string());return BAD_VALUE;}//2. 建立连接spConnection connection new Connection(inputChannel, inputWindowHandle, monitor);//3. 服务端的socket的fdint fd inputChannel-getFd();//将这个connection保存在mConnectionsByFd中索引为socket的fdmConnectionsByFd.add(fd, connection);//分发器的looper中添加这个fdmLooper-addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);//唤醒looper因为连接状态变化了mLooper-wake();return OK; }一个connection对象被创建出来这个connection表示客户端和服务端之间的一个连接通道用于传输数据。每个Connection都对应一个服务端的InputChannel通过这个InputChannel的fd索引InputDispatcher将所有的connection都保存在mConnectionsByFd中。再将这个fd注册到Looper的监控列表里这样一旦对socket写入数据Looper就会被唤醒接着就会调用回调函数的handleReceiveCallback。由于一个屏幕上可能会有多个Window正在显示所以一个Dispatcher可能会有多个Connection同时存在。 至此InputChannel被创建出来InputChannel两端的socket的fd分别被注册在了所在的Looper中。最后模型大致如下 2. InputManagerService 服务端从设备中捕获输入事件并派发 我们看完了InputManagerService到APP进程的通信方式接下来就需要探索 如何捕获事件如何派发事件 回到在SystemServer中启动InputManagerService的时候我们提到了一个EventHub但是没有深入探究么它主要利用Linux的inotify和epoll机制监听设备事件例如设备插拔、各种触摸、物理按钮等事件。 顾名思义EventHub就是一个不同设备事件的集线器它主要面向的是 /dev/input目录下的设备节点比如 /dev/input/event0 上的事件就是输入事件 //com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const spLooper looper) : mLooper(looper), mInteractive(true) {JNIEnv* env jniEnv();//...spEventHub eventHub new EventHub();mInputManager new InputManager(eventHub, this, this); }初始化InputManager时将eventHub作为参数传入 //InputManager.cpp InputManager::InputManager(const spEventHubInterface eventHub,const spInputReaderPolicyInterface readerPolicy,const spInputDispatcherPolicyInterface dispatcherPolicy) {//1. InputDispatcher在这里初始化的mDispatcher new InputDispatcher(dispatcherPolicy);//2. 同时还初始化了一个InputReader它用于事件读取mReader new InputReader(eventHub, readerPolicy, mDispatcher);initialize(); }//initialize()开启了两个线程一个接受输入的线程一个派发线程 void InputManager::initialize() {mReaderThread new InputReaderThread(mReader);mDispatcherThread new InputDispatcherThread(mDispatcher); }源码的注释给了我们InputReaderThread和InputDispatcherThread的定义 InputReaderThread用于读取并预处理原始输入事件最后发送消息到dispatcherThread中InputDispatcherThread阻塞等待新的事件到来并异步地将他们派发给APP进程。两者之间只有单向通知即从InputReaderThread将消息发送给InputDispatcherThread仅此单程。 InputReader //InputReader.cpp bool InputReaderThread::threadLoop() {//开启循环mReader-loopOnce();return true; }void InputReader::loopOnce() {//...//1.从eventhub中获取设备传来的输入事件size_t count mEventHub-getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);if (count) {//2. 如果读取到了事件处理该事件processEventsLocked(mEventBuffer, count);}if (inputDevicesChanged) {//输入域设备变化了mPolicy-notifyInputDevicesChanged(inputDevices);}//3. 通知派发mQueuedListener-flush(); }从eventhub中读取事件是阻塞的在eventhub.getEvents()中 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {//...RawEvent* event buffer;//1.从设备中读取数据到readBuffer[]//循环读取从不同的设备device中read()数据到readBuffer[]中Device* device mDevices.valueAt(deviceIndex);read(device-fd,readBuffer,sizeof(struct input_event)*capacity);//2.将readbuffer中的输入事件input_event放到buffer中回传回去//for循环从readbuffer中拿到iev填入event也就是返回的buffer中struct input_event iev readBuffer[i];event-deviceId deviceId;event-type iev.type;event-code iev.code;event-value iev.value;event 1;//...return event-buffer;//返回count数量。实际数据已经在buffer中了 }这就简单了IMS通过其InputReader阻塞地从设备中读取输入信息并进行数据处理然后进行事件派发。 我们知道InputReader通过EventHub阻塞从设备中读取输入信息接下来就进行数据处理 //InputReader.cpp void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {//传入的是之前EventHub从设备中读来的原始输入数据count为数据个数因为可能有多个设备for (const RawEvent* rawEvent rawEvents; count;) {int32_t type rawEvent-type;size_t batchSize 1;if (type EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId rawEvent-deviceId;while (batchSize count) {if (rawEvent[batchSize].type EventHubInterface::FIRST_SYNTHETIC_EVENT|| rawEvent[batchSize].deviceId ! deviceId) {break;}batchSize 1;}//批处理设备信息processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {//物理设备变化的处理switch (rawEvent-type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent-when, rawEvent-deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent-when, rawEvent-deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent-when);break;default:ALOG_ASSERT(false); // cant happenbreak;}}count - batchSize;rawEvent batchSize;} }可以看到InputManager管理的不仅仅是触摸输入各种输入设备的信息他都可以获取到。我们不细看如何处理消息的了直接到派发消息的过程。 InputReader读取到数据之后通过mQueuedListener这其实是InputDispatcher对象来唤醒InputDispatcher来派发事件。InputDispatcherThread是一个Looper线程基于native的Looper实现了Handler消息处理模型如果有input事件到来就被唤醒去处理事件处理完毕后继续睡眠放弃CPU使用权等待唤醒。我们来看看InputDispactherThread: //InputDispatcher.cpp bool InputDispatcherThread::threadLoop() {mDispatcher-dispatchOnce();return true; }void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime LONG_LONG_MAX;{ // acquire lockAutoMutex _l(mLock);//被唤醒来处理Input消息if (!haveCommandsLocked()) {dispatchOnceInnerLocked(nextWakeupTime);}//...} // release locknsecs_t currentTime now();int timeoutMillis toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//进入睡眠等待input事件。mLooper-pollOnce(timeoutMillis); }InputDispatcherThread的派发逻辑在dispatchOnceInnerLocked()里面有很多事件分支我们关注到TYPE_MOTION也就是触摸事件分支: //InputDispatcher.cpp void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {switch(mPendingEvent-type){case EventEntry::TYPE_CONFIGURATION_CHANGED:case ...:case EventEntry::TYPE_MOTION:{done dispatchMotionLocked(currentTime, typedEntry,dropReason, nextWakeupTime);break;}//...} }通过dispatchMotionLocked()来处理触摸事件 //InputDispatcher.cpp bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {//...// Identify targets.VectorInputTarget inputTargets;bool conflictingPointerActions false;int32_t injectionResult;if (isPointerEvent) {//1. 找到目标windowinjectionResult findTouchedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime, conflictingPointerActions);} else {injectionResult findFocusedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime);}setInjectionResultLocked(entry, injectionResult);//2. 派发事件dispatchEventLocked(currentTime, entry, inputTargets);return true; }可以看到触摸事件首先会通过 findTOuchedWindowTargetsLocked 找到目标 Window然后再通过 dispatchEventLocked进行消息派发。这下就和WindowManangerService注册Window的功能挂钩了可以通过判断触摸事件的位置以及窗口属性来确定将事件发送到哪个窗口Window。 我们来看到这个寻找窗口的方法findTOuchedWindowTargetsLocked(): //InputDispatcher.cpp //如果是触摸事件有pointer_event进入这里派发 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,const MotionEntry* entry, VectorInputTarget inputTargets, nsecs_t* nextWakeupTime,bool* outConflictingPointerActions) {//...spInputWindowHandle newTouchedWindowHandle;bool isTouchModal false;// Traverse windows from front to back to find touched window and outside targets.//1. 遍历windows找到目标size_t numWindows mWindowHandles.size();for (size_t i 0; i numWindows; i) {spInputWindowHandle windowHandle mWindowHandles.itemAt(i);const InputWindowInfo* windowInfo windowHandle-getInfo();if (windowInfo-displayId ! displayId) {continue; // wrong display}int32_t flags windowInfo-layoutParamsFlags;if (windowInfo-visible) {if (! (flags InputWindowInfo::FLAG_NOT_TOUCHABLE)) {isTouchModal (flags (InputWindowInfo::FLAG_NOT_FOCUSABLE| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) 0;//2. 如果触摸的点在这个窗口上记录一下if (isTouchModal || windowInfo-touchableRegionContainsPoint(x, y)) {newTouchedWindowHandle windowHandle;break; // found touched window, exit window loop}}}} }mWindowHandles表示着所有窗口根据点击位置与Z轴特性等进行具体确定。这个mWindowHandles是在WMS.addWindow()的时候通过InputMonitor间接地调用InputDispatcher::setInputWindows来设置的。所以每次窗口变化InputDispatcher都能获知最新的状态找到窗口后InputDispatcher::dispatchMotionLocked()最后通过dispatchEventLocked()进行了事件派发 //InputDispatcher.cpp void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,EventEntry* eventEntry, const VectorInputTarget inputTargets) {pokeUserActivityLocked(eventEntry);for (size_t i 0; i inputTargets.size(); i) {const InputTarget inputTarget inputTargets.itemAt(i);//拿到InputChannel连接ssize_t connectionIndex getConnectionIndexLocked(inputTarget.inputChannel);if (connectionIndex 0) {//Connection中包含了双方的socket的fdspConnection connection mConnectionsByFd.valueAt(connectionIndex);//进入派发prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);} } }最后通过prepareDispatchCycleLocked()进入了消息入队处理 //InputDispatcher.cpp void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const spConnection connection, EventEntry* eventEntry, const InputTarget* inputTarget) {//...//把消息入队enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); }void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const spConnection connection, EventEntry* eventEntry, const InputTarget* inputTarget) {bool wasEmpty connection-outboundQueue.isEmpty();// Enqueue dispatch entries for the requested modes.//根据不同的情况进行入队enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_OUTSIDE);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_IS);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);// If the outbound queue was previously empty, start the dispatch cycle going.//全部事件都入队完成后派发任务通知更新if (wasEmpty !connection-outboundQueue.isEmpty()) {startDispatchCycleLocked(currentTime, connection);} }消息入队 //InputDispatcher.cpp void InputDispatcher::enqueueDispatchEntryLocked(const spConnection connection, EventEntry* eventEntry, const InputTarget* inputTarget,int32_t dispatchMode) {//1. new 一个新的派发事件DispatchEntry* dispatchEntry new DispatchEntry(eventEntry, // increments refinputTargetFlags, inputTarget-xOffset, inputTarget-yOffset,inputTarget-scaleFactor);//2.根据event的不同进行处理//最后入队connection-outboudQueue.enqueueAtTail(dispatchEntry); }所有消息入队之后InputDispatcher通过startDispatchCycleLocked()启动connection的事件发送将队列中的任务逐个发送 //InputDispatcher.cpp void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const spConnection connection) {//如果是按键status connection-inputPublisher.publishKeyEvent();//如果是触摸status connection-inputPublisher.publishMotionEvent(); }InputDispatcher-Connection-inputPublisher-InputChannel.sendMessage()-socket.send() 接下来就到了我们之前跟踪到的InputChannel的socket通信了。输入事件从服务端通过socket发送给了客户端。需要注意的是APP进程也可能会有多个Socket因为一个Activity或者Dialog等都会拥有一个Window而这个Window最终都会通过WMS进行注册同时在WMS中生成一个WindowState以及用于输入事件传递的InputChannel。一个APP进程会有多个Window自然就会有多个InputChannel。每个InputChannel为一个Window服务。 3. 客户端Window的InputChannel接收从服务端传来的输入事件 3.1 注册输入事件的监听器 我们回顾到ViewRootImpl的setView()中除了让WMS.addToDisplay()添加window并建立inputchannel接着还注册了一个WindowInputEventReceiver //ViewRootImpl public void setView(...){res mWindowSession.addToDisplay();if (mInputChannel ! null) {if (mInputQueueCallback ! null) {mInputQueue new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}//建立了一个输入事件监听器。mInputEventReceiver new WindowInputEventReceiver(mInputChannel,Looper.myLooper());} }我们看到这个WindowInputEventReceiver的初始化它的父类是InputEventReceiver //InputEventReceiver public InputEventReceiver(InputChannel inputChannel, Looper looper) {//传入的是这个window添加时创立好的InputChannelmInputChannel inputChannel;mMessageQueue looper.getQueue();//调用了native方法mReceiverPtr nativeInit(new WeakReferenceInputEventReceiver(this),inputChannel, mMessageQueue);mCloseGuard.open(dispose); }private static native long nativeInit(WeakReferenceInputEventReceiver receiver,InputChannel inputChannel, MessageQueue messageQueue);在这里我们就可以注意到将APP进程主线程的Looper的消息队列MessageQueue的引用往下传递了可以猜到后续有消息会加入到MessageQueue中去。到了native层进行初始化 //android_view_InputEventReceiver.cpp static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {spNativeInputEventReceiver receiver new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue);//1. NativeInputEventReceiver::initialize();status_t status receiver-initialize();return reinterpret_castjlong(receiver.get()); }status_t NativeInputEventReceiver::initialize() {//2. setFdEvent()setFdEvents(ALOOPER_EVENT_INPUT);return OK; }void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents ! events) {mFdEvents events;int fd mInputConsumer.getChannel()-getFd();if (events) {//将socket客户端的fd添加到主线程的消息池mMessageQueue-getLooper()-addFd(fd, 0, events, this, NULL);} else {mMessageQueue-getLooper()-removeFd(fd);}} }我们还记得一开始添加window的时候创建了客户端和服务端的InputChannel。他们都是在system_server进程中创建的。socket服务端fd保存到了WMS的WindowState的mInputChannel。而socket客户端fd则通过binder通信传回给了APP进程的ViewRootImpl的mInputChannel。两端都通过格子的Looper监听对端的写操作一旦对端写入数据我端收到数据马上回调响应。例如屏幕有输入通过socket传到了APP进程立刻将消息发送到主线程Looper进行后续的InputEvent分发。 3.2 回调处理InputEvent 关于回调 服务端socket收到客户端的消息后回调的是inputDispatcher.handleReceiveCallback() 客户端socket收到服务端的消息后回调的是NativeInputEventReceiver.handleEvent() 我们主要关注APP进程如何接受消息。首先在注册监听器的时候native层通过mMessageQueue-getLooper()-addFd()添加了socket的fd这部分做了什么呢如何让looper可以接收到socket消息呢我们看到Looper的addFd()方法这是native层的 //Looper.cpp int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data); }int Looper::addFd(int fd, int ident, int events, const spLooperCallback callback, void* data) {// acquire lockAutoMutex _l(mLock);Request request;request.fd fd;request.ident ident;request.events events;request.seq mNextRequestSeq;request.callback callback;request.data data;if (mNextRequestSeq -1) mNextRequestSeq 0; // reserve sequence number -1struct epoll_event eventItem;request.initEventItem(eventItem);//epoll机制int epollResult epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, eventItem);//...return 1; }当接收到消息之后会通过LooperCallback回调。Looper根据fd来找到对应的监听器一个APP进程可能有多个Window就会有多个InputEventReceiver所以需要查找。最后调用到其handleEvent来处理对应事件 //android_view_InputEventReceiver.cpp int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {if (events ALOOPER_EVENT_INPUT) {JNIEnv* env AndroidRuntime::getJNIEnv();//消费事件status_t status consumeEvents(env, false /*consumeBatches*/, -1, NULL);mMessageQueue-raiseAndClearException(env, handleReceiveCallback);return status OK || status NO_MEMORY ? 1 : 0;}//如果是输出事件if (events ALOOPER_EVENT_OUTPUT) {//...}return 1; }consumeEvents()中进行消费事件就是进一步读取事件封装成java层的对象传递给java层进行相应的回调处理 //android_view_InputEventReceiver.cpp status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {//...for (;;) {uint32_t seq;InputEvent* inputEvent;//1. 获取事件status_t status mInputConsumer.consume(mInputEventFactory,consumeBatches, frameTime, seq, inputEvent, displayId);//...//根据事件类型包装在switch (inputEvent-getType()) {case AINPUT_EVENT_TYPE_KEY://如果是按键类型inputEventObj android_view_KeyEvent_fromNative(env,static_castKeyEvent*(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION: {//如果是触摸类型MotionEvent* motionEvent static_castMotionEvent*(inputEvent);inputEventObj android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}//...}//回调处理通过反射来调用Java层的方法进行回调if (inputEventObj) {env-CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,displayId);env-DeleteLocalRef(inputEventObj);}} }最后触摸事件也就被封装成了InputEvent通过反射回调到InputEventReceiver的dispatchInputEvent进行处理。最后也就通过ViewPostImeInputState来到了View进行触摸事件分发。 4. 总结 4.1 触摸事件分发流程 我们知道InputManagerService不仅能捕获触摸InputEvent还可以捕获各种输入设备的事件我们这里以触摸事件为例梳理流程 点击屏幕InputManagerService的InputReader线程通过EventHub捕获输入事件经过处理后将消息发给InputDispatcher线程InputDispatcher找到触摸的目标窗口通过InputChannel的 socket将事件发送给客户端的主线程Looper主线程Looper通过socket的fd找到对应的窗口将事件交给这个window具体window处理具体事件。例如Activity中的某个Button被按下了。 4.2 Socket 与 binder 的区别 我认为有几点 假设使用binder通信一个APP进程有多个window为每个window都开启一个binder线程进行监听binder的内存映射空间可能很快就会到达4M使得其他必须要binder通信的功能受到影响binder虽然是“一次拷贝”但还是在内核态、用户态切换了多次binder的优势在于减少了一次数据拷贝.但触摸事件传递的数据很小并不需要考虑对减少数据拷贝次数的优化而且减少了切换内核态的次数更高效。Socket可以实现异步通知只需要客户端服务端各一个线程即可是全双工而binder是单工通信如果要实现异步服务端和客户端各要两个线程。如此看来为了满足全双工异步通知socket需要的线程明显少于binder所以socket更为高效.此外socket基于tcp还保证了消息的有序性、可靠性。像AMS这种与APP进程就用的是binder通信因为中间可能要通过Intent来传递parcel数据这个数据虽然不超过1M但相对于触摸消息来说已经非常大了如果也用socket将会进行两次的数据拷贝所以它选择用binder通信通过内存映射来完成只需要一次的”数据拷贝“ 5. 参考文章 十分钟了解Android触摸事件原理InputManagerService Android Input五-InputChannel通信 Android Input子系统为什么要使用socket而不是binder
http://www.dnsts.com.cn/news/44600.html

相关文章:

  • 南山建网站wordpress什么意思
  • WordPress自定义js优化网络
  • 导购网站的seo怎么做wordpress侧边栏怎么加php代码
  • 网站建设公司如何拓宽业务树莓派运行wordpress
  • 移动做网站吗互联网广告平台有哪些
  • 查看网站访问量线上平台推广是做什么的
  • 做电视的视频网站吗汽车网站的建设方向
  • 站长权重网站建设怎么添加背景音乐
  • hph网站模板商业网站网站建设
  • 小规模开普票网站建设几个点合肥模板网站建设费用
  • 网上怎样做电缆网站公司网站建设 阿里
  • 问答系统网站建设1688黄页网品种大全2021
  • 网站流量 seowordpress从入门
  • 电子商务网站开发常用工具wordpress添加app文件
  • wordpress 视频站模板下载wordpress 博客 免费主题
  • 如何选择个人网站主题室内设计师培训班多少钱
  • 网站建设费用入什么科目excel 表格 做的网站
  • 网站文章正文可以做内链吗七宝做网站公司
  • 网站开发对企业有什么用网站建设流程分几步
  • qq网页版登录官网登录入口网站建设商城网站价格
  • 手机移动开发网站建设域名地址查询网
  • 网站怎么做描文本沙市网站建设
  • 网站做支付需要什么备案企业注册成立网址
  • 网站放友情链接违法吗数字营销沙盘大赛
  • 贵州建设职业技术学院网站系统搭建
  • 网站备案怎么做超链接个人网站建设计划表
  • 企业网站设计的要求wordpress for sae 4.3
  • 网站建设流程发布网站和网页制作热门国际新闻
  • 北京网站建站公微信小程序一起生活怎么注册
  • wordpress 手机站目录wordpress 简单