Android入门之创建InputChannel
来源:互联网 发布:macos 软件安装 编辑:程序博客网 时间:2024/05/29 11:53
首先需要知道什么是InputChannel。
我们说,InputDispatcher和客户窗口ViewRoot之间,是通过Pipe传递消息的,而Pipe是Linux系统调用的一部分,Android为了能够调用Pipe而创建了InputChannel类,可以说,InputChannel是Pipe的Android版。
InputChannel类有两个基本作用,一是保存消息端口对应的Pipe的读、写描述符,另一个是封装了Linux的Pipe系统调用,即程序员可以使用InputChannel所提供的函数创建底层的Pipe对象。
在阅读WmS的addWindow方法时,我们提到有以下代码:
if (outInputChannel != null) {String name = win.makeInputChannelName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);win.mInputChannel = inputChannels[0];inputChannels[1].transferToBinderOutParameter(outInputChannel);mInputManager.registerInputChannel(win.mInputChannel);}WmS即是在这里创建InputChannel的,来看看openInputChannelPair方法:
/** * 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) {if (name == null) {throw new IllegalArgumentException("name must not be null");}if (DEBUG) {Slog.d(TAG, "Opening input channel pair '" + name + "'");}return nativeOpenInputChannelPair(name);}
注意此方法的注释:创建一对新的InputChannel,一个提供给InputDispatcher,另一个用于应用的输入队列。
前面说到,应用程序和WmS之间交互会创建两个Pipe,一个用于读、一个用于写,在此处得到验证。
nativeOpenInputChannelPair方法是InputChannel对应的cpp的方法,找到文件android_view_InputChannel.cpp,找到nativeOpenInputChannelPair方法:
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;}
此方法创建了两个通道变量,分别是serverChannel和clientChannel,然后通过InputChannel::openInputChannelPair(name, serverChannel, clientChannel)为通道赋值。
InputChannel::openInputChannelPair这个方法来自于frameworks/base/libs/ui/InputTransport.cpp,感谢柯元旦先生,因为我到目前为止还不知道是如何找到这个文件的。。
InputChannel::openInputChannelPair内容如下所示:
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) { result = -errno; LOGE("channel '%s' ~ Could not create shared memory region. errno=%d", name.string(), errno); } else { result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE); if (result < 0) { LOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.", name.string(), result, serverAshmemFd); } 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) { result = -errno; LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d", name.string(), errno); } else { int forward[2]; if (pipe(forward)) { result = -errno; LOGE("channel '%s' ~ Could not create forward pipe. errno=%d", name.string(), errno); } else { int reverse[2]; if (pipe(reverse)) { result = -errno; LOGE("channel '%s' ~ Could not create reverse pipe. errno=%d", name.string(), errno); } 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; } ::close(forward[0]); ::close(forward[1]); } ::close(clientAshmemFd); } } ::close(serverAshmemFd); } outServerChannel.clear(); outClientChannel.clear(); return result;}
首先调用ashmem_create_region创建文件描述符用于多进程间共享,ashmem_create_region定义在system/core/libcutils/ashmem-dev.c中,内容如下:
/* * ashmem_create_region - creates a new ashmem region and returns the file * descriptor, or <0 on error * * `name' is an optional label to give the region (visible in /proc/pid/maps) * `size' is the size of the region, in page-aligned bytes */int ashmem_create_region(const char *name, size_t size){int fd, ret;fd = open(ASHMEM_DEVICE, O_RDWR);if (fd < 0)return fd;if (name) {char buf[ASHMEM_NAME_LEN];strlcpy(buf, name, sizeof(buf));ret = ioctl(fd, ASHMEM_SET_NAME, buf);if (ret < 0)goto error;}ret = ioctl(fd, ASHMEM_SET_SIZE, size);if (ret < 0)goto error;return fd;error:close(fd);return ret;}
表示创建一个新的ashmem区并返回文件描述符,异常时返回小于0的数据。
创建文件描述符后,经过以下处理:
(1) 调用ashmem_set_port_region设置该文件描述符的属性为可读、可写方式;
(2) 然后调用dup()函数,复制一个同样的描述符,赋值给clientAshmemFd;
(3) 使用pipe()系统调用创建一个管道forward,返回管理的两个端点,第一个是读,第二个是写;
(4) 使用pipe()系统调用创建两个管道reverse;
(5) 使用forward[0]、reverse[1]作为outServerChannel,使用forward[1]、reverse[0]作为outClientChannel;
重点项:
(1) 为什么要使用dup()?因为InputDispatcher线程和客户线程将共享这段内存区存储交互消息,或者说,两者都可以向这段内存区写数据,至于这些消息来自于InputDispatcher还是来自客户窗口,则在消息本身中进行了标;
(2) 管道和通道的对应关系如下图所示,其中f代表forward,r代表reverse:
(3) 注意管道(pipe)、共享内存(ashemem)和通道(channel)的区别,pipe用于InputDispatcher和客户窗口进行消息传递,但有时可能需要传递大量数据,因此又开辟了一段共享内存,这段共享内存不是必需的,而通道是每个通信端点保存的一个数据类,保存了pipe和共享内存的相关信息,因此,在ViewRoot类中包含一个InputChannel内部变量mInputChannel,而在WmS的mInputManager对象中也保存了和每个客户窗口通道的InputChannel对象列表。
(4) 这里创建的两个通道是可以互换的,即f0和r1作为outServerChannel,而r0和f1作为outClientChannel。
android_view_InputChannel创建通道完成后,创建一个jobjectArray channelPair对象,将该对象返回到Java代码中。
- Android入门之创建InputChannel
- Android入门之在WmS中注册InputChannel
- Android入门之在客户进程中注册InputChannel
- Android输入系统之InputChannel
- Android输入系统之InputChannel(上)
- Android输入系统之InputChannel(下)
- Android输入系统之InputChannel(上)
- Android输入系统之InputChannel(下)
- Android:输入系统(InputChannel)
- Android系统源码阅读(12):InputChannel的注册过程
- Android学习之创建自定义View(入门)
- 【入门】Android创建数据库
- Lucene入门之创建索引
- 入门:创建一个android项目
- Android开发入门-《创建Fragment》
- [Android入门]从零构建Android app之天气——1.创建项目
- Android入门(一):创建Android工程
- Android之创建快捷方式
- 工作中的oracle常用维护
- 第一个非重复出现的字符
- 浅谈java类加载机制
- 黑马程序员--Oracle学习_day05
- 软件用户体验非常好的前端设计
- Android入门之创建InputChannel
- 为elastic添加中文分词
- flume 示例一收集tomcat日志
- SimpleDateFormat安全的时间格式化
- 索引简单介绍
- 游戏任务成就体系的实现(五):通讯模块(MsgUtil)基于NodeJS+TCP的机制的实现
- WebApp开发实战视频教程
- Android 处理屏幕点击
- java读取csv文件