由浅入深学习android input系统 - InputChannel解析
Posted 许佳佳233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由浅入深学习android input系统 - InputChannel解析相关的知识,希望对你有一定的参考价值。
概述
前文链接:由浅入深学习android input系统(二) - input事件如何传递到app进程( InputDispatcher )
根据前文分析,InputDispatcher将事件分发给app进程是通过InputChannel,那么InputChannel又是什么时候建立的呢?
InputChannel的初始化
InputChannel初始化,调用任务栈如下:
- ViewRootImpl.setView()
- IWindowSession.addToDisplay()
等同于 WindowManagerService.addToDisplay() - WindowManagerService.addWindow()
- WindowState.openInputChannel()
- android.view.InputChannel.openInputChannelPair()
- android.view.InputChannel.nativeOpenInputChannelPair()
- android_view_InputChannel_nativeOpenInputChannelPair()
- InputTransport.InputChannel.openInputChannelPair()
InputTransport.InputChannel.openInputChannelPair()
此处分别初始化了两个socket,一个是client,一个是server,两个socket分别都支持接收与发送。
那么client和server分别对应什么场景呢?这需要继续根据任务栈往上看。
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.c_str(), 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));
std::string serverChannelName = name;
serverChannelName += " (server)";
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
std::string clientChannelName = name;
clientChannelName += " (client)";
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
serverChannel与clientChannel
android_view_InputChannel_nativeOpenInputChannelPair
此处会根据创建的client与server的两个channel分别创建两个android.view.InputChannel。
最终返回javaArray对象,0的位置是serverChannel,1的位置是clientChannel。
于是继续根据调用任务栈往上看。
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
std::string name = nameChars;
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
int register_android_view_InputChannel(JNIEnv* env) {
int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
NELEM(gInputChannelMethods));
jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>",
"()V");
return res;
}
WindowState.openInputChannel()
逻辑如下:
- mInputChannel指向serverChannel对象,mClientChannel指向clientChannel对象。
- InputManagerService.registerInputChannel()方法会将mInputChannel注册进去。(后面具体分析)
- 通过transferTo()方法,让outInputChannel使用mClientChannel的通道,然后回收mClientChannel的相关资源。
那么接下来其实我们就有两个探索的路线了:
- serverChannel:需要探索InputManagerService.registerInputChannel()
- clientChannel:需要探索outInputChannel是什么
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mWmService.mInputManager.registerInputChannel(mInputChannel);
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
// If the window died visible, we setup a dummy input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
serverChannel的探索
根据InputManagerService.registerInputChannel(),可以得到如下的任务栈:
- InputManagerService.registerInputChannel()
- InputManagerService.nativeRegisterInputChannel()
- NativeInputManager.registerInputChannel()
- InputDispatcher.registerInputChannel()
由此可以得知,这里的serverChannel就是InputDispatcher对应的InputChannel。
InputDispatcher通过这个channel将事件分发给具体的window。
对InputDispatcher不是很熟悉的读者推荐看下前文:
clientChannel的探索
再罗列下InputChannel初始化的任务栈:
- ViewRootImpl.setView()
- IWindowSession.addToDisplay()
等同于 WindowManagerService.addToDisplay() - WindowManagerService.addWindow()
- WindowState.openInputChannel()
通过上面这个任务栈回溯,我们可以发现,最终这个Channel是在ViewRootImpl.setView()中创建的。
并且在创建之后也没有初始化过,最终其实就是使用了clientChannel的通道。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
——————————————————省略
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
}
——————————————————省略
}
这个channel会在哪里使用到?
- 每个widnow都会使用这个channel来监听系统进程发来的事件
- 由于clientChannel是在WindowState中通过openInputChannel()方法来初始化的,因此,每个window都对应一个clientChannel。
- 也就意味着每个window都对应一个接收事件的socket通道,一个app进程如果有多个window,那么也会有多个接收事件的socket通道。
对inputChannel作用不是很了解的读者,推荐看下前两篇文章:
总结
- 每个window会对应两个InputChannel,serverChannel与clientChannel。
- serverChannel会注册到InputDispatcher中,InputDispatcher会通过这个InputChannel将事件分发给对应的进程与window。这个事件是clientChannel来接收。
- clientChannel负责接收InputDispatcher发来的事件,然后将事件传递个ViewRootImpl,再传递给View。
- InputChannel初始化的时机是:WindowManagerService.addWindow(),也就是window第一次被注册到系统进程的时候。
以上是关于由浅入深学习android input系统 - InputChannel解析的主要内容,如果未能解决你的问题,请参考以下文章
由浅入深学习android input系统 - input系统的启动
由浅入深学习android input系统 - input事件采集(InputReader)
由浅入深学习android input系统 - input事件采集(InputReader)
由浅入深学习android input系统 - input事件如何传递到View