现在的位置: 首页 > 综合 > 正文

Android 2.3 SD卡挂载流程浅析(七)

2013年10月14日 ⁄ 综合 ⁄ 共 22387字 ⁄ 字号 评论关闭

   前面六篇文章:

     
《Android 2.3 SD卡挂载流程浅析(一)》

     
《Android 2.3 SD卡挂载流程浅析(二)》

     
《Android 2.3 SD卡挂载流程浅析(三)》

     
《Android 2.3 SD卡挂载流程浅析(四)》

     
《Android 2.3 SD卡挂载流程浅析(五)》

     
《Android 2.3 SD卡挂载流程浅析(六)》

        前面六篇文章从底向上分析了SD卡挂载的流程,前面五篇文章主要是从底层往上,而第六篇文章则反其道而行之,从上层的设置界面开始往下分析,那么本篇文章会是一个引爆点,也就是说,这篇文章是从下往上和从上往下的一个交接处。因为过了段时间了,我们需要总结一下前面的文章,权当复习吧。哈哈,还是先祭处我们的老老图(T_T...)如下:


        这是SD卡挂载的整个流程图,首先插入SD卡后,触发硬件中断,Kernel发出uevent;其次通过NetLinkManager初步处理接收到的uevent然后往上传递处理之后的事件信息;然后VolumeManager接着处理经过初步处理之后的时间信息并将信息进行更细致的划分和处理并传递给上层;最后上层接收到SD卡挂载后的信息并显示在界面上。

        以上就是整个SD卡挂载的大致流程,下面是大致的流程图:

       废话不多说,从这张图大家应该可以理解SD卡的挂载了吧。那么前面五篇博文从底向上,从Kernel发出的uevent一直到Vold,而第六篇博文则是从Setting开始一直到MountService,那么本文主要则描述底层信息最后是如何传递到上层的。如果大家遗忘了的话,还请看看前面的文章。

       在第五篇博文中,我们分析到了MountService中的private void updatePublicVolumeState(String path, String state)方法,在该方法中我们当时只分析到了:

 

  1. <span style="font-size:18px;">synchronized (mListeners) {  
  2.             for (int i = mListeners.size() -1; i >= 0; i--) {  
  3.                 MountServiceBinderListener bl = mListeners.get(i);  
  4.                 try {  
  5.                     bl.mListener.onStorageStateChanged(path, oldState, state);  
  6.                 } catch (RemoteException rex) {  
  7.                     Slog.e(TAG, "Listener dead");  
  8.                     mListeners.remove(i);  
  9.                 } catch (Exception ex) {  
  10.                     Slog.e(TAG, "Listener failed", ex);  
  11.                 }  
  12.             }  
  13.         }</span>  

      在第六篇博文中,我们知道了SD卡挂载的消息是通过注册StorageManager的存储设备状态变更监听器来实现的,如下代码:

  1. <span style="font-size:18px;">if (mStorageManager == null) {  
  2.             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);  
  3.             mStorageManager.registerListener(mStorageListener);  
  4. }</span>  


       那么也就是说,如果要通知到上层改变SD卡的挂载状态,底层的信息一定会触发上层注册的监听器,那么我就开始我们的旅程吧!

       1.踏上征程

       我们先继续查看MountService中的private void updatePublicVolumeState(String
path, String state)方法中的同步块中的方法:

  1. <span style="font-size:18px;">MountServiceBinderListener bl = mListeners.get(i);</span>  

       该方法是从ArrayList<MoutServiceBinderListener>中获取之前存储的bl对象,如果大家忘记了我们存储bl的地方的话,可以回头看一下上一篇博文的讲解,实际上是在MountService.java中的registerListener()方法中通过mListeners.add(bl);方法添加的。

       我们接着看:

  1. <span style="font-size:18px;">bl.mListener.onStorageStateChanged(path, oldState, state);</span>  


       我们在第五篇博文《Android 2.3 SD卡挂载流程浅析(五)》的最后,分析到了这句代码,但是只能卡在那里。但我们经过第六篇博文

《Android 2.3 SD卡挂载流程浅析(六)》
的讲解之后,我们应该能够顺利进行了。

       这里我们跟踪mListener到MountServiceBinderListener这个类中:

  1. <span style="font-size:18px;">private final class MountServiceBinderListener implements IBinder.DeathRecipient {  
  2.         final IMountServiceListener mListener;  
  3.   
  4.         MountServiceBinderListener(IMountServiceListener listener) {  
  5.             mListener = listener;  
  6.   
  7.         }  
  8.        public void binderDied() {  
  9.             if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");  
  10.             synchronized (mListeners) {  
  11.             mListeners.remove(this);  
  12.              mListener.asBinder().unlinkToDeath(this0);  
  13.        }  
  14. }  
  15.     }</span>  


       这里我们看到mListener=listener;因为我们前面已讲过,IMountServiceListener是一个接口,不可能实例化对象,因此传递的参数可以肯定是实现了它的类的对象。那么我们跟踪这里的MountServieBinderListener(IMountServiceListener listener)看传递进来的参数是什么:

      

  1. <span style="font-size:18px;">public void registerListener(IMountServiceListener listener) {  
  2.         synchronized (mListeners) {  
  3.             MountServiceBinderListener bl = new MountServiceBinderListener(listener);  
  4.             try {  
  5.                 listener.asBinder().linkToDeath(bl, 0);  
  6.                 mListeners.add(bl);  
  7.             } catch (RemoteException rex) {  
  8.                 Slog.e(TAG, "Failed to link to listener death");  
  9.             }  
  10.         }  
  11. }</span>  


        在MountService中的registerListener()方法中我们找到了它的影子,但是一看代码就知道,这里也只是一个中转站,但是看过前面文章的朋友因该想起来了吧。对,没错,这就是我前面提到过经过AIDL之后,最终调用的registerListener()方法。

        那么这里MountService中的MountServiceBinderListener类中的mListener对象,是从StorageManager中的MountServiceBinderListener转换来的,转换过程前面已经有阐述这里不多讲。也就是说这里的mListener可以当作StorageManager中的MountServiceBinderListener的对象,那么这里bl.mListener.onStorageStateChanged(path,
oldState, state);的方法实际上也是StorageManager中的MountServiceBinderListener的方法。所以我们找到StorageManager中的MountServiceBinderListener类:

  1. <span style="font-size:18px;">private class MountServiceBinderListener extends IMountServiceListener.Stub {  
  2.         public void onUsbMassStorageConnectionChanged(boolean available) {  
  3.             final int size = mListeners.size();  
  4.             for (int i = 0; i < size; i++) {  
  5.                 mListeners.get(i).sendShareAvailabilityChanged(available);  
  6.             }  
  7.         }  
  8.   
  9.         public void onStorageStateChanged(String path, String oldState, String newState) {  
  10.             final int size = mListeners.size();  
  11.             for (int i = 0; i < size; i++) {  
  12.                 mListeners.get(i).sendStorageStateChanged(path, oldState, newState);  
  13.             }  
  14.         }  
  15.     }</span>  

       果然其中有我们寻找的onStrageStateChanged()方法,那么我们就继续分析吧。

      2.披荆斩棘,直捣黄龙

     

  1. <span style="font-size:18px;">public void onStorageStateChanged(String path, String oldState, String newState) {  
  2.             final int size = mListeners.size();  
  3.             for (int i = 0; i < size; i++) {  
  4.                 mListeners.get(i).sendStorageStateChanged(path, oldState, newState);  
  5.        }  
  6. }</span>  


       首先看到mListeners.get(i)这个方法我们直接就回联想到ArrayList,那么这个是这个ArrayList中存放到底是什么对象呢?如果大家对上一篇文章有影响的话应该不会陌生,这里的存放的是ListenerDelegate的对象。好了我们继续跳转到sendStorageStateChanged()方法中去:

  1. <span style="font-size:18px;">void sendStorageStateChanged(String path, String oldState, String newState) {  
  2.             StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);  
  3.             mHandler.sendMessage(e.getMessage());  
  4. }</span>  


       那么继续跟踪StroageStateChangedStorageEvent();这个构造方法:

  1. <span style="font-size:18px;">private class StorageStateChangedStorageEvent extends StorageEvent {  
  2.         public String path;  
  3.         public String oldState;  
  4.         public String newState;  
  5.   
  6.         public StorageStateChangedStorageEvent(String p, String oldS, String newS) {  
  7.             super(EVENT_STORAGE_STATE_CHANGED);  
  8.             path = p;  
  9.             oldState = oldS;  
  10.             newState = newS;  
  11.         }  
  12.     }</span>  


      在这个类中的构造方法中,我们可以看到它通过super掉了父类的构造方法,我们继续跟踪该super方法:

  1. <span style="font-size:18px;">private class StorageEvent {  
  2.         static final int EVENT_UMS_CONNECTION_CHANGED = 1;  
  3.         static final int EVENT_STORAGE_STATE_CHANGED = 2;  
  4.         static final int EVENT_OBB_STATE_CHANGED = 3;  
  5.   
  6.         private Message mMessage;  
  7.   
  8.         public StorageEvent(int what) {  
  9.             mMessage = Message.obtain();  
  10.             mMessage.what = what;  
  11.             mMessage.obj = this;  
  12.         }  
  13.   
  14.         public Message getMessage() {  
  15.             return mMessage;  
  16.         }  
  17. }</span>  


      哈哈,原来是封装了一个Message,这里的what值是2。那么我们回到:

  1. <span style="font-size:18px;">void sendStorageStateChanged(String path, String oldState, String newState) {  
  2.             StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);  
  3.             mHandler.sendMessage(e.getMessage());  
  4. }</span>  


      我们接着看mHandler.sendMessage(e.getMessage());方法,通过前面的代码,我们可以看到,e.getMessage()返回的是一个Message对象,正好这里通过mHandler发送给出去。

      可能这里有朋友要问了,我怎么知道发送给谁呢?谁来接收呢?别急,我们回忆一下,在上一篇博文中,我们通过从上往下分析,最后我们分析到ListenerDelegate这个类中,发现了其中的handleMessage()方法,这里我把这个类贴出来:

  1. <span style="font-size:18px;">private class ListenerDelegate {  
  2.         final StorageEventListener mStorageEventListener;  
  3.         private final Handler mHandler;  
  4.   
  5.         ListenerDelegate(StorageEventListener listener) {  
  6.             mStorageEventListener = listener;  
  7.             mHandler = new Handler(mTgtLooper) {  
  8.                 @Override  
  9.                 public void handleMessage(Message msg) {  
  10.                     StorageEvent e = (StorageEvent) msg.obj;  
  11.   
  12.                     if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {  
  13.                         UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;  
  14.                         mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);  
  15.                     } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {  
  16.                         StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;  
  17.                         mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);  
  18.                     } else {  
  19.                         Log.e(TAG, "Unsupported event " + msg.what);  
  20.                     }  
  21.                 }  
  22.             };  
  23.         }  
  24.   
  25.         StorageEventListener getListener() {  
  26.             return mStorageEventListener;  
  27.         }  
  28.   
  29.         void sendShareAvailabilityChanged(boolean available) {  
  30.             UmsConnectionChangedStorageEvent e = new UmsConnectionChangedStorageEvent(available);  
  31.             mHandler.sendMessage(e.getMessage());  
  32.         }  
  33.   
  34.         void sendStorageStateChanged(String path, String oldState, String newState) {  
  35.             StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);  
  36.             mHandler.sendMessage(e.getMessage());  
  37.         }  
  38. }</span>  


       哈哈,handleMessage()远在天边近在眼前啊。

       那我们赶紧继续吧。我们在handleMessage()中找到如下方法:

  1. <span style="font-size:18px;">StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;  
  2. mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);</span>  

     

       这里的mStorageEventListener,是通过ListenerDelegate(StorageEventListener listener)的构造方法传递的StorageEventListener对象,因此这里也就是去调用StorageEventListener中的onStorageStateChanged()方法,传递的参数:ev.path,ev.oldState,ev.newState
这些参数都是从MountService那里传送过来的。

       3.胜利在望

       现在我们回到Memory.java中,这里为什么要回到Memory.java中呢?如果我们直接去StorageEventListener这个类中会发现实际山该类只是一个接口,其中只有空方法。我们回到Memory.java中可以看到:

  1. <span style="font-size:18px;">StorageEventListener mStorageListener = new StorageEventListener() {  
  2.   
  3.         @Override  
  4.         public void onStorageStateChanged(String path, String oldState, String newState) {  
  5.             Log.i(TAG, "Received storage state changed notification that " +  
  6.                     path + " changed state from " + oldState +  
  7.                     " to " + newState);  
  8.             updateMemoryStatus();  
  9.         }  
  10.     };</span>  


       在Memory.java中,我们可以看到通过匿名内部类的方法,实现了StorageEventListener中的onStorageStateChanged()方法,因此,最终的调用这里的onStorageStateChanged()方法。这样SD卡挂载的消息我们可以通过抓取这里的log来看到。

       接下来分析updateMemoryStatus():

  1. <span style="font-size:18px;">private void updateMemoryStatus() {  
  2.         String status = Environment.getExternalStorageState();  
  3.         String readOnly = "";  
  4.         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {  
  5.             status = Environment.MEDIA_MOUNTED;  
  6.             readOnly = mRes.getString(R.string.read_only);  
  7.         }  
  8.   
  9. //SD卡已挂载    
  10.         if (status.equals(Environment.MEDIA_MOUNTED)) {  
  11.             if (!Environment.isExternalStorageRemovable()) {  
  12.                 // This device has built-in storage that is not removable.
      
  13.                 // There is no reason for the user to unmount it.
      
  14.                 if (mSdMountToggleAdded) {  
  15.                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);  
  16.                     mSdMountToggleAdded = false;  
  17.                 }  
  18.             }  
  19.             try {  
  20.                 File path = Environment.getExternalStorageDirectory();  
  21.                 StatFs stat = new StatFs(path.getPath());  
  22. //获取SD卡的块容量 总块数 可用块数   
  23.                 long blockSize = stat.getBlockSize();  
  24.                 long totalBlocks = stat.getBlockCount();  
  25.                 long availableBlocks = stat.getAvailableBlocks();  
  26.                   
  27. //SD卡总容量 可用容量 这些东西说白了你就把它当作textview.setText("可用容量");一样的就可以了
      
  28.                 mSdSize.setSummary(formatSize(totalBlocks * blockSize));  
  29.                 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);  
  30.   
  31.                 mSdMountToggle.setEnabled(true);  
  32.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));  
  33.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));  
  34.   
  35.             } catch (IllegalArgumentException e) {  
  36.                 // this can occur if the SD card is removed, but we haven't received the 
      
  37.                 // ACTION_MEDIA_REMOVED Intent yet.
      
  38.                 status = Environment.MEDIA_REMOVED;  
  39.             }  
  40.               
  41.         } else {  
  42.             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));  
  43.             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));  
  44.   
  45.   
  46.             if (!Environment.isExternalStorageRemovable()) {  
  47.                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {  
  48.                     if (!mSdMountToggleAdded) {  
  49.                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);  
  50.                         mSdMountToggleAdded = true;  
  51.                     }  
  52.                 }  
  53.             }  
  54.   
  55.             if (status.equals(Environment.MEDIA_UNMOUNTED) ||  
  56.                 status.equals(Environment.MEDIA_NOFS) ||  
  57.                 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {  
  58.                 mSdMountToggle.setEnabled(true);  
  59.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  60.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));  
  61.             } else {  
  62.                 mSdMountToggle.setEnabled(false);  
  63.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  64.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));  
  65.             }  
  66.         }  
  67.   
  68.         File path = Environment.getDataDirectory();  
  69.         StatFs stat = new StatFs(path.getPath());  
  70.         long blockSize = stat.getBlockSize();  
  71.         long availableBlocks = stat.getAvailableBlocks();  
  72.         findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));  
  73. }</span>  


        分析到这里我们的目的已经实现了。如果忘记了我们的目的那我这里重申一次:从SD卡插入一直到SD卡挂载信息显示到系统设置界面的过程分析。

        总结:先说一下分析SD卡挂载流程这件事情的起因。因为工作原因,接触到了Android,但是在检测SD卡挂载着一块始终有些问题,多方搜索后无果,那我就自己硬着头皮啃吧。一开始找起点,怎么找呢?发现系统设置里面有SD卡挂载的相关信息,那么就开始从Android系统设置的源码开始找,逐渐的发现,越深入越复杂。对于我这个新手来说很多东西不懂,经过不断的搜索,以及查询相关资料,最终奋战了2个星期将SD卡的挂载流程大致分析清楚了。因为自己也是新手,所以很多东西分析得不一定都对,所以还望读者指出错误。

        经过本次源码分析,学习到了很多知识,这些知识其实在书本上也有提到,但需要真正理解其意思的话,必须要通过实践。

        附上前六篇博文地址:

     
《Android 2.3 SD卡挂载流程浅析(一)》

     
《Android 2.3 SD卡挂载流程浅析(二)》

     
《Android 2.3 SD卡挂载流程浅析(三)》

     
《Android 2.3 SD卡挂载流程浅析(四)》

     
《Android 2.3 SD卡挂载流程浅析(五)》

     
《Android 2.3 SD卡挂载流程浅析(六)》

       最后附上参考文章及博客:

http://blog.163.com/lzh_327/blog/static/7219480201122103947556/

http://blog.csdn.net/free2o/article/details/4174275

http://blog.csdn.net/sustzombie/article/details/6120903

http://hi.baidu.com/nnqidhbd/blog/item/15df0151cb2c0d03377abe72.html

http://www.eoeandroid.com/thread-32766-1-1.html

抱歉!评论已关闭.