之所以Activity能够接收到来自底层的Keyevent是因为在显示时创建了一个InputChannel。当底层有按键事件时就会通过这个InputChannel传递上来。
在启动一个activity时将会调用ViewRootImpl.setView()函数。下面将这个函数中的主要部分给出:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
····
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//这个对应client端,将InputEvent传递给view
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
} catch (RemoteException e) {}
···
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//mInputChannel注册到native层
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
···
}
上面段代码中主要进行了三个动作:
1.创建一个Java层的InputChannel,这个InputChannel后面用来创建两个socket,一个是client端,app使用它接收按键信息;一个是server端,底层将按键信息写入server端
2.调用WindowSession.addToDisplay,向系统中添加session
3.注册WindowInputEventReceiver,当client端有消息时,通知view来消息。
下面看一下WindowSession.addToDisplay的实现
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel);
}
这里面的mService实际上就是WindowManagerService
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();//这个就是创建一个关于这个view的字符串
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//调用InputChannel创建两个socket
win.setInputChannel(inputChannels[0]);//server channel
inputChannels[1].transferTo(outInputChannel);//client channel 将Native层的对象转换成Java层的对象
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);//将server socket注册到底层
}
}
下面看一下这个registerInputChannel函数的实现。
InputManangerService.java
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
com_android_server_iinput_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
jint ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);//获取native层的InputManager
···
status_t status = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor);
···
}
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
//检查这个channel是否存在
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
//将这个fd注册到looper中。用来接收来自framework层的消息,并且调用handleReceiveCallback进行处理
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
至此就已经将Server端的socket注册到了Looper中了。至于addFd函数的实现,请参考Native层的Looper.cpp。
Client端socket注册实在创建WindowInputEventReceiver对象时注册的。
下面看一下 WindowInputEventReceiver类
ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {}
InputEventReceiver.java
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 JNI函数。
android_view_InputEventReceiver.cpp
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
//将java层的对象转换成c中的对象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
···
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
}
android_view_InputEventReceiver.cpp
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue) :
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel), mMessageQueue(messageQueue),
mBatchedInputEventPending(false), mFdEvents(0) {
}
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);
}
}
}
上面就是将mInputConsumer的中Channel的fd注册到looper中。其中Channel就是传递下来的client端的socket。至此client端的socket也已经注册完毕。后面就是利用Server/Client socket进行通讯,实现按键信息的传递。