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代码中。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 高考第一志愿没录取怎么办 电子画颜料干了怎么办 数字画颜料干了怎么办 彩砂纸画不好了怎么办 宝宝吃了油画棒怎么办 2岁宝宝不爱刷牙怎么办 两岁宝宝不刷牙怎么办 1岁宝宝不爱刷牙怎么办 3岁宝宝不肯刷牙怎么办 20岁没学历迷茫怎么办 四岁了不长头发怎么办 17岁掉头发严重怎么办 头发很油,又少怎么办 25岁头发变稀怎么办 宝宝头发少又黄怎么办 头旋附近头发少怎么办 25岁掉头发严重怎么办 2岁宝宝头发稀少怎么办 掉头发很厉害怎么办吧 头发点的很厉害怎么办 为什么掉头发很厉害怎么办 产后2年脱发严重怎么办 产妇掉头发很厉害怎么办 头发掉了怎么办小妙招 头顶头发掉的厉害怎么办 20岁头发有些少怎么办 头痒头发掉厉害怎么办 头油头痒掉头发怎么办 头发痒掉发严重怎么办 宝宝的脸皴了怎么办 宝宝冬天脸皴了怎么办 白衬衣领子变黄怎么办 白衬衣领子烂了怎么办 玩手机眼睛红了怎么办 吃了发芽的土豆怎么办 散尾竹叶子发黄怎么办 吃了一朵长春花怎么办 3d模型打开缓慢怎么办 多肉叶片发芽后怎么办 多肉种子发芽后怎么办 玫瑰金手机掉漆怎么办