Android应用程序键盘(Keyboard)消息处理机制分析(12)
Step 12. InputChannel.openInputChannelPair
这个函数定义在frameworks/base/core/java/android/view/InputChannel.java文件中:
- public final class InputChannel implements Parcelable {
- ......
- /**
- * Creates a new input channel pair. One channel should be provided to the input
- * dispatcher and the other to the application's input queue.
- * @param name The descriptive (non-unique) name of the channel pair.
- * @return A pair of input channels. They are symmetric and indistinguishable.
- */
- public static InputChannel[] openInputChannelPair(String name) {
- ......
- return nativeOpenInputChannelPair(name);
- }
- ......
- }
这个函数调用本地方法nativeOpenInputChannelPair来进一步执行操作。
Step 13. InputChannel.nativeOpenInputChannelPair
这个函数定义在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:
- static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
- jclass clazz, jstring nameObj) {
- const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
- String8 name(nameChars);
- env->ReleaseStringUTFChars(nameObj, nameChars);
- sp<InputChannel> serverChannel;
- sp<InputChannel> clientChannel;
- status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- if (result) {
- LOGE("Could not open input channel pair. status=%d", result);
- jniThrowRuntimeException(env, "Could not open input channel pair.");
- return NULL;
- }
- // TODO more robust error checking
- jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(serverChannel));
- jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(clientChannel));
- jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
- env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
- env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
- return channelPair;
- }
这个函数根据传进来的参数name在C++层分别创建两个InputChannel,一个作为Server端使用,一个作为Client端使用,这里的Server端即是指InputManager,而Client端即是指应用程序。这两个本地的InputChannel是通过InputChannel::openInputChannelPair函数创建的,创建完成后,再相应地在Java层创建相应的两个InputChannel,然后返回。
Step 14. InputChannel.openInputChannelPair
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputChannel::openInputChannelPair(const String8& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
- status_t result;
- int serverAshmemFd = 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 {
- // Dup the file descriptor because the server and client input channel objects that
- // are returned may have different lifetimes but they share the same shared memory region.
- int clientAshmemFd;
- clientAshmemFd = dup(serverAshmemFd);
- if (clientAshmemFd < 0) {
- ......
- } else {
- int forward[2];
- if (pipe(forward)) {
- ......
- } else {
- int reverse[2];
- if (pipe(reverse)) {
- ......
- } else {
- String8 serverChannelName = name;
- serverChannelName.append(" (server)");
- outServerChannel = new InputChannel(serverChannelName,
- serverAshmemFd, reverse[0], forward[1]);
- String8 clientChannelName = name;
- clientChannelName.append(" (client)");
- outClientChannel = new InputChannel(clientChannelName,
- clientAshmemFd, forward[0], reverse[1]);
- return OK;
- }
- ......
- }
- ......
- }
- }
- }
- ......
- }
在阅读这个函数之前,我们首先了解一下C++层的InputChannel的构造函数:
- InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
- int32_t sendPipeFd) :
- 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文件中:
- public class InputManager {
- ......
- /**
- * Registers an input channel so that it can be used as an input event target.
- * @param inputChannel The input channel to register.
- */
- public void registerInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null.");
- }
- nativeRegisterInputChannel(inputChannel, false);
- }
- ......
- }
它通过调用本地方法nativeRegisterInputChannel来执行进一步的操作。
Step 16. InputManager.nativeRegisterInputChannel
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj, jboolean monitor) {
- ......
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- status_t status = 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_t NativeInputManager::registerInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
- ......
- status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);
- ......
- }
这个函数主要是调用了InputDispatcher的registerInputChannel来真正执行注册输入通道的操作。
Android应用程序键盘(Keyboard)消息处理机制分析(12)相关推荐
- Android系统中的Binder通信机制分析(7)- Java 层的 Binder 机制
声明 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6.7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾- 文中参考 ...
- Android应用程序键盘(Keyboard)消息处理机制分析(3)
Step 15. Looper.pollOnce 这个函数定义在frameworks/base/libs/utils/Looper.cpp文件中,具体可以参考前面Android应用程序消息处理机制(L ...
- Android应用程序线程消息循环模型分析(5)
从AsyncTask的实现可以看出,当我们第一次创建一个AsyncTask对象时,首先会执行下面静态初始化代码创建一个线程池sExecutor: private static final Bloc ...
- Android应用程序线程消息循环模型分析
出自:http://blog.csdn.net/luoshengyang/article/details/6905587 我们知道,Android应用程序是通过消息来驱动的,即在应用程序的主线程(UI ...
- 以下未发布-Android的多线程以及异步消息处理机制,android移动开发基础案例教程源码
1.Message: Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间进行数据交换.除了 what 字段,还可以使用 arg1 和 arg2 来携带整型数据,使用 ...
- Android应用程序键盘(Keyboard)消息处理机制分析(20)
Step 24. InputQueue.dispatchKeyEvent 这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件 ...
- Android应用程序键盘(Keyboard)消息处理机制分析(26)
Step 7. LocalWindowManager.removeViewImmediate 这个函数定义在frameworks/base/core/java/android/view/Window. ...
- Android应用程序键盘(Keyboard)消息处理机制分析(17)
Step 11. InputDispatcher.dispatchOnceInnerLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文 ...
- Android应用程序键盘(Keyboard)消息处理机制分析(14)
3. InputManager分发键盘消息给应用程序的过程分析 在分析InputManager分发键盘消息给应用程序的过程之前,我们先假设现在没有键盘事件发生,因此,InputManager中的Inp ...
最新文章
- LabVIEW OCR 实现车牌识别(实战篇—3)
- wireshark过滤规则学习总结
- 矩阵sum_推荐系统——从协同过滤到矩阵分解
- c语言while找rn,STM32Fatfs遍历文件(C语言实现)
- phpStorm中的unescaped xml character问题
- wordpress functions.php 在哪,在functions.php中定义变量并在WordPress中的函数钩子中访问它们...
- 最新ffmpeg编译和用eclipse进行源码调试
- Android静态安全检测 - Broadcast Receiver组件暴露
- 携程去哪儿移动端产品分析报告
- web前端之HTML
- ef 连接localdb_如何连接和使用Microsoft SQL Server Express LocalDB
- CentOS永久修改主机名
- 集合的洗牌,排序,拆分以及常用遍历方法
- 关于Ext.grid.EditorGridPanel使用中遇到的问题
- BT软件系统包含哪些部分?BT技术如何突破运营商的封锁?
- VScode 亮暗 高对比度 主题推荐
- golang interface 与 反射
- 金句: 對比MBA學位,我們更需要PSD學位的人! Poor, Smart and Deep Desire to… | consilient_lollapalooza on Xanga...
- 【Uplift】特征选择篇
- 基于控制台的五子棋小游戏(简易)