Android的存储系统—Vold与MountService分析

来源:互联网 发布:海报打印机 知乎 编辑:程序博客网 时间:2024/05/21 06:43

Android的存储系统—Vold与MountService分析(一)

Android的存储系统(一)

看了很长时间Vold存储模块的相关知识,也死扣了一段时间的Android源码,发现Android存储系统所涉及的函数调用,以及Kernel与上层之间的Socket传输真的是让人头疼,除了需要整理整个架构的原理以外,还要反复看源码,真真的郁闷。

郁闷之余,还是打算把自己看过的经验之贴和参考资料进行整理,以帖子的形式发出来,供码神们参考,有不对的地方请指正,我们互相交流,下面就进入主题。

Android的存储系统主要由:SystemServer进程中的MountService和Vold进程中的VolumeManager组成。

它们管理着系统的存储设备,执行各种操作,如:mount、unmount、format等。

图1 Android存储系统架构图

图2 Android存储系统原理图

【重要组成分析】

1、NetlinkManager

     全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp。

     该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。

2、VolumeManager

     全称是VolumeManager.cpp位于Android 4.x源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。

     因为我们这里是SD的挂载,因此经过NetlinkManager处理过后的消息会分为五种,分别是:block、switch、usb_composite、battery、power_supply。

     这里SD卡挂载的事件是block。

3、DirectVolume

     位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理。

     block事件又可以分为:Add、Removed、Change、Noaction这四种。

4、Volume

     位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。

5、CommandListener

     该类位于位于/system/vold/CommandListener.cpp,通过vold socket与NativeDaemonConnector通信。

6、NativeDaemonConnector

     该类位于frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递。

7、MountService

     位于frameworks/base/services/java/com.android.server/MountService.java。

     MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在Android系统中这是一个非常重要的类。

8、StorageManaer

     位于frameworks/base/core/java/andriod/os/storage/StorageManager.java。

     在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有Storage相关项,同时Setting也注册了该类的监听器。

     而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获取SD卡状态。

 

【SD卡挂载流程】

1、Kernel发出SD卡插入uevent消息。

2、NetlinkHandler::onEvent()接收内核发出的uevent并进行解析。

3、VolumeManager::handleBlockEvent()处理经过第二步处理后的事件。

4、接下来调用DirectVolume::handleBlockEvent()。

在该方法中需要注意亮点:

(1)程序首先会遍历mPath容器,寻找与event对应的sysfs_path是否存在于mPath容器中;

(2)针对event中的action有4种处理方式:Add、Removed、Change、Noaction。

 

 

 

 

5、经过上一步之后会调用DirectVolume::handleDiskAdded()方法,该方法中会广播disk insert消息。

6、SocketListener::runListener()会接收DirectVolume::handleDiskAdded()广播的消息。该方法主要完成对event中数据的获取,通过Socket。

7、调用FrameworkListener::onDataAvailable()方法处理接收到的消息内容。

8、FrameworkListener::dispatchCommand()该方法用于分发指令。

9、在FrameworkListener::dispatchCommand()方法中,通过runCommand()方法去调用相应的指令。

10、在/system/vold/CommandListener.cpp中有runCommand()的具体实现。在该类中可以找到这个方法:CommandListener::VolumeCmd::runCommand(),从字面意思上来看这个方法就是对Volume分发指令的解析。该方法中会执行“mount”函数:vm>mountVolume(arg[2])。

11、mountVolume(arg[2])在VolumeManager::mountVolume()中实现,在该方法中调用v>mountVol()。

12、mountVol()方法在Volume::mountVol()中实现,该函数是真正的挂载函数。(在该方法中,后续的处理都在该方法中,在Mount过程中会广播相应的消息给上层,通过setState()函数)。

13、setState(Volume::Checking);广播给上层,正在检查SD卡,为挂载做准备。

14、Fat::check();SD卡检查方法,检查SD卡是否是FAT格式。

15、Fat::doMount()挂载SD卡。

至此,SD的挂载已算初步完成,接下来应该将SD卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的。

 

16、MountService的构造函数中会开启监听线程,用于监听来自vold的socket信息。

     Thread thread = new Thread(mConnector,VOLD_TAG); thread.start();

17、mConnector是NativeDaemonConnector的对象,NativeDaemonConnector继承了Runnable并Override了run方法。在run方法中通过一个while(true)调用ListenToSocket()方法来实现实时监听。

18、在ListenToSocket()中,首先建立与Vold通信的Socket Server端,然后调用MountService中的onDaemonConnected()方法。

19、onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定义的,MountService实现了该接口并Override了onDaemonConnected()方法。该方法开启一个线程用于更新外置存储设备的状态,主要更新状态的方法也在其中实现。

20、然后回到ListenToSocket中,通过inputStream来获取Vold传递来的event,并存放在队列中。

21、然后这些event会在onDaemonConnected()通过队列的”队列.take()”方法取出。并根据不同的event调用updatePublicVolumeState()方法,在该方法中调用packageManagerService中的updateExteralState()方法来更新存储设备的状态。

22、更新是通过packageHelper.getMountService().finishMediaUpdate()方法来实现的。

23、在updatePublicVolumeState()方法中,更新后会执行如下代码:

       bl.mListener.onStorageStateChanged();

在Android源码/packages/apps/Settings/src/com.android.settings.deviceinfo/Memory.java代码中,实现了StorageEventListener 的匿名内部类,并Override了onStorageStateChanged()方法。因此在updatePublicVolumeState()中调用onStorageStateChanged()方法后,Memory.java中也会收到。在Memory.java中收到以后会在Setting界面进行更新,系统设置—存储中会更新SD卡的状态。从而SD卡的挂载从底层到达了上层。

 

在下一个帖子中我会对Vold模块的源码以及MountService服务进行分析,包括main函数、NetlinkManager、NetlinkHandler、处理block类型的uevent、处理MountService命令、VolumeManager、NativeDaemonConnector等源码,很快就会与大家见面,感谢支持,欢迎交流与指正!

 

Android的存储系统—Vold与MountService分析(二)

Android的存储系统(二)

回顾:前贴主要分析了Android存储系统的架构和原理图,简要的介绍了整个从Kernel-->Vold-->上层MountService之间的数据传输流程,在这样的基础上,我们开始今天的源码分析!

【源码分析】

1. Vold的main函数

  Vold也是通过init进程启动,它在init.rc中的定义如下:

1 service vold /system/bin/vold2     class core3     socket vold stream 0660 root mount4     ioprio be 2

  Vold服务放到了core分组,这就意味着系统启动时,它就会被init进程启动。这里定义的一个socket,主要用语Vold和Java层的MountService通信

   Vold模块的源代码位于system/vold,我们看看入口函数main(),代码如下:

复制代码
1 int main() {2     VolumeManager *vm;3     CommandListener *cl;4     NetlinkManager *nm;56     SLOGI("Vold 2.1 (the revenge) firing up");7     8     mkdir("/dev/block/vold", 0755);                 // 创建vold目录910    klog_set_level(6);1112    if (!(vm = VolumeManager::Instance())) {        // 创建VolumeManager对象13        exit(1);14    };15     16    if (!(nm = NetlinkManager::Instance())) {       // 创建NetlinkManager对象17        exit(1);18    };1922    cl = new CommandListener();                     // 创建CommandListener对象23    vm->setBroadcaster((SocketListener *) cl);      // 建立vm和cl的联系24    nm->setBroadcaster((SocketListener *) cl);      // 建立nm和cl的联系2526    if (vm->start()) {                              // 启动VolumeManager27        exit(1);28    }2930    if (process_config(vm)) {                       // 创建文件/fstab.xxx中定义的Volume对象31        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));32    }3334    cryptfs_pfe_boot();3536    if (nm->start()) {                              // 启动NetlinkManager,会调用NetlinkManager的start()方法,它创建PF_NETLINK socket,并开启线程从此socket中读取数据37        exit(1);38    }3940    coldboot("/sys/block");                         // 冷启动,创建/sys/block下的节点文件4142    if (cl->startListener()) {                      // 开始监听Framework的socket43        exit(1);44    }45    46    while(1) {                                      // 进入循环47        sleep(1000);                                // 主线程进入休眠48    }49
50 SLOGI(
"Vold exiting");51 exit(0);52 }
复制代码

   main函数的主要工作是创建3个对象:VolumeManager、NetlinkManager和CommandListener,同时将CommandListener对象分别设置到了VolumeManager对象和NetlinkManager对象中。

  从前贴的架构图中可以发现,CommandListener对象用于和Java层的NativeDaemonConnector对象进行socket通信,因此,无论是VolumeManager对象还是NetlinkManager对象都需要拥有CommandListener对象的引用。

2. 监听驱动发出的消息—Vold的NetlinkManager对象

  NetlinkManager对象的主要工作是监听驱动发出的uevent消息。

  main()函数中调用NetlinkManager类的静态函数Instance()来创建NetlinkManager对象,代码如下:

1 NetlinkManager *NetlinkManager::Instance() {2       if (!sInstance)3           sInstance = new NetlinkManager();      // NetlinkManager对象通过静态变量sInstance来引用,这意味着vold进程中只有一个NetlinkManager对象。4       return sInstance;5

  看下NetlinkManager的构造函数,代码如下:

1 NetlinkManager::NetlinkManager() {2       mBroadcaster = NULL;3

  NetlinkManager的构造函数只是对mBroadcaster进行了初始化。我们可以发现main()函数中通过调用NetlinkManager的setBroadcaster()函数来给变量mBroadcaster重新赋值。

nm->setBroadcaster((SocketListener *) cl);

  main()函数还调用了NetlinkManager的start()函数,我们观察一下NetlinkManager中的start()方法,代码如下:

复制代码
 1 int NetlinkManager::start() { 2      struct sockaddr_nl nladdr; 3      int sz = 64 * 1024; 4      int on = 1; 5  6      memset(&nladdr, 0, sizeof(nladdr)); 7      nladdr.nl_family = AF_NETLINK; 8      nladdr.nl_pid = getpid(); 9      nladdr.nl_groups = 0xffffffff;10      /*创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件*/11      if ((mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {12          SLOGE("Unable to create uevent socket: %s", strerror(errno));13          return -1;14      }15      /*设置缓冲区大小为64KB*/16      if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {17          SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));18          goto out;19      }20      /*设置允许 SCM_CREDENTIALS 控制消息的接收*/21      if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {22          SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));23          goto out;24      }25      /*绑定 socket 地址*/26      if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {27          SLOGE("Unable to bind uevent socket: %s", strerror(errno));28          goto out;29      }30      /*利用新创建的socket实例化一个NetlinkHandler类对象用于监听socket,NetlinkHandler继承了类NetlinkListener,NetlinkListener又继承了类SocketListener*/31      mHandler = new NetlinkHandler(mSock);32       if (mHandler->start()) {                                               // 启动NetlinkHandler,调用NetlinkHandler的start()函数33           SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));34           goto out;35      }36 37      return 0;38 39 out:40      close(mSock);41      return -1;42 }
复制代码

   我们看一下NetlinkManager的家族关系,如下图:

  上面的虚线为启动时的调用流程:

  (1) class NetlinkManager(在其start函数中创建了NetlinkHandler对象,并把创建的socket作为参数)

  (2)class NetlinkHandler: public NetlinkListener(实现了onEvent)

  (3) class NetlinkListener : public SocketListener(实现了onDataAvailable)

  (4) class SocketListener(实现了runListener,在一个线程中通过select查看哪些socket有数据,通过调用onDataAvailable来读取数据)。

总结:此贴主要分析了Vold的main()函数和NetlinkManager对象的源码,通过源码了解对象的创建时机和函数调用流程,下一贴会继续从NetlinkHandler的start()方法深入分析,继续源码的学习,很快会与大家见面,欢迎大家批评指正,我们互相学习。

Android的存储系统—Vold与MountService分析(三)

Android的存储系统(三)

回顾:前帖分析了Vold的main()函数和NetlinkManager的函数调用流程,截止到NetlinkHandler的创建和start()调用,本帖继续分析源码

 

 

1、处理block类型的uevent

  main()函数创建了CommandListener对象,NetlinkManager的start()函数又创建了NetlinkHandler对象,如果将CommandListener类和NetlinkHandler类的继承关系图画出来,会发现它们都是从SocketListener类派生出来的,如下图所示:

 

图1 NetlinkHandler和CommandListener的继承关系

  原理:处于最底层的SocketListener类的作用是监听socket的数据,接收到数据后分别交给FrameworkListener类和NetlinkListener类的函数,并分别对来自Framework和驱动的数据进行分析,分析后根据命令再分别调用CommandListener和NetlinkHandler中的函数。

  观察NetlinkHandler类的构造方法,代码如下:

NetlinkHandler::NetlinkHandler(int listenerSocket) :                NetlinkListener(listenerSocket) {}

  这个构造方法很简单,再看看它的start()方法,代码如下:

int NetlinkHandler::start() {    return this->startListener();}

  可以发现,start()方法调用了SocketListener的startListener()函数,代码如下:

复制代码
 1 int SocketListener::startListener(int backlog) { 2      if (!mSocketName && mSock == -1) { 3          SLOGE("Failed to start unbound listener"); 4          errno = EINVAL; 5          return -1; 6      } else if (mSocketName) {            // 只有CommandListener中会设置mSocketName 7            if ((mSock = android_get_control_socket(mSocketName)) < 0) { 8                SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno)); 9                return -1;10            }11            SLOGV("got mSock = %d for %s", mSock, mSocketName);12      }13 14      if (mListen && listen(mSock, backlog) < 0) {15          SLOGE("Unable to listen on socket (%s)", strerror(errno));16          return -1;17      } else if (!mListen)18           mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));19 20      if (pipe(mCtrlPipe)) {                          // 创建管道,用于退出监听线程21          SLOGE("pipe failed (%s)", strerror(errno));22          return -1;23      }24 25      if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {         // 创建一个监听线程26          SLOGE("pthread_create (%s)", strerror(errno));27          return -1;28      }29 30      return 0;31 }
复制代码

  startListener()函数开始监听socket,这个函数在NetlinkHandler中会被调用,在CommandListener也会被调用。

  startListener()函数首先判断变量mSocketName是否有值,只有CommandListener对象会对这个变量赋值,它的值就是在init.rc中定义的socket字符串。

      调用函数 android_get_control_socket()的目的是从环境变量中取得socket的值,这样CommandListener对象得到了它需要监听的socket,

  而对于NetlinkHandler对象而言,它的mSocket不为NULL,前面已经创建了socket。

 

  startListener()函数接下来会根据成员变量mListener的值来判断是否需要调用Listen()函数来监听socket。这个mListen的值在对象构造时根据参数来初始化。

  对于CommandListener对象,mListener的值为ture,对于NetlinkHandler对象,mListener的值为false,这是因为CommandListener对象和SystemServer通信,需要监听socket连接,而NetlinkHandler对象则不用。

  

  接下来startListener()函数会创建一个管道,这个管道的作用是通知线程停止监听,这个线程就是startListener()函数最后创建的监听线程,它的运行函数是threadStart(),在前贴的NetlinkManager家族图系中我们可以清晰的发现,其代码如下:

void *SocketListener::threadStart(void *obj) {     SocketListener *me = reinterpret_cast<SocketListener *>(obj);     me->runListener();                                             // 调用runListener()方法     pthread_exit(NULL);     return NULL;}

  threadStart()中又调用了runListener()函数,代码如下:

复制代码
 1 void SocketListener::runListener() { 2  3       SocketClientCollection pendingList; 4  5       while(1) {             // 无限循环,一直监听 6             SocketClientCollection::iterator it; 7             fd_set read_fds; 8             int rc = 0; 9             int max = -1;10 11             FD_ZERO(&read_fds);       // 清空文件描述符集read_fds12 13             if (mListen) {            // 如果需要监听14                 max = mSock;15                 FD_SET(mSock, &read_fds);               // 把mSock加入到read_fds16             }17 18             FD_SET(mCtrlPipe[0], &read_fds);                 // 把管道mCtrlPipe[0]也加入到read_fds19             if (mCtrlPipe[0] > max)20                 max = mCtrlPipe[0];21 22             pthread_mutex_lock(&mClientsLock);               // 对容器mClients的操作需要加锁23             for (it = mClients->begin(); it != mClients->end(); ++it) {    // mClient中保存的是NetlinkHandler对象的socket,或者CommandListener接入的socket24                   int fd = (*it)->getSocket();25                   FD_SET(fd, &read_fds);      // 遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds26                   if (fd > max) {                                     // 也加入到read_fds27                       max = fd;28                   }29             }30             pthread_mutex_unlock(&mClientsLock);31             SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);32             if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {              // 执行select调用,开始等待socket上的数据到来33                  if (errno == EINTR)                                              // 因为中断退出select,继续34                      continue;35                  SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);36                  sleep(1);                               // select出错,休眠1秒后继续37                  continue;38             } else if (!rc)39                  continue;                           // 如果fd上没有数据到达,继续40 41             if (FD_ISSET(mCtrlPipe[0], &read_fds)) {42                 char c = CtrlPipe_Shutdown;43                 TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));44                 if (c == CtrlPipe_Shutdown) {45                     break;46                 }47                 continue;48             }49             if (mListen && FD_ISSET(mSock, &read_fds)) {           // 如果是CommandListener对象上有连接请求50                 struct sockaddr addr;51                 socklen_t alen;52                 int c;53 54                 do {55                       alen = sizeof(addr);56                       c = accept(mSock, &addr, &alen);             // 接入连接请求57                       SLOGV("%s got %d from accept", mSocketName, c);58                 } while (c < 0 && errno == EINTR);                 // 如果是中断导致失败,重新接入59                 if (c < 0) {60                     SLOGE("accept failed (%s)", strerror(errno));61                     sleep(1);62                     continue;                                      // 接入发生错误,继续循环63                 }64                 pthread_mutex_lock(&mClientsLock);65                 mClients->push_back(new SocketClient(c, true, mUseCmdNum));   // 把接入的socket连接加入到mClients,这样再循环时就会监听到它的数据到达66                 pthread_mutex_unlock(&mClientsLock);67             }68             69             /* Add all active clients to the pending list first */70             pendingList.clear();71             pthread_mutex_lock(&mClientsLock);72             for (it = mClients->begin(); it != mClients->end(); ++it) {73                  SocketClient* c = *it;74                  int fd = c->getSocket();75                  if (FD_ISSET(fd, &read_fds)) {76                      pendingList.push_back(c);             // 如果mClients中的某个socket上有数据了,把它加入到pendingList列表中77                      c->incRef();78                  }79             }80             pthread_mutex_unlock(&mClientsLock);81 82             /* Process the pending list, since it is owned by the thread,* there is no need to lock it */83             while (!pendingList.empty()) {                 // 处理pendingList列表84                   /* Pop the first item from the list */85                   it = pendingList.begin();86                   SocketClient* c = *it;87                   pendingList.erase(it);                   // 把处理了的socket从pendingList列表中删除88                   /* Process it, if false is returned, remove from list */89                   if (!onDataAvailable(c)) {90                       release(c, false);   // 调用release()函数-->调用onDataAvailable()方法91                   }92                   c->decRef();93             }94       }95 }
复制代码

  SocketListener::runListener是线程真正执行的函数。

  以上runListener()函数虽然比较长,但这是一段标准的处理混合socket连接的代码,对于我们编写socket的程序大有帮助,这里先做简单了解。

 

  <--------接下来,我们继续分析......-------->

 

  runListener()函数收到从驱动传递的数据或者MountService传递的数据后,调用onDataAvailable()函数来处理,FrameworkListener类和NetlinkListener类都会重载这个函数。

  首先来分析一下NetlinkListener类的onDataAvailable()函数是如何实现的!

  直接上代码:

复制代码
 1 bool NetlinkListener::onDataAvailable(SocketClient *cli) 2 { 3       int socket = cli->getSocket(); 4       ssize_t count; 5       uid_t uid = -1; 6       /*从socket中读取kernel发送来的uevent消息*/ 7       count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid)); 8       if (count < 0) {                     // 如果count<0,进行错误处理 9           if (uid > 0)10               LOG_EVENT_INT(65537, uid);11           return false;12       }13 14       NetlinkEvent *evt = new NetlinkEvent();     // 创建NetlinkEvent对象15       if (evt->decode(mBuffer, count, mFormat)) {     // 调用decode()函数16           onEvent(evt);                           // 在NetlinkHandler中实现17       } else if (mFormat != NETLINK_FORMAT_BINARY) {18           SLOGE("Error decoding NetlinkEvent");19       }20       delete evt;21       return true;22 }
复制代码

  NetlinkListener类的onDataAvailable()函数首先调用uevent_kernel_multicast_uid_recv()函数来接收uevent消息。

  接收到消息后,会创建NetlinkEvent对象,然后调用它的decode()函数对消息进行解码,然后用得到的消息数据给NetlinkEvent对象的成员变量赋值。

  最后onDataAvailable()函数调用了onEvent()函数继续处理消息,onEvent()函数的代码如下:

复制代码
void NetlinkHandler::onEvent(NetlinkEvent *evt) {      VolumeManager *vm = VolumeManager::Instance();      const char *subsys = evt->getSubsystem();      if (!subsys) {          SLOGW("No subsystem found in netlink event");          return;      }      if (!strcmp(subsys, "block")) {          vm->handleBlockEvent(evt);           // 调用VolumeManager的handleBlockEvent()函数来处理      }}
复制代码

  NetlinkHandler的onEvent()函数中会判断event属于哪个子系统的,如果属于“block”(SD热插拔),则调用VolumeManager的handleBlockEvent()函数来处理,代码如下:

复制代码
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {      const char *devpath = evt->findParam("DEVPATH");      VolumeCollection::iterator it;      bool hit = false;      for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {           if (!(*it)->handleBlockEvent(evt)) {        // 对每个DirectVolume对象,调用它handleBlockEvent来处理这个event               hit = true;                             // 如果某个Volume对象处理了Event,则返回               break;           }      }.....}
复制代码

总结:本帖的源码分析先到这里为止,下一贴再分析DirectVolume对象的handleBlockEvent()函数以及CommandListener对象如何处理从MountService发送的命令数据,即我们之前还没有讨论的关于FrameworkListener的onDataAvailable()函数的代码!

PS:希望对Android手机开发、IOS、以及游戏(纯兴趣,白菜)和Java EE感兴趣的码友们互粉,这样我也能及时的看到你们的大神之作和经验之贴,感谢感谢!



原文地址 http://www.cnblogs.com/pepsimaxin/p/5195842.html

0 0
原创粉丝点击