zoukankan      html  css  js  c++  java
  • Android输入输出机制之来龙去脉

    Java代码  收藏代码
    1. openInputChannelPair(  

    阅读本文的前提条件是知道匿名管道和匿名共享内存是怎么一回事,否则阅读相应的文章。

    Anonymous pipes 和Anonymous Shared Memory。

    首先ViewRoot的SetView方法中的关键地方:

    第一处是创建:

    Java代码  收藏代码
    1. mInputChannel = new InputChannel();  
    2. try {  
    3.                    res = sWindowSession.add(mWindow, mWindowAttributes,  
    4.                            getHostVisibility(), mAttachInfo.mContentInsets,  
    5.                            mInputChannel);  

    第二处是注册:

    Java代码  收藏代码
    1. InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
    2.                            Looper.myQueue());  

    创建部分的第一个方法InputChanel()构造函数是个空函数。重要的是第二个函数,

    Java代码  收藏代码
    1. res = sWindowSession.add(mWindow, mWindowAttributes,  
    2.         getHostVisibility(), mAttachInfo.mContentInsets,  
    3.         mInputChannel);  

     这个函数调用的是系统服务,所谓的系统服务,就是运行在SYstem进程的服务程序。代码进入到了android系统服务进程的WindowManagerService类的Session类的add方法,下面是add方法:

    Java代码  收藏代码
    1. public int add(IWindow window, WindowManager.LayoutParams attrs,  
    2.         int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {  
    3.     return addWindow(this, window, attrs, viewVisibility, outContentInsets,  
    4.             outInputChannel);  
    5. }  

     add调用addWindow,下面进入addWindow,addWindow比较长,仅仅列出重要的几行代码:

    Java代码  收藏代码
    1. if (outInputChannel != null) {  
    2.     String name = win.makeInputChannelName();  
    3.     InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);  
    4.     win.mInputChannel = inputChannels[0];  
    5.     inputChannels[1].transferToBinderOutParameter(outInputChannel);  
    6.       
    7.     mInputManager.registerInputChannel(win.mInputChannel);  
    8. }  

     这里就牵涉到了匿名管道了,进入OpenInputChannelPair来看,调用了nativeOpenInputChannelPair,下面看nativeOpenInputChannelPair做了什么事情:

    Cpp代码  收藏代码
    1. static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,  
    2.         jclass clazz, jstring nameObj) {  
    3.     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);  
    4.     String8 name(nameChars);  
    5.     env->ReleaseStringUTFChars(nameObj, nameChars);  
    6.   
    7.     sp<InputChannel> serverChannel;  
    8.     sp<InputChannel> clientChannel;  
    9.     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);  
    10. }  

     最重要的是

      status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);这一行

    Cpp代码  收藏代码
    1. status_t InputChannel::openInputChannelPair(const String8& name,  
    2.         sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {  
    3.     status_t result;  
    4.   
    5.     int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);  
    6.     if (serverAshmemFd < 0) {  
    7.         result = -errno;  
    8.         LOGE("channel '%s' ~ Could not create shared memory region. errno=%d",  
    9.                 name.string(), errno);  
    10.     } else {  
    11.         result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);  
    12.         if (result < 0) {  
    13.             LOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",  
    14.                     name.string(), result, serverAshmemFd);  
    15.         } else {  
    16.             // Dup the file descriptor because the server and client input channel objects that  
    17.             // are returned may have different lifetimes but they share the same shared memory region.  
    18.             int clientAshmemFd;  
    19.             clientAshmemFd = dup(serverAshmemFd);  
    20.             if (clientAshmemFd < 0) {  
    21.                 result = -errno;  
    22.                 LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",  
    23.                         name.string(), errno);  
    24.             } else {  
    25.                 int forward[2];  
    26.                 if (pipe(forward)) {  
    27.                     result = -errno;  
    28.                     LOGE("channel '%s' ~ Could not create forward pipe.  errno=%d",  
    29.                             name.string(), errno);  
    30.                 } else {  
    31.                     int reverse[2];  
    32.                     if (pipe(reverse)) {  
    33.                         result = -errno;  
    34.                         LOGE("channel '%s' ~ Could not create reverse pipe.  errno=%d",  
    35.                                 name.string(), errno);  
    36.                     } else {  
    37.                         String8 serverChannelName = name;  
    38.                         serverChannelName.append(" (server)");  
    39.                         outServerChannel = new InputChannel(serverChannelName,  
    40.                                 serverAshmemFd, reverse[0], forward[1]);  
    41.   
    42.                         String8 clientChannelName = name;  
    43.                         clientChannelName.append(" (client)");  
    44.                         outClientChannel = new InputChannel(clientChannelName,  
    45.                                 clientAshmemFd, forward[0], reverse[1]);  
    46.                         return OK;  
    47.                     }  
    48.                     ::close(forward[0]);  
    49.                     ::close(forward[1]);  
    50.                 }  
    51.                 ::close(clientAshmemFd);  
    52.             }  
    53.         }  
    54.         ::close(serverAshmemFd);  
    55.     }  
    56.   
    57.     outServerChannel.clear();  
    58.     outClientChannel.clear();  
    59.     return result;  
    60. }  

       这段代码又长又臭,总而言之就是创建用来【发送和接受信号】的接受和发送描述符,和生成用来【传递事件】的匿名共享内存,生成InputChannel对象。创建好之后,AddWindow方法通过BInder机制返回给【用户进程】。   客户端对应的是【应用程序】(读),服务端对应的是【InputDispatcher】(写)。

       理解本段代码的关键是:代码中的 reverse和forward是相对于server来说的。对于server来说,后向管道用来接收,前向管道用来发送。函数pipe出来的值,数组的0索引对应的描述符是发送端。1对应的是接收端。

       上面的介绍基本上就结束了。后面也许,我们更想知道的是这两个InputChannel如何通信的。一个在ViewRoot中,一个在InputDiapacher中。通信方式几本上就是,

       InputReader(InputReader.cpp中)启动无限循环,读取一个事件,发送给InputDispacher,InputDispatcher把事件写入到共享内存,并通过管道发送信号给ViewRoot中的InputChannel,InputChannel收到信号后,通过InputConsumer的consume方法来把事件发送给VIewRoot中的InputChannel。

  • 相关阅读:
    SharePoint学习资料收集
    VS2008 IDE界面
    罗列没有主键的表
    Google Calendar API练习
    pb中数据窗口中字段只显示255个的解决方法
    oracle 中的事务和update from 语句
    sqlserver2005数据库扩容方案
    在一个表上创建非聚集索引和聚集索引
    <xsl:applytemplates/>的应用
    时间戳转换为日期类型
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/4024276.html
Copyright © 2011-2022 走看看