概述:
本文讲述Android Input输入框架中 InputChannel的功能。从前面的讲述,我们知道input系统服务最终将输入事件写入了InputChannel,而input属于system_server进程,App属于另外一个进程,当Input系统服务想要把事件传递给App进行处理时这里就涉及到了一个跨进程通信的问题。Android系统中常用的就是通过Binder实现进程间通信,但是Binder属于CS架构,单一的连接不能实现Client和Server的双向通信,如果我们想要实现进程间的双向通信,则必须建立两个Binder通道。Android系统有多个App 这样则每个App都需要两个Binder通信通道才能完成Input系统进行通信。这显然不是很好的选择, 所以Android系统的Input框架选择了传统Linux的Unix域通信机制,也就是我们本章讲述的InputChannel实现原理。
本文涉及的源码路径
frameworks/native/libs/input/InputTransport.cpp
frameworks/base/core/java/android/view/InputChannel.java
frameworks/base/core/jni/android_view_InputChannel.cpp
InputChannel类
public final class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
......
@SuppressWarnings("unused")
private long mPtr; // used by native code
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native void nativeDup(InputChannel target);
private native String nativeGetName();
......
public InputChannel() {
}
@Override
protected void finalize() throws Throwable {
try {
nativeDispose(true);
} finally {
super.finalize();
}
}
......
public static InputChannel[] openInputChannelPair(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
if (DEBUG) {
Slog.d(TAG, "Opening input channel pair '" + name + "'");
}
return nativeOpenInputChannelPair(name);
}
public String getName() {
String name = nativeGetName();
return name != null ? name : "uninitialized";
}
......
public void dispose() {
nativeDispose(false);
}
......
public void transferTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
nativeTransferTo(outParameter);
}
......
}
1、从java层的InputChannel类可以看出,该类的核心实现都是由native jni层完成的。因此接下来我们主要从Native层讲述InputChannel的实现原理;
2、mPtr 保存native层对应的对象地址;
3、可序列化,InputChannel对象可以跨组件传递;
InputChannel 方法
由于我们本系列的博客讲述Android Input输入管理框架,下面的讲述中,只讲述设计到Input输入管理的关键方法实现。
1、openInputChannelPair
openInputChannelPair的实现如下所示:
public static InputChannel[] openInputChannelPair(String name) {
......
return nativeOpenInputChannelPair(name);
}
该方法本质上直接调用了jni方法nativeOpenInputChannelPair,如下所示:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
......
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
......
// 创建两个InputChannel 空java对象
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
// 初始化数组对象
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
// 返回创建的2个java层对象
return channelPair;
}
nativeOpenInputChannelPair方法主要完成如下的逻辑:
1、创建一个网络socket对套接字,分别由serverChannel和clientChannel进行管理,这部分源码不再展开详细讲解;
2、创建java层的InputChannel两个数组对象;
3、创建serverChannel和clientChannel InputChannel 对象,并分别设置server和client的native层的对象地址;
4、初始化第2步创建的java数组对象,然后将该数组对象返回java空间;
2、transferTo
transferTo的方法实现如下所示:
public void transferTo(InputChannel outParameter) {
......
nativeTransferTo(outParameter);
}
改方法直接调用了nativeTransferTo,如下所示:
/**
* 将自己的native C++对象绑定给otherObj的native C++上,然后清空自己的native对象
*/
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
// 获的调用对象的native对象地址
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
// 将otherObj对象的native地址设置为调用对象的nativeInputChannel
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
// 清空调用对象的native对象地址
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
nativeTransferTo的实现逻辑细节,我们不再展开描述,该方法的主要功能就是将otherObj 对象的native层的C++地址替换为调用该方法的InputChannel对象的native层的地址;
3、dispose
dispose的实现方法,如下所示:
public void dispose() {
nativeDispose(false);
}
该方法也是直接调用native方法,如下所示:
static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (nativeInputChannel) {
if (finalized) {
ALOGW("Input channel object '%s' was finalized without being disposed!",
nativeInputChannel->getInputChannel()->getName().string());
}
nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
// 删除C++对象
delete nativeInputChannel;
}
}
该方法的实现细节,我们不再展开描述,它的主要功能就是释放native层的InputChannel对象。
总结
本文为Android Input框架中App和Input 服务进行通信的桥梁InputChannel,本质上就是就是对linux中unix域 Socket进程间通信的封装,Framework层调用java层的InputChannel方法,而该方法的核心实现都处于native层。下一篇讲述App如何使用InputChannel与input系统服务建立链接。