5Activity注册InputChannel和处理Event
5.1 注册InputChannel
InputChannel的涉及面比较广,我们知道,仅有android手机当前界面的activity能响应键盘/触摸等事件,那InputChannel肯定和界面刚准备显示的流程代码有关系,在创建界面时,会调用ViewRootImpl的setView方法,最后会调用WMS的addWindow方法。
在android 6.0 中, 在WallpaperService中的updateSurface方法中创建InputChannel对象然后调用Session的addToDisplay方法,
mInputChannel = new InputChannel();
if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
mInputChannel) < 0) {
最后也会调用WMS的addWindow方法,至于界面的绘制流程以后再详细的跟踪。
暂时就先从这儿看起,
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);
}
registerInputChannel方法上面已经分析过了,主要分析openInputChannelPair方法
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
•••
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
•••
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
•••
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
首先调用openInputChannelPair方法创建2个InputChannel,一个客户端,一个服务端。然后创建2个对应的NativeInputChannel对象。
所以addWindow中的inputChannels变量中inputChannels[0] 代表服务端, inputChannels[1] 代表客户端。
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;
}
openInputChannelPair为2个InputChannel对象都创建了sockets。
服务端的InputChannel通过registerInputChannel方法注册到系统服务进程中,这样客户端和服务端的InputChannel就可以通过sockets通信了。
4.1 节中有个问题是应用在哪儿接收Event呢.
在ViewRootImpl的setView方法中调用WMS的addWindow后,会新建
WindowInputEventReceiver对象,总体流程图如下:
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
Looper.myLooper是应用程序的主线程。
直接看Looper.cpp的addFd方法,将fd标志添加到epoll监控中,并且将指向NativeInputEventReceiver的request添加到mRequests列表中。
因此,应用程序可以收到socket发送的Event消息,并且可以回调NativeInputEventReceiver中的方法。
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request);
详细的回调流程图如下:
消息循环会调用搭配looper对象的pollonce方法,
pollInner方法调用epoll_wait等待epoll事件的到来,事件到来后就调用NativeInputEventReceiver的handleEvent方法。
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
•••
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.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);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
result = POLL_CALLBACK;
}
•••
consumeEvents 首先会调用InputConsumer的consume方法来接收输入Event,
然后通过JNI调用InputEventReceiver的dispatchInputEvent等方法。
InputEvent* inputEvent;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
•••
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
consume方法获取Event,看到了吧,刚好和4.1 小节中sendMessage方法相对应
status_t result = mChannel->receiveMessage(&mMsg);
具体实现如下,调用系统的recv方法。
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;
}
最后, ViewRootImpl的enqueueInputEvent方法如下,
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
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();
}
}
Event 兜兜转转,转转兜兜,真艰难啊。
至此,Event终于从底层从系统走到应用中了。