Android Input输入系统之三:KeyEvent事件分发和上层应用层对事件的接收

本文主要是介绍Android Input输入系统之三:KeyEvent事件分发和上层应用层对事件的接收,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

相关参考:
《Android按键Input KeyEvent》
《Android Input输入系统之一:KeyEvent事件监听及事件分发流程》
《Android Input输入系统之二:KeyEvent注入事件及事件分发流程》
《Android Input输入系统之三:KeyEvent事件分发和上层应用层对事件的接收》
《Android Input输入系统之四:KeyEvent事件中的InputChannel通信》
《Android Input输入系统之五:按键调节音量加减》

在文章《Android Input输入系统之二:KeyEvent注入事件及事件分发流程》中,
InputDispatcherThread使用InputChannel的sendMessage方法发送了按键消息。

那么上层应用层是如何接收到按键消息的呢?

上层的应用在创建窗口的时候addView和addWindow的时候也创建了InputChannel,并且在WindowInputEventReceiver进行接收。

可以说,每个窗口的创建都需要经过addWindow().
\frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {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) {//省略一部分代码//...final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}//省略一部分代码//...}
}

我们可以看到有一个InputChannel outInputChannel的参数。并且执行了openInputChannel,打开这个outInputChannel。

/** A window in the window manager. */
public class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {void openInputChannel(InputChannel outInputChannel) {if (mInputChannel != null) {throw new IllegalStateException("Window already has an input channel.");}String name = getName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);mInputChannel = inputChannels[0];mClientChannel = inputChannels[1];mInputWindowHandle.inputChannel = inputChannels[0];if (outInputChannel != null) {mClientChannel.transferTo(outInputChannel);mClientChannel.dispose();mClientChannel = null;} else {// If the window died visible, we setup a dummy input channel, so that taps// can still detected by input monitor channel, and we can relaunch the app.// Create dummy event receiver that simply reports all events as handled.mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);}mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);}@OverrideString getName() {return Integer.toHexString(System.identityHashCode(this))+ " " + getWindowTag();}CharSequence getWindowTag() {CharSequence tag = mAttrs.getTitle();if (tag == null || tag.length() <= 0) {tag = mAttrs.packageName;}return tag;}
}

可以发现,使用openInputChannelPair创建了一对InputChannel实例,名称就是window的title或者包名。

参数outInputChannel的来源,是在frameworks\base\core\java\android\view\ViewRootImpl.java的setView中创建了
mInputChannel = new InputChannel();并通过mWindowSession.addToDisplay()中传给WindowManagerService中的addWindow()

frameworks\base\core\java\android\view\ViewRootImpl.java
/*** We have one child*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {//省略一部分代码//...if (mInputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}//省略一部分代码//...
}

接下来,ViewRootImpl中的setView(),做了两件事:
1.创建了一个InputQueue,它表示一个输入事件队列并设置事件回调。
2.创建一个WindowInputEventReceiver的实例负责读取事件。

WindowInputEventReceiver是ViewRootImpl的内部类,也是InputEventReceiver的子类:
final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}
}

frameworks\base\core\java\android\view\InputEventReceiver.java
//创建一个input事件接收器并绑定到指定的inputChannel中

public InputEventReceiver(InputChannel inputChannel, Looper looper) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null");}if (looper == null) {throw new IllegalArgumentException("looper must not be null");}mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);mCloseGuard.open("dispose");
}

关键是调用了nativeInit方法,这个方法是一个native方法,实现在frameworks\base\core\jni\android_view_InputEventReceiver.cpp文件中。

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);if (inputChannel == NULL) {jniThrowRuntimeException(env, "InputChannel is not initialized.");return 0;}sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == NULL) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);//调用NativeInputEventReceiver的initialize()status_t status = receiver->initialize();if (status) {String8 message;message.appendFormat("Failed to initialize input event receiver.  status=%d", status);jniThrowRuntimeException(env, message.string());return 0;}receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}

NativeInputEventReceiver定义在android_view_InputEventReceiver中,父类是LooperCallback。

这个方法中构造了NativeInputEventReceiver类的实例,并调用其initialize方法:

status_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, NULL);} else {mMessageQueue->getLooper()->removeFd(fd);}}
}

我们传入的Looper是我们主线程的Looper,这里调用了我们主线程Looper的getFd方法,addFd()是个关键的方法。

addFd(),设置了callback回调,使用epoll_ctl监听文件描述符,当收到事件后,回调NativeInputEventReceiver的handleEvent();

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {// This error typically occurs when the publisher has closed the input channel// as part of removing a window or finishing an IME session, in which case// the consumer will soon be disposed as well.if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  ""events=0x%x", getInputChannelName().c_str(), events);}return 0; // remove the callback}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) {for (size_t i = 0; i < mFinishQueue.size(); i++) {const Finish& finish = mFinishQueue.itemAt(i);status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);if (status) {mFinishQueue.removeItemsAt(0, i);if (status == WOULD_BLOCK) {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",getInputChannelName().c_str(), i, mFinishQueue.size());}return 1; // keep the callback, try again later}ALOGW("Failed to send finished signal on channel '%s'.  status=%d",getInputChannelName().c_str(), status);if (status != DEAD_OBJECT) {JNIEnv* env = AndroidRuntime::getJNIEnv();String8 message;message.appendFormat("Failed to finish input event.  status=%d", status);jniThrowRuntimeException(env, message.string());mMessageQueue->raiseAndClearException(env, "finishInputEvent");}return 0; // remove the callback}}if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",getInputChannelName().c_str(), mFinishQueue.size());}mFinishQueue.clear();setFdEvents(ALOOPER_EVENT_INPUT);return 1;}ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  ""events=0x%x", getInputChannelName().c_str(), events);return 1;
}

其中调用了consumeEvents()方法。consumeEvents()实则调用了InputEventReceiver.java中的dispatchInputEvent()

// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event, displayId);
}public void onInputEvent(InputEvent event, int displayId) {finishInputEvent(event, false);
}WindowInputEventReceiver复写了onInputEvent,实际调用的是WindowInputEventReceiver中的onInputEvent()方法。@Override
public void onInputEvent(InputEvent event, int displayId) {enqueueInputEvent(event, this, 0, true);
}void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {adjustInputEventForCompatibility(event);QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);// Always enqueue the input event in order, regardless of its time stamp.// We do this because the application or the IME may inject key events// in response to touch events and we want to ensure that the injected keys// are processed in the order they were received and we cannot trust that// the time stamp of injected events are monotonic.QueuedInputEvent last = mPendingInputEventTail;if (last == null) {mPendingInputEventHead = q;mPendingInputEventTail = q;} else {last.mNext = q;mPendingInputEventTail = q;}mPendingInputEventCount += 1;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);if (processImmediately) {doProcessInputEvents();} else {scheduleProcessInputEvents();}
}

调用了doProcessInputEvents()

void doProcessInputEvents() {// Deliver all pending input events in the queue.while (mPendingInputEventHead != null) {QueuedInputEvent q = mPendingInputEventHead;mPendingInputEventHead = q.mNext;if (mPendingInputEventHead == null) {mPendingInputEventTail = null;}q.mNext = null;mPendingInputEventCount -= 1;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);long eventTime = q.mEvent.getEventTimeNano();long oldestEventTime = eventTime;if (q.mEvent instanceof MotionEvent) {MotionEvent me = (MotionEvent)q.mEvent;if (me.getHistorySize() > 0) {oldestEventTime = me.getHistoricalEventTimeNano(0);}}mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);deliverInputEvent(q);}// We are done processing all input events that we can process right now// so we can clear the pending flag immediately.if (mProcessInputEventsScheduled) {mProcessInputEventsScheduled = false;mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);}
}

处理逻辑在deliverInputEvent()中,

private void deliverInputEvent(QueuedInputEvent q) {Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",q.mEvent.getSequenceNumber());if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);}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);}
}

之前setView()中时,创建了mSyntheticInputStage,

// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
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);mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;

在这里:
stage = mSyntheticInputStage;
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
stage.deliver(q);

所以,这里又三个阶段,stage可能是ViewPostImeInputStage()对象,也有可能是EarlyPostImeInputStage(),也有可能是ViewPreImeInputStage();这里分析其中一个ViewPreImeInputStage()

这几个类中都有onProcess();并且都是继承了InputStage

abstract class InputStage {/*** Delivers an event to be processed.*/public final void deliver(QueuedInputEvent q) {if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {forward(q);} else if (shouldDropInputEvent(q)) {finish(q, false);} else {apply(q, onProcess(q));}}
}/*** Delivers pre-ime input events to the view hierarchy.* Does not support pointer events.*/
final class ViewPreImeInputStage extends InputStage {public ViewPreImeInputStage(InputStage next) {super(next);}@Overrideprotected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q);}return FORWARD;}private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event = (KeyEvent)q.mEvent;if (mView.dispatchKeyEventPreIme(event)) {return FINISH_HANDLED;}return FORWARD;}
}

deliver(q),会调用每个InputStage子类的onProcess(),

执行processKeyEvent()

private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event = (KeyEvent)q.mEvent;if (mView.dispatchKeyEventPreIme(event)) {return FINISH_HANDLED;}return FORWARD;
}

mView.dispatchKeyEventPreIme(event),分发了事件到View中。

dispatchKeyEventPreIme是在输入法弹出界面时,响应的方法。

当到了ViewPostImeInputStage阶段,执行的processKeyEvent(),会调用:
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}

将按键消息传递到view结构。

总结

其中,最重要的流程还是NativeInputEventReceiver中的handleEvent()回调。

具体源码还得研究Looper是如何进行回调的。

另外:
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}

Looper中进行监听的文件描述符getFd();
int fd = mInputConsumer.getChannel()->getFd();

其中mInputConsumer.getChannel()来自与nativeInit()中的:
sp inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);

流程图:
在这里插入图片描述
在这里插入图片描述

图片来源于:

参考资料:
https://www.jianshu.com/p/f05d6b05ba17

这篇关于Android Input输入系统之三:KeyEvent事件分发和上层应用层对事件的接收的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/733586

相关文章

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Windows 系统下 Nginx 的配置步骤详解

《Windows系统下Nginx的配置步骤详解》Nginx是一款功能强大的软件,在互联网领域有广泛应用,简单来说,它就像一个聪明的交通指挥员,能让网站运行得更高效、更稳定,:本文主要介绍W... 目录一、为什么要用 Nginx二、Windows 系统下 Nginx 的配置步骤1. 下载 Nginx2. 解压

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

Python使用pynput模拟实现键盘自动输入工具

《Python使用pynput模拟实现键盘自动输入工具》在日常办公和软件开发中,我们经常需要处理大量重复的文本输入工作,所以本文就来和大家介绍一款使用Python的PyQt5库结合pynput键盘控制... 目录概述:当自动化遇上可视化功能全景图核心功能矩阵技术栈深度效果展示使用教程四步操作指南核心代码解析

ubuntu20.0.4系统中安装Anaconda的超详细图文教程

《ubuntu20.0.4系统中安装Anaconda的超详细图文教程》:本文主要介绍了在Ubuntu系统中如何下载和安装Anaconda,提供了两种方法,详细内容请阅读本文,希望能对你有所帮助... 本文介绍了在Ubuntu系统中如何下载和安装Anaconda。提供了两种方法,包括通过网页手动下载和使用wg

ubuntu系统使用官方操作命令升级Dify指南

《ubuntu系统使用官方操作命令升级Dify指南》Dify支持自动化执行、日志记录和结果管理,适用于数据处理、模型训练和部署等场景,今天我们就来看看ubuntu系统中使用官方操作命令升级Dify的方... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。

使用Python和SQLAlchemy实现高效的邮件发送系统

《使用Python和SQLAlchemy实现高效的邮件发送系统》在现代Web应用中,邮件通知是不可或缺的功能之一,无论是订单确认、文件处理结果通知,还是系统告警,邮件都是最常用的通信方式之一,本文将详... 目录引言1. 需求分析2. 数据库设计2.1 User 表(存储用户信息)2.2 CustomerO