東莞高端品牌網(wǎng)站建設營銷推廣的工具有哪些
本文基于Android12。
InputChannel表示其他進程通過文件描述符傳遞輸入事件到View的通道,因為需要跨進程傳輸,實現(xiàn)了Parcelable序列化接口,所以也能夠理解Java層的InputChannel后面為什么使用copyTo()方法初始化。
輸入事件的接收方是View,所以InputChannel的創(chuàng)建肯定和View的創(chuàng)建流程有關,關于View的創(chuàng)建流程參考:https://blog.csdn.net/qq_36063677/article/details/129908973。
一、InputChannel初始化
ViewRootImpl在setView()方法實例化了Java層的InputChannle對象,但是正如ViewRootImpl創(chuàng)建的mSurface對象一樣,這只是一個引用,一個“空的”對象,后續(xù)在WindowManagerService經(jīng)過實際的初始化,再通過copyTo()方法拷貝到InputChannel引用中。
// ViewRootImpl.javapublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {// 1.創(chuàng)建 inputChannel引用。InputChannel inputChannel = null;if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel();}// 2.傳遞引用給mWindowSession,inputChannel在WMS中被初始化,并通過copyTo()全拷貝到inputChannel引用。res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);// 3.通過inputChannel創(chuàng)建WindowInputEventReceiver對象,接收處理輸入事件。ALOOPER_EVENT_INPUTif (inputChannel != null) {mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());}}
setView()方法中關于InputChannel的操作主要有3步:
- 創(chuàng)建 inputChannel引用。就是實例化InputChannel引用,InputChannel構(gòu)造方法是個空方法,所以什么實際操作都沒有做。
- 傳遞引用給mWindowSession,inputChannel在WMS中被初始化,并通過copyTo()全拷貝到inputChannel引用。
- 通過inputChannel創(chuàng)建WindowInputEventReceiver對象,接收處理輸入事件。
我們從第2步開始分析。
1.1 openInputChannle
mWindowSession將inputChannel引用傳遞給WMS.addWindow()方法:
// WindowManagerService.java public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls, Rect outAttachedFrame,float[] outSizeCompatScale) {final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (openInputChannels) {win.openInputChannel(outInputChannel);}}
addWindow()創(chuàng)建了WindowState對象,調(diào)用其openInputChannel(outInputChannel)方法。
// WindowState.java void openInputChannel(InputChannel outInputChannel) {if (mInputChannel != null) {String name = getName();mInputChannel = mWmService.mInputManager.createInputChannel(name);if (outInputChannel != null) {mInputChannel.copyTo(outInputChannel);}}}
WindowState通過InputMangerService創(chuàng)建InputChannel,經(jīng)過NativeInputManagerService類的native方法createInputChannel(name),最終到InputDispatcher::createInputChannel(name)方法實際創(chuàng)建。
1.1.1 InputDispatcher::createInputChannel
// InputDispatcher.h // All registered connections mapped by input channel token.std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByTokenGUARDED_BY(mLock);// InputDispatcher.cppResult<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {std::unique_ptr<InputChannel> serverChannel;std::unique_ptr<InputChannel> clientChannel;// 1.通過CPP層的InputChannel創(chuàng)建serverChannel,clientChannelstatus_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);{std::scoped_lock _l(mLock);const sp<IBinder>& token = serverChannel->getConnectionToken();// 2.得到serverChannel的文件描述符 fdint fd = serverChannel->getFd();// 3.將connection對象添加到 mConnectionsByToken管理sp<Connection> connection =new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);mConnectionsByToken.emplace(token, connection);std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,this, std::placeholders::_1, token);mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid);// 4.監(jiān)聽serverChannel的文件描述符 fd,當有事件發(fā)生時,回調(diào)callbackmLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);}// 5.喚醒mLoopermLooper->wake();return clientChannel;}
InputDispatcher主要做了5件事:
- 通過CPP層的InputChannel創(chuàng)建serverChannel,clientChannel,最后返回clientChannel給Java層的InputChannel,到這里Java層InputChannel才被初始化完成,返回創(chuàng)建WindowInputEventReceiver對象。
- 得到serverChannel的文件描述符 fd。
- 將connection對象添加到 mConnectionsByToken管理,mConnectionsByToken定義在InputDispatcher.h文件,管理所有連接的InputChannel對象,map的key是token,又是使用Binder對象作為token。inputflinger文章中(https://blog.csdn.net/qq_36063677/article/details/130475299)4.3.3小節(jié)InputDispatcher在分發(fā)事件時就是通過這個mConnectionsByToken獲取到具體的connection,發(fā)送事件。
- 監(jiān)聽serverChannel的文件描述符 fd,ALOOPER_EVENT_INPUT表示為要監(jiān)聽的文件類型,當有事件發(fā)生時,回調(diào)callback方法handleEvent(),也就是InputDispatcher::handleReceiveCallback()。LooperEventCallback繼承了LooperCallback類,LooperCallback是Looper監(jiān)聽文件描述符回調(diào)方法的標準類,當文件描述符fd上有事件到來時,LooperCallback的handleEvent()方法會被執(zhí)行,關于Looper->addFd()更多細節(jié)參考:https://blog.csdn.net/chwan_gogogo/article/details/46953563
- 喚醒mLooper,mLooper在InputDispatcher構(gòu)造方法中被初始化,mLooper = new Looper(false);。
查看cpp層InputChannel::openInputChannelPair()具體細節(jié):
1.1.2 InputChannel::openInputChannelPair
// InputTransport.cppstatus_t InputChannel::openInputChannelPair(const std::string& name,std::unique_ptr<InputChannel>& outServerChannel,std::unique_ptr<InputChannel>& outClientChannel) {int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {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));sp<IBinder> token = new BBinder();std::string serverChannelName = name + " (server)";android::base::unique_fd serverFd(sockets[0]);outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);std::string clientChannelName = name + " (client)";android::base::unique_fd clientFd(sockets[1]);outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);return OK;}std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,android::base::unique_fd fd, sp<IBinder> token) {const int result = fcntl(fd, F_SETFL, O_NONBLOCK);return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));}InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token): mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {}
- InputChannel使用socket通信,openInputChannelPair()方法創(chuàng)建兩個socke對象,一個client,一個server。socketpair()函數(shù)用于創(chuàng)建一對無名的、相互連接的套接子。如果函數(shù)成功,則返回0,創(chuàng)建好的套接字分別是sv[0]和sv[1],所以outServerChannel和outClientChannel這兩個socket在創(chuàng)建的時候就是相互連接的,之后只需要在各自的fd中通過send()發(fā)送數(shù)據(jù)就好。
- setsockopt()設置socket屬性,在<sys/socket.h>頭文件中聲明,實例化一個BBinder()對象作為token使用,這里outServerChannel和outClientChannel都使用的是同一個token。
- InputChannel::create()通過fcntl()設置socket為非阻塞類型,fcntl()方法可以改變已打開的文件性質(zhì)。
- 封裝InputChannel對象。
InputChannel初始化過程終于結(jié)束了,那么1.1.1小節(jié)中mLooper監(jiān)聽的serverChannel的文件描述符 fd什么時候會觸發(fā)呢?
答案在上一篇inputflinger文章中(https://blog.csdn.net/qq_36063677/article/details/130475299)4.3.3小節(jié)。
二、InputChannel發(fā)送事件
InputDispatcher在接收到事件后,InputDispatcher::dispatchEventLocked()從mConnectionsByToken變量中通過token獲取到Connection對象,最終在4.3.4.2 小節(jié)startDispatchCycleLocked()方法調(diào)用
connection->inputPublisher.publishMotionEvent()發(fā)送輸入事件,這里還是以Motion事件為例:
// InputTransport.cppstatus_t InputPublisher::publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,int32_t edgeFlags, int32_t metaState, int32_t buttonState,MotionClassification classification, const ui::Transform& transform, float xPrecision,float yPrecision, float xCursorPosition, float yCursorPosition,const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,uint32_t pointerCount, const PointerProperties* pointerProperties,const PointerCoords* pointerCoords) {InputMessage msg;msg.header.type = InputMessage::Type::MOTION;msg.header.seq = seq;msg.body.motion.eventId = eventId;msg.body.motion.deviceId = deviceId;msg.body.motion.source = source;//.......return mChannel->sendMessage(&msg);}
mChannel是之前創(chuàng)建的InputChannel對象serverChannel,查看sendMessage(&msg)方法:
// InputTransport.cppstatus_t InputChannel::sendMessage(const InputMessage* msg) {const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);ssize_t nWrite;do {nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);return OK;}
調(diào)用socket的send()函數(shù)發(fā)送數(shù)據(jù)。
至此輸入事件通過socket發(fā)送出去了,InputDispatcher執(zhí)行回調(diào)LooperEventCallback,那么事件又是如何被接收的呢?
三、InputEventReceiver
ViewRootImpl在setView()方法實例化InputChannel后,作為參數(shù)初始化WindowInputEventReceiver對象,WindowInputEventReceiver繼承InputEventReceiver類。
3.1 WindowInputEventReceiver
WindowInputEventReceiver也在ViewRootImpl中定義:
// ViewRootImpl.javafinal class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);} @Overridepublic void onInputEvent(InputEvent event) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");List<InputEvent> processedEvents;try {processedEvents =mInputCompatProcessor.processInputEventForCompatibility(event);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}if (processedEvents != null) {if (processedEvents.isEmpty()) {// InputEvent consumed by mInputCompatProcessorfinishInputEvent(event, true);} else {for (int i = 0; i < processedEvents.size(); i++) {enqueueInputEvent(processedEvents.get(i), this,QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);}}} else {enqueueInputEvent(event, this, 0, true);}}}// InputEventReceiver.javapublic InputEventReceiver(InputChannel inputChannel, Looper looper) {mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),mInputChannel, mMessageQueue);mCloseGuard.open("InputEventReceiver.dispose");}
WindowInputEventReceiver構(gòu)造方法中將參數(shù)傳遞給了父類InputEventReceiver,實現(xiàn)了onInput()方法,后續(xù)接收到事件后通過這個方法處理Java層的分發(fā)邏輯,Eventlooper是ViewRoolImpl的當前線程Looper.myLooper(),也就是主線程,InputEventReceiver調(diào)用nativeInit()繼續(xù)下一步操作,創(chuàng)建NativeInputEventReceiver。
3.2 NativeInputEventReceiver
NativeInputEventReceiver定義在JNI文件中,方便后續(xù)回調(diào)JAVA方法。
// android_view_InputEventReceiver.cppstatic jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {// 獲取InputChannelstd::shared_ptr<InputChannel> inputChannel =android_view_InputChannel_getInputChannel(env, inputChannelObj); // 獲取messageQueuesp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);// 1. 實例化NativeInputEventReceiversp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);// 2. receiver->initialize()status_t status = receiver->initialize(); receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get())}
nativeInit()主要做了兩件事,實例化NativeInputEventReceiver,并且調(diào)用其initialize()方法。
3.2.1 NativeInputEventReceiver實例化
// android_view_InputEventReceiver.cppNativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,const sp<MessageQueue>& messageQueue): mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),mInputConsumer(inputChannel),mMessageQueue(messageQueue),mBatchedInputEventPending(false),mFdEvents(0) {}
NativeInputEventReceiver構(gòu)造方法中持有Java層InputEventRecevier對象的引用mReceiverWeakGlobal, 將inputChannel作為參數(shù)實例化mInputConsumer對象。
InputConsumer類定義在InputTransport.h頭文件,從InputChannel消費事件。
// InputTransport.cppInputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel): mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
client端的inputChannel被賦值給InputConsumer的mChannel變量。
回到NativeInputEventReceiver初始化流程中。
3.2.2 NativeInputEventReceiver::initialize
// android_view_InputEventReceiver.cppstatus_t NativeInputEventReceiver::initialize() {setFdEvents(ALOOPER_EVENT_INPUT);return OK;}void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {mFdEvents = events;int fd = mInputConsumer.getChannel()->getFd();if (events) {mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);} else {mMessageQueue->getLooper()->removeFd(fd);}}}
initialize()監(jiān)聽client InputChannel的文件描述符 fd,文件類型文件類型和發(fā)送端一樣,也是ALOOPER_EVENT_INPUT。在上述1.1.1小節(jié)中我們知道后續(xù)會回調(diào)LooperCallback的handleEvent()方法,NativeInputEventReceiver也繼承了LooperCallback類,實現(xiàn)了自己的handleEvent():
3.3 handleEvent
// android_view_InputEventReceiver.cppint 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, nullptr);mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;}if (events & ALOOPER_EVENT_OUTPUT) {const status_t status = processOutboundEvents();if (status == OK || status == WOULD_BLOCK) {return KEEP_CALLBACK;} else {return REMOVE_CALLBACK;}}return KEEP_CALLBACK;}
handleEvent只是對事件類型進行區(qū)分,如果是ALOOPER_EVENT_INPUT類型,詳細具體到處理流程在consumeEvents()方法。
3.4 consumeEvents
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {for (;;) {uint32_t seq;// 1.InputConsumer從socket讀取封裝事件InputEvent* inputEvent;status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);// 2.區(qū)分事件類型,封裝為Java層的InputEvent對象jobject inputEventObj;switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<KeyEvent*>(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION: {MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {*outConsumedBatch = true;}inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}}// 3.調(diào)用Java層InputEventReceiver對象的dispatchInputEvent方法,參數(shù)為seq,inputEventObjif (inputEventObj) {env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);env->DeleteLocalRef(inputEventObj);}}}
consumeEvents()處理了事件接收的主要流程:
- 啟動一個死循環(huán),不斷從InputConsumer獲取事件,賦值到inputEvent引用,InputConsumer持有客戶端InputChannel的引用,從客戶端socket讀取數(shù)據(jù),將數(shù)據(jù)解析封裝成InputEvent對象。
- 將InputEvent類型進行區(qū)分,具體共有AINPUT_EVENT_TYPE_KEY,AINPUT_EVENT_TYPE_MOTION,AINPUT_EVENT_TYPE_FOCUS,AINPUT_EVENT_TYPE_CAPTURE,AINPUT_EVENT_TYPE_DRAG,AINPUT_EVENT_TYPE_TOUCH_MODE六種類型,對于Motion事件,封裝成Java層的MotionEvent對象。
- receiverObj指向NativeInputEventReceiver實例化時傳遞過來的Java層InputEventReceiver引用,調(diào)用Java層InputEventReceiver對象的dispatchInputEvent方法,參數(shù)為seq,inputEventObj,至此回到了Java層的處理流程。
在分析Java層的處理流程之前,先看下InputConsumer是如何讀取socket數(shù)據(jù)解析成InputEvent對象的。
3.4.1 InputConsumer.consume
// InputTransport.cppstatus_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {while (!*outEvent) {status_t result = mChannel->receiveMessage(&mMsg);switch (mMsgmMsg.header.type) {case InputMessage::Type::KEY: {KeyEvent* keyEvent = factory->createKeyEvent();if (!keyEvent) return NO_MEMORY;initializeKeyEvent(keyEvent, &mMsg);*outSeq = mMsg.header.seq;*outEvent = keyEvent;break;}case InputMessage::Type::MOTION: {MotionEvent* motionEvent = factory->createMotionEvent();if (!motionEvent) return NO_MEMORY;updateTouchState(mMsg);initializeMotionEvent(motionEvent, &mMsg);*outSeq = mMsg.header.seq;*outEvent = motionEvent;break;}}}return OK;}
mChannel是NativeInputEventReceiver傳遞過去的客戶端InputChannel對象,InputConsumer從mChannel讀取消息,在上述第二小節(jié):InputChannel發(fā)送事件 可知,發(fā)送的InputMessage對象封裝了mMsg.header.type等事件類型消息,這里通過事件類型消息進行區(qū)分,封裝成具體對應的輸入事件,如KeyEvent,MotionEvent等,賦值給outEvent。
InputChannel通過recv()方法接收socket消息:
// InputTransport.cppstatus_t InputChannel::receiveMessage(InputMessage* msg) {ssize_t nRead;do {nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);} while (nRead == -1 && errno == EINTR);}
3.4.2 dispatchInputEvent
事件的讀取解析流程到此結(jié)束了,接下來看看InputReceiver是如何分發(fā)事件的。
// InputEventReceiver.java// Called from native code.@SuppressWarnings("unused")@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}
InputEventReceiver調(diào)用onInputEvent(event)開始處理事件的分發(fā)流程,上述3.1小節(jié)可知,onInputEvent方法在WindowInputEventReceiver類中被覆蓋。Java層的事件分發(fā)流程參考:https://editor.csdn.net/md/?articleId=130476234