Android Activity 接收按鍵原理分析(1)

之所以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進行通訊,實現按鍵信息的傳遞。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章