挺拔sdcard時,各種廣播的區別


本文轉自 http://blog.csdn.net/androidbluetooth/article/details/7603428

博客聲明:

 

1. 使用 android2.1 源碼說明問題

 

2. 使用真機,操作系統是 android-2.1

 

3. 分享一下學習方法,不是為了測試而測試,請大家舉一反三

 

結合 Service 與 Broadcast 監聽外部存儲設備的狀態,通過測試主要想知道在我們操作外部存儲設備時候發生了哪些事情、以及 Intent 幾個 Action 到底是何意?

 

測試代碼見 附錄,至於如何啟動這個 Service,隨您意!

 

主要的 Action 

 

                                 

 

 

注冊這 13 個 action,然后運行 app ,點擊 back 服務退至后台。

 

now,ready!來操作 sdcard。

 

1.  直接拔掉 sdcard

 

 

2.  再次將 sdcard 插入卡槽

 

 


 

先大概 1-3 秒的 media checking,然后才是 mounted -- scanner started -- scanner finished

 

3.  在通知欄卸載 sdcard

 


 

 

緊接着,從卡槽拔出 sdcard(必須拔出,才會接收到下面的 action)

 


 

可以看出,這種情況屬於正常卸載 sdcard,不是強制拔出。不同於 1.

 

這個時候,你將 sdcard 插入卡槽,發生的情況與 2 一致。

 

 

4. 在通知欄選擇 "計算機與 sd 卡之間復制文件",即共享

 

在彈出的對話框選擇 "裝載"

 


 

然后,我們再次在通知欄選擇 "關閉 usb 存儲設備",接下來發生的與 2 一致。

 

 

從這幾個測試,我們可以發現幾個規律:

 

1.  不管以何種方式卸載(正常卸載拔出、正常卸載不拔出 sd 卡、直接拔出 sd 卡

 

系統都會發出下面的 action 廣播

 

ACTION_MEDIA_EJECT

 

ACTION_MEDIA_UNMOUNTED

 

 

2.  不管以何種方式安裝 sd 卡,系統都會發出下面的 action 廣播

 


 

 

3.  ACTION_MEDIA_REMOVED 與 ACTION_MEDIA_UNMOUNTED 區別

 

 

ACTION_MEDIA_REMOVED 

 

表示 sdcard 已經從卡槽移除。

 

ACTION_MEDIA_UNMOUNTED 

 

只可以說明 sd 卡沒有 mount 在文件系統上面,不可以說明其已經從卡槽移除。

 

從測試 4 就可以看出這個端倪。

 

 

4.  ACTION_MEDIA_REMOVED 與 ACTION_MEDIA_BAD_REMOVAL 區別

 

 

ACTION_MEDIA_BAD_REMOVAL 

 

只有在直接拔出 sd 卡時,系統才會發送這樣的 action 廣播。

 

ACTION_MEDIA_REMOVED 

 

不管何種方式從卡槽拔出 sd 卡時,系統就會發送這樣的 action 廣播。

 

 

5.  選擇通過 usb 共享,系統一定會發出下面的 action 廣播

 

ACTION_MEDIA_SHARED

 

 

ok,明白上面的道理(你基於的開發平台是否是這樣,你還需要測試,我這里只是拋磚引玉),可以在接收到這些廣播的時候,根據 action 寫自己的邏輯代碼了。如:

 

 

[java]  view plain copy print ?
  1.        @Override  
  2. public void onReceive(Context context, Intent intent) {  
  3.     final String action = intent.getAction();  
  4.     if (Intent.ACTION_MEDIA_EJECT.equals(action)) {  
  5.         // 本人感覺 ACTION_MEDIA_EJECT 比  
  6.         //  ACTION_MEDIA_UNMOUNTED 好  
  7.           
  8.         // sd 卡不可用  
  9.     } else if (Intent.ACTION_MEDIA_REMOVED.equals(action)) {  
  10.         // sd 卡已經被移除卡槽  
  11.     } else if (Intent.ACTION_MEDIA_SHARED.equals(action)) {  
  12.         // 選擇通過 usb 共享  
  13.     } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {  
  14.         // sd 卡可用  
  15.     }  
  16. }  

 

但是這里提醒一下:

 

接收到 ACTION_MEDIA_EJECT 廣播之后,sd 卡還是可以讀寫的,

直到接收到 ACTION_MEDIA_REMOVED、ACTION_MEDIA_UNMOUNTED等廣播之后,sd 卡才不可以讀寫。

 

可以借助 Music 源碼 MediaPlaybackService.java 看看:

 

 

[java]  view plain copy print ?
  1. @Override  
  2. public void onReceive(Context context, Intent intent) {  
  3.     String action = intent.getAction();  
  4.     if (action.equals(Intent.ACTION_MEDIA_EJECT)) {  
  5.         saveQueue(true);  
  6.         mQueueIsSaveable = false;  
  7.         closeExternalStorageFiles(intent.getData().getPath());  
  8.     } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {  
  9.         mMediaMountedCount++;  
  10.         mCardId = MusicUtils.getCardId(MediaPlaybackService.this);  
  11.         reloadQueue();  
  12.         mQueueIsSaveable = true;  
  13.         notifyChange(QUEUE_CHANGED);  
  14.         notifyChange(META_CHANGED);  
  15.     }  
  16. }  



 

到這個時候,我們應該搞明白是系統哪個類發出這樣的廣播?有沒有新的發現?

 

android2.1/frameworks/base/services/java/com/android/server/MountService.java

 

與其相關的類是

 

android2.1/frameworks/base/services/java/com/android/server/MountListener.java

 

繼續跟蹤 MountService.java , 我們會發現實例化 intent:

 

intent = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));

 

都包含一個 scheme 為 file 的 path,那麽這個 path 是什么呢?

 

可以在 onReceive 方法里面得到這個值

 

final String path = intent.getData().getPath()

 

其實,就是 "/sdcard" (即 sd 卡路徑)。

 

這個信息很有用!!!

 

比如你的手機可以外括除了 sd 卡的其它外部設備(如 u 盤、map 卡)

那麽這個返回的路徑就不一樣,可以根據返回的路徑判斷你當前操作的是哪個設備了!

 

耶耶,酷比嘞!

 

在 MountService.java 里面還有一個與眾不同的地方:

 

 

[java]  view plain copy print ?
  1. void notifyMediaMounted(String path, boolean readOnly) {  
  2.         setMediaStorageNotification(000falsefalsenull);  
  3.         updateUsbMassStorageNotification(falsefalse);  
  4.         Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,   
  5.                 Uri.parse("file://" + path));  
  6.         intent.putExtra("read-only", readOnly);  
  7.         mMounted = true;  
  8.         mContext.sendBroadcast(intent);  
  9. }  



 

 intent.putExtra("read-only", readOnly)

 

其中 readOnly 是一個 boolean 值,在 onReceive 里面 只有 action 是 ACTION_MEDIA_MOUNTED,接收到該值是 false.

 

 

 

 

------------- 附錄 

 

PlayerService.java

 

 

[java]  view plain copy print ?
  1. package mark.zhang;  
  2.   
  3. import android.app.Service;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.IntentFilter;  
  8. import android.os.IBinder;  
  9. import android.util.Log;  
  10.   
  11. public class PlayerService extends Service {  
  12.     private static final String TAG = "PlayerService";  
  13.   
  14.     @Override  
  15.     public IBinder onBind(Intent intent) {  
  16.         return null;  
  17.     }  
  18.   
  19.     @Override  
  20.     public void onCreate() {  
  21.         super.onCreate();  
  22.         registerReceivers();  
  23.     }  
  24.   
  25.     @Override  
  26.     public int onStartCommand(Intent intent, int flags, int startId) {  
  27.         return super.onStartCommand(intent, flags, startId);  
  28.     }  
  29.   
  30.     @Override  
  31.     public void onDestroy() {  
  32.         Log.d(TAG, "onDestroy------");  
  33.         super.onDestroy();  
  34.         unregisterReceivers();  
  35.     }  
  36.   
  37.     private BroadcastReceiver externalStorageReceiver = null;  
  38.   
  39.     /** 
  40.      * 注冊廣播 
  41.      */  
  42.     private void registerReceivers() {  
  43.         if (externalStorageReceiver == null) {  
  44.             externalStorageReceiver = new BroadcastReceiver() {  
  45.   
  46.                 @Override  
  47.                 public void onReceive(Context context, Intent intent) {  
  48.                     final String action = intent.getAction();  
  49.                     final String path = intent.getData().getPath();  
  50.                     Log.d(TAG, "receive action = " + action);  
  51.                     boolean value = intent.getBooleanExtra("read-only"true);  
  52.                     Log.d(TAG, "external storage path = " + path);  
  53.                     Log.d(TAG, "external storage value = " + value);  
  54.                 }  
  55.             };  
  56.   
  57.             final IntentFilter filter = new IntentFilter();  
  58.             filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);  
  59.             filter.addAction(Intent.ACTION_MEDIA_BUTTON);  
  60.             filter.addAction(Intent.ACTION_MEDIA_CHECKING);  
  61.             filter.addAction(Intent.ACTION_MEDIA_EJECT);  
  62.             filter.addAction(Intent.ACTION_MEDIA_MOUNTED);  
  63.             filter.addAction(Intent.ACTION_MEDIA_NOFS);  
  64.             filter.addAction(Intent.ACTION_MEDIA_REMOVED);  
  65.             filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);  
  66.             filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);  
  67.             filter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);  
  68.             filter.addAction(Intent.ACTION_MEDIA_SHARED);  
  69.             filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);  
  70.             filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);  
  71.             // 必須添加,否則無法接收到廣播  
  72.             filter.addDataScheme("file");  
  73.   
  74.             registerReceiver(externalStorageReceiver, filter);  
  75.         }  
  76.     }  
  77.   
  78.     /** 
  79.      * 取消注冊 
  80.      */  
  81.     private void unregisterReceivers() {  
  82.         if (externalStorageReceiver != null) {  
  83.             unregisterReceiver(externalStorageReceiver);  
  84.             externalStorageReceiver = null;  
  85.         }  
  86.     }  
  87.   
  88. }  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM