博观而约取,厚积而薄发。这篇文章主要讲述Android系统源码阅读(12):InputChannel的注册过程相关的知识,希望能为你提供帮助。
android系统源码阅读(12):InputChannel的注册过程
请对照AOSP版本:6.0.1_r50。InputManager可以获得输入事件并分发,Activity需要处理这些输入事件。那么,这两者之间如何建立的连接呢?这就需要InputChannel作为桥梁建立两者之间的通道。
1. ViewRootImpl创建InputChannel这里ViewRoot类已经消失了,由ViewRootImpl替代。Activity在创建时会将自己的DecorView设置给对应的ViewRootImpl。
文章图片
1.1
这一步会创建client的InputChannel,并且将当前启动的Activity的窗口传递给WindowManagerService。
frameworks/base/core/java/android/view/ViewRootImpl.java
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//将view设置为传入的DecorView
mView = view;
//..mAdded = true;
int res;
/* = WindowManagerImpl.ADD_OKAY;
*///新建InputChannel
if ((mWindowAttributes.inputFeatures
&
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//mWindowSession是一个Binder代理对象
//它引用了运行在WindowManagerService中的一个类型为Session的Binder本地对象
//向WindowManagerService添加正在启动的Activity的窗口
//这里还会将InputChannel传递过去
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
//..
} finally {
//..
}
//..if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//将InputChannel和主线程关联起来,在下面会详细讲解
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
}
}
}
1.2
这一步是进程间的请求,从应用进程转到WindowManagerService进程,对应于1.1中的函数addToDisplay。这里会补足一些参数,开始调用下一步函数。
1.3
这一步也是调整一些参数,然后交给WindowManagerService来处理。
1.4
这里会将传入的Window存入Map来进行统一管理,同时创建了一对server/client端的InputChannel。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java :
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {//先对添加的window做一些检查,省略..//创建了一个WindowSatete对象
WindowState 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();
//创建了一个InputChannel对,在1.5中详细讲解
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//一个放在WindowState里作为server端的InputChannel
win.setInputChannel(inputChannels[0]);
//一个转化为client传递过来的outInputChannel
inputChannels[1].transferTo(outInputChannel);
//从上一篇文章中的1.1可知,InputManager作为参数传入
//WindowManagerService的构造函数,并且存放在mInputManager中
//下面章节会详细讲述如何注册server端的InputChannel
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}// From now on, no exceptions or errors allowed!
//将win放入mWindowMap,以client的binder为关键字
mWindowMap.put(client.asBinder(), win);
//将win加入相应的list,省略..mInputMonitor.setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}if (imMayMove) {
moveInputMethodWindowsIfNeededLocked(false);
}assignLayersLocked(displayContent.getWindowList());
// Don‘t do layout here, the window must call
// relayout to be displayed, so we‘ll do it there.if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
}return res;
}
1.5
这一步会将任务交给c++层来处理。
1.6
这一步对应的c++的函数为
android_view_InputChannel_nativeOpenInputChannelPair
,它会创建两个InputChannel,并返回。frameworks/base/core/jni/android_view_InputChannel.cpp :
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
//将java String变为char*
const char* nameChars = env->
GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->
ReleaseStringUTFChars(nameObj, nameChars);
sp<
InputChannel>
serverChannel;
sp<
InputChannel>
clientChannel;
//创建c++层的两个Channel,将在下一步详细讲解
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
jobjectArray channelPair = env->
NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
//创建java的server channel
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
//创建java的client channel
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
//存入java数组
env->
SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->
SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
1.7
注意,这一步真的要创建ChannelPair了。
frameworks/native/libs/input/InputTransport.cpp :
status_t InputChannel::openInputChannelPair(const String8&
name,
sp<
InputChannel>
&
outServerChannel, sp<
InputChannel>
&
outClientChannel) {
int sockets[2];
//这个socketpair建立的是一个可以双向通信的管道,创建一对套接字描述符
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));
//开始new 两个InputChannel
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;
}
socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组s[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在s[1]中写入,然后从s[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从1读取,仍然阻塞;反之亦然。该段解释来自。
这里new了两个InputChannel,该类的构造函数如下:
frameworks/native/libs/input/InputTransport.cpp :
InputChannel::InputChannel(const String8&
name, int fd) :
mName(name), mFd(fd) {
//..
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
//..
}
这里将描述符mFd设置为nonblock。
显然,这里和Android 2.3版本有着很大区别。在旧版本中使用的是匿名共享内存和两个pipe来实现双向的通信。显然,新版本利用的linux系统的新机制,更为简洁高效。
以上步骤实在新Activity建立,窗口开始创建时执行的。这里主要就让WindowManagerService建立一对InputChannel,将Activity的Window和InputDispatcher建立起连接,从而传输输入事件。
2. Server端注册InputChannel在1.4中,创建了一对InputChannel,其中Server端的InputChannel会注册进InputManagerService。
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
文章图片
2.1
这一步就检验了一下传入的InputChannel是否为空。下面一言不合就开始调用native函数。
2.2
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
//将指针转化为NativeInputManager
NativeInputManager* im = reinterpret_cast<
NativeInputManager*>
(ptr);
//将java的InputChannel对象转化为一个c++层的InputChannel对象
sp<
InputChannel>
inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
//将InputWindowHandle转化为c++层的InputWindowHandler
sp<
InputWindowHandle>
inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
//将注册任务交给NativeInputManager
status_t status = im->
registerInputChannel(env, inputChannel, inputWindowHandle, monitor);
//..
2.3
这一步通过NativeInputManager中的InputManager,InputManager通过InputDispatcher来注册InputChannel。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
mInputManager->
getDispatcher()->
registerInputChannel(
inputChannel, inputWindowHandle, monitor);
2.4
终于,将Server端的InputChannel交给了InputDispatcher。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
AutoMutex _l(mLock);
//判断是否该InputChannel已经添加过
if (getConnectionIndexLocked(inputChannel) >
= 0) {
ALOGW("Attempted to register already registered input channel ‘%s‘",
inputChannel->
getName().string());
return BAD_VALUE;
}
//创建一个Connection
sp<
Connection>
connection = new Connection(inputChannel, inputWindowHandle, monitor);
//获得InputChannel的文件描述符
int fd = inputChannel->
getFd();
//将Connection 以文件描述符为关键字,添加入m
mConnectionsByFd.add(fd, connection);
//..
//将文件描述符添加Looper,让Looper监控InputChannel的IO事件
//当IO事件发生时,就会调用回调函数handleReceiveCallback
mLooper->
addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
到这一步,InputDispatcher已经将InputChannel的Server端管理起来了。当Dispatcher将一个事件通过Channel发送给应用程序窗口以后,会进入休眠状态,直到应用窗口再次通过这个Channel返回一个消息将其激活,Dispatcher才准备发送下一个消息。
这里创建的Connection,Connection中又创建了InputPublisher。InputPublisher可以直接将消息通过这个InputChannel发送出去。
3. 向InputManagerService注册当前激活的应用程序窗口再次回顾一下1.4的,在1.4中WindowManagerService在焦点发生改变时,需要改变Focused Window。这里会在InputMonitor中注册当前激活的窗口。
文章图片
3.1
在InputMonitor中,有mInputFocus保存着当前激活的窗口,这里会将mInputFocus设置为传入的newWindow。然后调用updateInputWindowLW继续更新激活的窗口。
3.2
在1.4中,每个window会被加入一个Window List。这里会遍历这些List中的Windows,然后将这些windows进一步交给InputManagerService处理。
rameworks/base/services/core/java/com/android/server/wm/InputMonitor.java :
// Populate the input window list with information about all of the windows that
// could potentially receive input.
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
//..// Add all windows on the default display.
//mService指WindowManagerService
final int numDisplays = mService.mDisplayContents.size();
for (int displayNdx = 0;
displayNdx <
numDisplays;
++displayNdx) {
WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
for (int winNdx = windows.size() - 1;
winNdx >
= 0;
--winNdx) {
final WindowState child = windows.get(winNdx);
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
// Skip this window because it cannot possibly receive input.
continue;
}//..final int flags = child.mAttrs.flags;
final int privateFlags = child.mAttrs.privateFlags;
final int type = child.mAttrs.type;
//只有一个window可以获得Focus
final boolean hasFocus = (child == mInputFocus);
final boolean isVisible = child.isVisibleLw();
//..
addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
hasWallpaper);
}
}// Send windows to native code.
//将这些windows交给InputManager继续进行注册
mService.mInputManager.setInputWindows(mInputWindowHandles);
// Clear the list in preparation for the next round.
clearInputWindowHandlesLw();
3.3
这一步InputManagerService将任务交给了c++层。
3.4
进入c++层,将这些windowHandles交给NativeInputManager。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobjectArray windowHandleObjArray) {
//将ptr转化为指针
NativeInputManager* im = reinterpret_cast<
NativeInputManager*>
(ptr);
//windowHandleObjArray是传入的windowHandles
im->
setInputWindows(env, windowHandleObjArray);
}
3.5
这一步先将java的windowHandle变为c++对象,然后这些windowHandle被交给InputDispatcher处理。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
if (windowHandleObjArray) {
jsize length = env->
GetArrayLength(windowHandleObjArray);
//将java对象转化为c++对象
for (jsize i = 0;
i <
length;
i++) {
jobject windowHandleObj = env->
GetObjectArrayElement(windowHandleObjArray, i);
if (! windowHandleObj) {
break;
// found null element indicating end of used portion of the array
}sp<
InputWindowHandle>
windowHandle =
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
if (windowHandle != NULL) {
windowHandles.push(windowHandle);
}
env->
DeleteLocalRef(windowHandleObj);
}
}//将这些windowHandles交给Dispatcher处理
mInputManager->
getDispatcher()->
setInputWindows(windowHandles);
3.6
InputDispatcher跟新WindowHandle,并且更新Focused window。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
{ // acquire lock
AutoMutex _l(mLock);
//清理旧的WindowHandle
Vector<
sp<
InputWindowHandle>
>
oldWindowHandles = mWindowHandles;
mWindowHandles = inputWindowHandles;
sp<
InputWindowHandle>
newFocusedWindowHandle;
bool foundHoveredWindow = false;
//找到新的Focused WindowHandle
for (size_t i = 0;
i <
mWindowHandles.size();
i++) {
const sp<
InputWindowHandle>
&
windowHandle = mWindowHandles.itemAt(i);
if (!windowHandle->
updateInfo() || windowHandle->
getInputChannel() == NULL) {
mWindowHandles.removeAt(i--);
continue;
}
if (windowHandle->
getInfo()->
hasFocus) {
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
foundHoveredWindow = true;
}
}if (!foundHoveredWindow) {
mLastHoverWindowHandle = NULL;
}if (mFocusedWindowHandle != newFocusedWindowHandle) {
//旧的FocusedWindow和新的不同,则需要停止向旧的Channel中发送消息
if (mFocusedWindowHandle != NULL) {
sp<
InputChannel>
focusedInputChannel = mFocusedWindowHandle->
getInputChannel();
if (focusedInputChannel != NULL) {
synthesizeCancelationEventsForInputChannelLocked(
focusedInputChannel, options);
}
}
//设置新的Focused Window
mFocusedWindowHandle = newFocusedWindowHandle;
}// Release information for windows that are no longer present.
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (size_t i = 0;
i <
oldWindowHandles.size();
i++) {
const sp<
InputWindowHandle>
&
oldWindowHandle = oldWindowHandles.itemAt(i);
if (!hasWindowHandleLocked(oldWindowHandle)) {
//释放已经不存在的旧Window
oldWindowHandle->
releaseInfo();
}
}
} // release lock// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->
wake();
到这里,InputDispatcher获取了和激活的Window的双向通信通道,同时Dispatcher也知道了是哪个Window处于Focused状态。只要Client端将通信通道建立完毕,则Dispatcher可以向Activity的Window发送消息了。
4. Client端注册InputChannel
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
在1.1中,通过WindowSession添加Window和InputChannel后,mInputChannel对象已经转化为双向通道中的client端通道了,从1.4可以知道。然后,该步骤又创建了一个WindowInputEventReceiver类的对象mInputEventReceiver,它将mInputChannel和主线程绑定在一起。
下面就看一下WindowInputEventReceiver的构造过程。
文章图片
4.1
WindowInputEventReceiver是InputEventReceiver的子类,具体构造过程在InputEventReceiver中。
4.2
这一步又开始调用native函数。
frameworks/base/core/java/android/view/InputEventReceiver.java:
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<
InputEventReceiver>
(this), inputChannel, mMessageQueue);
4.3
创建c++层的NativeInputEventReceiver。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
//将java对象转化为c++对象
sp<
InputChannel>
inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
//传入的java MessageQueue转化为c++的MessageQueue
sp<
MessageQueue>
messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
//..//创建了一个NativeInputEventReceiver,构造函数在下面
sp<
NativeInputEventReceiver>
receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
//对其进行初始化
status_t status = receiver->
initialize();
//返回一个NativeInputEventReceiver的指针
receiver->
incStrong(gInputEventReceiverClassInfo.clazz);
// retain a reference for the object
return reinterpret_cast<
jlong>
(receiver.get());
}
NativeInputEventReceiver的构造函数。
frameworks/base/core/jni/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) {
if (kDebugDispatchCycle) {
ALOGD("channel ‘%s‘ ~ Initializing input event receiver.", getInputChannelName());
}
}
4.4
这一步调用函数setFdEvents,参数为
ALOOPER_EVENT_INPUT
。4.5
将传入的Channel让主线程监听起来,以便处理传入的消息。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {‘
if (mFdEvents != events) {
mFdEvents = events;
//获取Channel的文件描述符
int fd = mInputConsumer.getChannel()->
getFd();
if (events) {
//获取Looper,给其添加监听fd的IO事件的请求
//MessageQueue的Looper为主线程Looper,如果Looper进入睡眠
//则会被Channel上写入的事件唤醒,从而可以处理新来的消息
mMessageQueue->
getLooper()->
addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->
getLooper()->
removeFd(fd);
}
}
}
这里监听的文件描述符发生IO事件时,调用的回调函数就是NativeInputEventReceiver自己,因为它为LooperCallback子类。
同时注意这里的Looper就是应用主线程的Looper,这里向其epoll多添加了一个文件描述符进行监听,因为epoll可以同时监听多个epoll的IO事件。同时设定了监听的类型为ALOOPER_EVENT_INPUT。
回忆下第10章的1.6步骤中epoll_wait进入的睡眠后,会监听mWakeEventFd文件描述符的事件。不仅如此,这一步还会监听更多的文件描述符的事件,这里就包括建立的InputChannel的文件描述符。所以在主线程通过调用pollInner进入睡眠以后,被唤醒后会判断发生事件的文件描述符是哪一个。
system/core/libutils/Looper.cpp :
if (fd == mWakeEventFd) {
if (epollEvents &
EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >
= 0) {
int events = 0;
if (epollEvents &
EPOLLIN) events |= EVENT_INPUT;
if (epollEvents &
EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents &
EPOLLERR) events |= EVENT_ERROR;
if (epollEvents &
EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
【Android系统源码阅读(12)(InputChannel的注册过程)】到这里,InputChannel在Client端也进行了监听,整个完整的window所在的应用主线程和InputDispatcher线程之间的双向Channel已经建立完毕,同时InputDispatcher知道哪一个window处于激活状态,因为它知道向哪一个window发送消息。
推荐阅读
- Android 开发转型前端准备知识
- Unity3d Android SDK接入解析Unity3d Android SDK的设计与两种接入方式
- Android性能优化之Systrace工具介绍_&&Systrace生成的trace.html打开空白或者打不开的解决办法
- Android Multimedia框架总结(十三)CodeC部分之OpenMAX框架初识及接口与适配层实现
- Android andbug 命令
- android中string.xml中%1$s%1$d等的用法
- Android系统源码阅读(13)(Input消息的分发过程)
- css伪类和伪类选择器 – CSS高级教程
- Accolite招聘经验更新(校园内)