Android应用程序键盘(Keyboard)消息处理机制分析(12)
Step 12.InputChannel.openInputChannelPair
这个函数定义在frameworks/base/core/java/android/view/InputChannel.java文件中:
- publicfinalclassInputChannelimplementsParcelable{
- ......
- /**
- *Createsanewinputchannelpair.Onechannelshouldbeprovidedtotheinput
- *dispatcherandtheothertotheapplication'sinputqueue.
- *@paramnameThedescriptive(non-unique)nameofthechannelpair.
- *@returnApairofinputchannels.Theyaresymmetricandindistinguishable.
- */
- publicstaticInputChannel[]openInputChannelPair(Stringname){
- ......
- returnnativeOpenInputChannelPair(name);
- }
- ......
- }
这个函数调用本地方法nativeOpenInputChannelPair来进一步执行操作。
Step 13.InputChannel.nativeOpenInputChannelPair
这个函数定义在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:
- staticjobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv*env,
- jclassclazz,jstringnameObj){
- constchar*nameChars=env->GetStringUTFChars(nameObj,NULL);
- String8name(nameChars);
- env->ReleaseStringUTFChars(nameObj,nameChars);
- sp<InputChannel>serverChannel;
- sp<InputChannel>clientChannel;
- status_tresult=InputChannel::openInputChannelPair(name,serverChannel,clientChannel);
- if(result){
- LOGE("Couldnotopeninputchannelpair.status=%d",result);
- jniThrowRuntimeException(env,"Couldnotopeninputchannelpair.");
- returnNULL;
- }
- //TODOmorerobusterrorchecking
- jobjectserverChannelObj=android_view_InputChannel_createInputChannel(env,
- newNativeInputChannel(serverChannel));
- jobjectclientChannelObj=android_view_InputChannel_createInputChannel(env,
- newNativeInputChannel(clientChannel));
- jobjectArraychannelPair=env->NewObjectArray(2,gInputChannelClassInfo.clazz,NULL);
- env->SetObjectArrayElement(channelPair,0,serverChannelObj);
- env->SetObjectArrayElement(channelPair,1,clientChannelObj);
- returnchannelPair;
- }
这个函数根据传进来的参数name在C++层分别创建两个InputChannel,一个作为Server端使用,一个作为Client端使用,这里的Server端即是指InputManager,而Client端即是指应用程序。这两个本地的InputChannel是通过InputChannel::openInputChannelPair函数创建的,创建完成后,再相应地在Java层创建相应的两个InputChannel,然后返回。
Step 14.InputChannel.openInputChannelPair
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_tInputChannel::openInputChannelPair(constString8&name,
- sp<InputChannel>&outServerChannel,sp<InputChannel>&outClientChannel){
- status_tresult;
- intserverAshmemFd=ashmem_create_region(name.string(),DEFAULT_MESSAGE_BUFFER_SIZE);
- if(serverAshmemFd<0){
- ......
- }else{
- result=ashmem_set_prot_region(serverAshmemFd,PROT_READ|PROT_WRITE);
- if(result<0){
- ......
- }else{
- //Dupthefiledescriptorbecausetheserverandclientinputchannelobjectsthat
- //arereturnedmayhavedifferentlifetimesbuttheysharethesamesharedmemoryregion.
- intclientAshmemFd;
- clientAshmemFd=dup(serverAshmemFd);
- if(clientAshmemFd<0){
- ......
- }else{
- intforward[2];
- if(pipe(forward)){
- ......
- }else{
- intreverse[2];
- if(pipe(reverse)){
- ......
- }else{
- String8serverChannelName=name;
- serverChannelName.append("(server)");
- outServerChannel=newInputChannel(serverChannelName,
- serverAshmemFd,reverse[0],forward[1]);
- String8clientChannelName=name;
- clientChannelName.append("(client)");
- outClientChannel=newInputChannel(clientChannelName,
- clientAshmemFd,forward[0],reverse[1]);
- returnOK;
- }
- ......
- }
- ......
- }
- }
- }
- ......
- }
在阅读这个函数之前,我们首先了解一下C++层的InputChannel的构造函数:
- InputChannel::InputChannel(constString8&name,int32_tashmemFd,int32_treceivePipeFd,
- int32_tsendPipeFd):
- mName(name),mAshmemFd(ashmemFd),mReceivePipeFd(receivePipeFd),mSendPipeFd(sendPipeFd){
- ......
- }
为了创建一个InputChannel,我们需要准备四个参数,一个是输入通道的名称name,一个是匿名共享内存文件描述符,一个是管道的读端文件描述符,一个是管道的写端文件描述符。在上面的openInputChannelPair函数,输入通道的名称已经作为参数传递进来,因此,还需要创建匿名共享内存文件,还有管道。这里需要创建两个管道,一个称为前向管道(forward pipe),一个称为反向管道(reverse pipe),它们交叉使用在Server端和Client端的InputChannel中,这样就使入Server和Client可以互相通信了。
具体来说,Server端和Client端的InputChannel分别是这样构成的:
Server Input Channel: ashmem - reverse(read) - forward(write)
Client Input Channel: ashmem - forward(read) - reverse(write)
前面我们在Android应用程序消息处理机制(Looper、Handler)分析一文中学习Android应用程序的消息处理机制时知道,管道可以用作进程间通信,其中一个进程在管道的读端等待新的内空可读,另一个进程在管道的写端写入新的内容以唤醒在管道读端等待的进程,这样就实现了进程间通信。在我们这个情景中,Client端可以在前向管道(forward pipe)的读端睡眠等待新的内容可读,而Server端可以通过向前向管道(forward pipe)的写端写入新的内容来唤醒Client端,同样,把前向管道(forward pipe)换成反向管道(reverse pipe),也能实现Client端唤醒Server端。在后面我们分析InputDispatcher分发键盘消息时,会看到它们的用法。
有了这些背景知识后,相信上面的openInputChannelPair的代码就容易理解了,这里就不再详述了。
创建好了这两个输入通道后,回到Step 11中的WindowManagerService.addWindow函数中,一方面它把刚才创建的Client端的输入通道通过outInputChannel参数返回到应用程序中:
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
另一方面,它还要把刚才创建的Server端的输入通道注册到InputManager中:
- mInputManager.registerInputChannel(win.mInputChannel);
Step 15. InputManager.registerInputChannel
这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- publicclassInputManager{
- ......
- /**
- *Registersaninputchannelsothatitcanbeusedasaninputeventtarget.
- *@paraminputChannelTheinputchanneltoregister.
- */
- publicvoidregisterInputChannel(InputChannelinputChannel){
- if(inputChannel==null){
- thrownewIllegalArgumentException("inputChannelmustnotbenull.");
- }
- nativeRegisterInputChannel(inputChannel,false);
- }
- ......
- }
它通过调用本地方法nativeRegisterInputChannel来执行进一步的操作。
Step 16.InputManager.nativeRegisterInputChannel
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- staticvoidandroid_server_InputManager_nativeRegisterInputChannel(JNIEnv*env,jclassclazz,
- jobjectinputChannelObj,jbooleanmonitor){
- ......
- sp<InputChannel>inputChannel=android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- status_tstatus=gNativeInputManager->registerInputChannel(
- env,inputChannel,inputChannelObj,monitor);
- ......
- }
这里首先通过Java层的InputChannel对象获得C++层的InputChannel对象,它们之间的对应关系是在前面的Step 13中设置好的,接着调用NativeInputManager的registerInputChannel执行进一步的操作。
Step 17. NativeInputManager.registerInputChannel
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- status_tNativeInputManager::registerInputChannel(JNIEnv*env,
- constsp<InputChannel>&inputChannel,jobjectinputChannelObj,boolmonitor){
- ......
- status=mInputManager->getDispatcher()->registerInputChannel(inputChannel,monitor);
- ......
- }
这个函数主要是调用了InputDispatcher的registerInputChannel来真正执行注册输入通道的操作。
更多相关文章
- C语言函数以及函数的使用
- android客户端利用sokcet通信和向Java服务端发请求,Java服务端把
- Android Layout布局文件里的android:layout_height等属性为什么
- Android 自动编译、打包生成apk文件 3 - 使用SDK Ant方式