系列文章目录
目录
2.3.3 NativeInputEventReceiver::handleEvent
2.3.4 NativeInputEventReceiver::consumeEvents
2.3.14 NativePreImeInputStage.onProcess
2.3.15 ViewPreImeInputStage.onProcess
2.3.16 ImeInputStage.onProcess
2.3.17 EarlyPostImeInputStage.onProcess
2.3.18 NativePostImeInputStage.onProcess
2.3.19 ViewPostImeInputStage.onProcess
2.3.20 ViewPostImeInputStage.processKeyEvent
2.4.1 DecorView.dispatchKeyEvent
2.4.1.1.1 Activity.dispatchKeyEvent
2.4.1.1.2 PhoneWindow.superDispatchTouchEvent
2.4.1.1.3.DecorView.superDispatchTouchEvent
2.4.1.1.4 ViewGroup.dispatchKeyEvent
2.4.1.1.5 View.dispatchKeyEvent
2.4.1.4.8 ViewRootImpl.finishInputEvent
2.4.1.4.9 InputEventReceiver.finishInputEvent
2.4.1.4.10 nativeFinishInputEvent
2.4.1.4.13 sendUnchainedFinishedSignal
2.4.1.4.14 InputChannel::sendMessage
2.4.1.4.15 InputDispatcher::handleReceiveCallback
2.4.1.4.16 InputPublisher::receiveFinishedSignal
2.4.1.4.17 InputChannel::receiveMessage
2.4.1.4.18 finishDispatchCycleLocked
2.4.1.4.19 runCommandsLockedInterruptible
2.4.1.4.20 doDispatchCycleFinishedLockedInterruptible
2.4.1.2 情况二:当前应用没有焦点窗口,但存在Activity兜底处理的流程分析
2.4.1.2.1 Activity.dispatchKeyEvent
2.4.1.3 情况三:当前无应用,音量加减交给PhoneWindow处理流程分析
2.4.1.3.1 PhoneWindow.onKeyDown
1.简介
通过前文我们知道了IMS会通过已经注册好的socket发送按键事件到应用程序端,那么本篇便来讲解一下应用程序端的流程。
1.1 APP程序是如何接受普通按键事件?
主要步骤:
1.控件(如activity,View)实现View.OnKeyListener接口,并实现其方法onKey
2.控件调用其setOnKeyListener方法,传入监听按键响应的listener
3.按下按键,此时onKey方法会执行。
1.1.1 App端代码案例
public class MainActivity extends AppCompatActivity implements View.OnKeyListener {
//MainActivity实现View.OnKeyListener
Button mActivitytest;
EditText mEditText;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initview();
}
//如果目标 View 没有消费掉此次事件(即返回值为 false)则会调用 Activity 的 onKeyDown或者onKeyup来处理。
public boolean onKeyDown(int keyCode, KeyEvent event) {
int keycode = event.getKeyCode();
mEditText.setText("onKeyDown按键监听到了key ="+keycode+ "按键按下");
return true;
}
public boolean onKeyUp(int keyCode, KeyEvent event)
{
int keycode = event.getKeyCode();
mEditText.setText("onKeyUp按键监听到了key ="+keycode+ "按键抬起");
return true;
}
protected void initview()
{
mEditText = (EditText) findViewById(R.id.onkeytest);
mEditText.setOnKeyListener(this);
}
@Override
//应用程序消费按键事件的地方
public boolean onKey(View view, int i, KeyEvent keyEvent) {
int keycode = keyEvent.getKeyCode();
int action = keyEvent.getAction();
if(action == 0)
{
mEditText.setText("onKey按键监听到了key ="+keycode+ "按键按下");
}else if(action == 1)
{
mEditText.setText("onKey按键监听到了key ="+keycode+ "按键抬起");
}
return true;
}
}
布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/onkeytest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="50dp"/>
<Button
android:id="@+id/Activitytest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始测试"
android:layout_weight="1"
android:textSize="50dp"/>
</LinearLayout>
</LinearLayout>
1.1.2 测试结果演示
1.当我们只打开应用(重写了Activity的onKeyDown和onKeyup函数),而不点击EditText的区域时候,即EditText此时不是焦点,当按下音量减少按键,结果如下。即此时走的是Activity的兜底的onKeyDown和onKeyup函数。
2.当我们只打开应用,然后点击EditText的区域时候,即EditText此时是焦点,当按下音量减少按键,结果如下。即此时走的是EditText的onKey函数。
3.当我们不打开应用或者不重写Activity的onKeyDown和onKeyup函数时,按下音量减少按键。如图:
2.流程分析
2.1 主要步骤
第一步:当IMS通过socket发送按键事件后,应用端的NativeInputEventReceiver::handleEvent会被调用。
第二步:然后应用端从socket下读取按键事件,然后应用端会调用WindowInputEventReceiver(ViewRootImpl 的内部类)重写的 onInputEvent 方法,此方法就是 java 层事件分发的起点。
第三步:应用端会通过InputStage责任链的方式,将按键事件进行派发,最终会派发到ViewPostImeInputStage.processKeyEvent,这个便是View派发的起点。
第四步:如果有焦点窗口,则View会派发按键事件到焦点窗口的onkey函数,如果无焦点窗口,但重写了Activity的onKeyDown和onKeyup函数,则会交给Activity的onKeyDown和onKeyup函数处理,如果当前无Activity或者不重写Activity的onKeyDown和onKeyup函数时,按下音量减少按键,此时会交给meida服务去减少音量。
第五步:当第四步无论是那种情况,处理完成后都会调用调用InputEventReceiver的finishInputEvent函数通过socket向IMS服务发送此按键事件已经完成处理的消息。此时IMS服务侧的handleReceiveCallback回调函数会被执行。
第六步:IMS服务侧的handleReceiveCallback回调函数执行中会从IMS服务端的socket下读取应用端发送的消息,然后将处理完成的事件从等待队列中删除。
2.2 时序图
(可保存到本地放大观看)
2.3 源码分析
在【android 9】【input】【8.发送按键事件2——InputDispatcher线程】篇中,我们知道当发送普通按键时,IMS最终会通过socket将按键消息发送给应用端,而在【android 9】【input】【9.发送按键事件3——Inputchannel的创建过程】篇中,我们知道应用端会监听来自IMS的消息。
2.3.1 Looper::addFd
上文中我们知道此函数的主要作用为调用epoll_ctl监听此应用的socket,即监听来自IMS的输入事件。
int Looper::addFd(int fd, int ident, int events,
const sp<LooperCallback>& callback, void* data) {
{
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
AutoMutex _l(mLock);
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback; // 是指 NativeInputEventReceiver
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0;
struct epoll_event eventItem;
request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
// 通过 epoll 监听 fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
......
mRequests.add(fd, request); // 该fd 的 request 加入到 mRequests 队列
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
......
mRequests.replaceValueAt(requestIndex, request);
}
}
return 1;
}
2.3.2 pollInner
Looper机制中线程循环调用pollInner函数。(后续本人会分析一篇关于Looper的机制)
int Looper::pollInner(int timeoutMillis)
{
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//当无消息的时候,会阻塞在此处
//处理所有消息
for (int i = 0; i < eventCount; i++)
{
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get())
{
}
else
{
ssize_t requestIndex = mRequests.indexOfKey(fd);//此时mRequests不为空,因为在监听的时候添加了socket的fd和对应的回调对象,
//此时根据socket中的fd找到对应的回调对象。
if (requestIndex >= 0)
{
int events = 0;
if (epollEvents & EPOLLIN)
events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT)
events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR)
events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP)
events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));//往mResponses塞入了事件
}
}
}
Done:;
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++)
{
Response &response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK)//在addFd中ident就是POLL_CALLBACK
{
int fd = response.request.fd;
int events = response.events;
void *data = response.request.data;
int callbackResult = response.request.callback->handleEvent(fd, events, data);//执行回调函数
if (callbackResult == 0)
{
removeFd(fd, response.request.seq);
}
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
void Looper::pushResponse(int events, const Request& request) {
Response response;
response.events = events;
response.request = request;
mResponses.push(response);
}
2.3.3 NativeInputEventReceiver::handleEvent
从上面我们知道,当应用端监听的socket下有输入事件时,此函数会被调用。
主要作用是:
1.应用端调用consumeEvents消费事件数据。
此函数的ALOOPER_EVENT_INPUT代表socket可读,即应用程序要开始分发来自IMS的输入事件。
ALOOPER_EVENT_OUTPUT代表socket可写,即应用程序向IMS发送完成输入事件的处理的消息。
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & ALOOPER_EVENT_INPUT) {//socket可读
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");//调用handleReceiveCallback告诉ims此消息已经被成功消费了。
return status == OK || status == NO_MEMORY ? 1 : 0;
}
/**
if (events & ALOOPER_EVENT_OUTPUT) {//socket可写
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;
}
*/
return 1;
}
2.3.4 NativeInputEventReceiver::consumeEvents
此函数的主要作用是:
1.mInputConsumer的consume()函数从Inputchannel中读取一条InputMessage
2.将c++的inputEvent生成java的事件类型。
3.通过jni调用java层的dispatchInputEvent函数开始分发。
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
//consumeBatches是 false
//outConsumedBatch是空
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
int32_t displayId;
//通边mInputConsumer的consume()函数从Inputchannel中读取一条InputMessage。
//解析为InputEvent后,通过inputEvent参数传出*/
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent, &displayId);
assert(inputEvent);
if (!skipCallbacks) {
/**
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
ALOGW("channel '%s' ~ Receiver object was finalized "
"without being disposed.", getInputChannelName().c_str());
return DEAD_OBJECT;
}
}
*/
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));//将c++的inputEvent生成java的事件类型
break;
default:
assert(false); // InputConsumer should prevent this from ever happening
inputEventObj = NULL;
}
if (inputEventObj) {//通过jni调用dispatchInputEvent函数开始分发inputEventObj
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
displayId);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.",getInputChannelName().c_str());
skipCallbacks = true;
}
}
if (skipCallbacks) {//如果发送异常,则直接发送完成信号
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
2.3.5 InputConsumer::consume
主要作用是:
1.从socket中读取数据并转化为InputMessage结构体的格式。
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
int32_t* displayId) {
*outSeq = 0;
*outEvent = NULL;
*displayId = -1; // Invalid display.
// //获取下一条输入消息。
//循环,直到可以返回事件或没有接收到其他事件为止。
while (!*outEvent) {
/**
if (mMsgDeferred) {//默认false不执行
//mMsg包含上一次调用中尚未处理的有效输入消息。
mMsgDeferred = false;
}*/
else {
// 从inputchannel中接收一个数据
status_t result = mChannel->receiveMessage(&mMsg);
/**
if (result) {//ok是0
// Consume the next batched event unless batches are being held for later.
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
if (*outEvent) {
break;
}
}