本文關鍵詞:外部設備,由筆耕文化傳播整理發(fā)布。
博客聲明:
1. 使用 android2.1 源碼說明問題
2. 使用真機,操作系統(tǒng)是 android-2.1
3. 分享一下學習方法,不是為了測試而測試,請大家舉一反三
結合 Service 與 Broadcast 監(jiān)聽外部存儲設備的狀態(tài),通過測試主要想知道在我們操作外部存儲設備時候發(fā)生了哪些事情、以及 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 插入卡槽,發(fā)生的情況與 2 一致。
4. 在通知欄選擇 "計算機與 sd 卡之間復制文件",即共享
在彈出的對話框選擇 "裝載"
然后,我們再次在通知欄選擇 "關閉 usb 存儲設備",接下來發(fā)生的與 2 一致。
從這幾個測試,我們可以發(fā)現(xiàn)幾個規(guī)律:
1. 不管以何種方式卸載(正常卸載拔出、正常卸載不拔出
sd 卡、直接拔出 sd 卡)
系統(tǒng)都會發(fā)出下面的 action 廣播
ACTION_MEDIA_EJECT
ACTION_MEDIA_UNMOUNTED
2. 不管以何種方式安裝 sd 卡,系統(tǒng)都會發(fā)出下面的 action 廣播
3. ACTION_MEDIA_REMOVED 與 ACTION_MEDIA_UNMOUNTED 區(qū)別
ACTION_MEDIA_REMOVED
表示 sdcard 已經(jīng)從卡槽移除。
ACTION_MEDIA_UNMOUNTED
只可以說明 sd 卡沒有 mount 在文件系統(tǒng)上面,不可以說明其已經(jīng)從卡槽移除。
從測試 4 就可以看出這個端倪。
4. ACTION_MEDIA_REMOVED 與 ACTION_MEDIA_BAD_REMOVAL 區(qū)別
ACTION_MEDIA_BAD_REMOVAL
只有在直接拔出 sd 卡時,系統(tǒng)才會發(fā)送這樣的 action 廣播。
ACTION_MEDIA_REMOVED
不管何種方式從卡槽拔出 sd 卡時,系統(tǒng)就會發(fā)送這樣的 action 廣播。
5. 選擇通過 usb 共享,系統(tǒng)一定會發(fā)出下面的
action 廣播
ACTION_MEDIA_SHARED
ok,明白上面的道理(你基于的開發(fā)平臺是否是這樣,你還需要測試,我這里只是拋磚引玉),可以在接收到這些廣播的時候,根據(jù) action 寫自己的邏輯代碼了。如:
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_MEDIA_EJECT.equals(action)) {
// 本人感覺 ACTION_MEDIA_EJECT 比
// ACTION_MEDIA_UNMOUNTED 好
// sd 卡不可用
} else if (Intent.ACTION_MEDIA_REMOVED.equals(action)) {
// sd 卡已經(jīng)被移除卡槽
} else if (Intent.ACTION_MEDIA_SHARED.equals(action)) {
// 選擇通過 usb 共享
} else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
// sd 卡可用
}
}
但是這里提醒一下:
接收到 ACTION_MEDIA_EJECT 廣播之后,sd 卡還是可以讀寫的,
直到接收到 ACTION_MEDIA_REMOVED、ACTION_MEDIA_UNMOUNTED等廣播之后,sd 卡才不可以讀寫。
可以借助 Music 源碼 MediaPlaybackService.java 看看:
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
saveQueue(true);
mQueueIsSaveable = false;
closeExternalStorageFiles(intent.getData().getPath());
} else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
mMediaMountedCount++;
mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
reloadQueue();
mQueueIsSaveable = true;
notifyChange(QUEUE_CHANGED);
notifyChange(META_CHANGED);
}
}
到這個時候,我們應該搞明白是系統(tǒng)哪個類發(fā)出這樣的廣播?有沒有新的發(fā)現(xiàn)?
android2.1/frameworks/base/services/java/com/android/server/MountService.java
與其相關的類是
android2.1/frameworks/base/services/java/com/android/server/MountListener.java
繼續(xù)跟蹤 MountService.java
, 我們會發(fā)現(xiàn)實例化 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 卡)
那麼這個返回的路徑就不一樣,可以根據(jù)返回的路徑判斷你當前操作的是哪個設備了!
耶耶,酷比嘞!
在 MountService.java
里面還有一個與眾不同的地方:
void notifyMediaMounted(String path, boolean readOnly) {
setMediaStorageNotification(0, 0, 0, false, false, null);
updateUsbMassStorageNotification(false, false);
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + path));
intent.putExtra("read-only", readOnly);
mMounted = true;
mContext.sendBroadcast(intent);
}
intent.putExtra("read-only",
readOnly)
其中 readOnly
是一個 boolean 值,在 onReceive 里面
只有 action 是 ACTION_MEDIA_MOUNTED,接收到該值是 false.
-------------
附錄
PlayerService.java
package mark.zhang;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
public class PlayerService extends Service {
private static final String TAG = "PlayerService";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
registerReceivers();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy------");
super.onDestroy();
unregisterReceivers();
}
private BroadcastReceiver externalStorageReceiver = null;
/**
* 注冊廣播
*/
private void registerReceivers() {
if (externalStorageReceiver == null) {
externalStorageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final String path = intent.getData().getPath();
Log.d(TAG, "receive action = " + action);
boolean value = intent.getBooleanExtra("read-only", true);
Log.d(TAG, "external storage path = " + path);
Log.d(TAG, "external storage value = " + value);
}
};
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
filter.addAction(Intent.ACTION_MEDIA_BUTTON);
filter.addAction(Intent.ACTION_MEDIA_CHECKING);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_NOFS);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
filter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
// 必須添加,否則無法接收到廣播
filter.addDataScheme("file");
registerReceiver(externalStorageReceiver, filter);
}
}
/**
* 取消注冊
*/
private void unregisterReceivers() {
if (externalStorageReceiver != null) {
unregisterReceiver(externalStorageReceiver);
externalStorageReceiver = null;
}
}
}
本文關鍵詞:外部設備,由筆耕文化傳播整理發(fā)布。
本文編號:
211450
本文鏈接:http://sikaile.net/wenshubaike/xxkj/211450.html