概述:
本文講述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 codeprivate 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() {}@Overrideprotected 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地址設置為調用對象的nativeInputChannelandroid_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系統服務建立鏈接。