android 按键分析,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(ptr);//获取native层的InputManager

···

status_t status = im->registerInputChannel(env, inputChannel, inputWindowHandle, monitor);

···

}

status_t NativeInputManager::registerInputChannel(JNIEnv* env,

const sp& inputChannel,

const sp& inputWindowHandle, bool monitor) {

return mInputManager->getDispatcher()->registerInputChannel(

inputChannel, inputWindowHandle, monitor);

}

InputDispatcher.cpp

status_t InputDispatcher::registerInputChannel(const sp& inputChannel,

const sp& 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 = 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(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 = android_view_InputChannel_getInputChannel(env,inputChannelObj);

sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);

···

sp 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,

const sp& 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进行通讯,实现按键信息的传递。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值