之所以Activity能夠接收到來自底層的Keyevent是因爲在顯示時創建了一個InputChannel。當底層有按鍵事件時就會通過這個InputChannel傳遞上來。
View的初始化
在啓動一個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註冊到底層
}
}
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註冊
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進行通訊,實現按鍵信息的傳遞。