這篇文章從系統源代碼分析,講述如何將程序創建的多媒體文件加入系統的媒體庫,如何從媒體庫刪除,以及大多數程序開發者經常遇到的無法添加到媒體庫的問題等。本人將通過對源代碼的分析,一一解釋這些問題。
Android中的多媒體文件掃描機制
Android提供了一個很棒的程序來處理將多媒體文件加入的媒體庫中。這個程序就是MediaProvider,現在我們簡單看以下這個程序。首先看一下它的Receiver
MediaScannerReceiver只接收符合action和數據規則正確的intent。
MediaScannerReciever如何處理Intent
1.當且僅當接收到action android.intent.action.BOOT_COMPLETED才掃描內部存儲(非內置和外置sdcard)
2.除了action為android.intent.action.BOOT_COMPLETED 的以外的intent都必須要有數據傳遞。
3.當收到 Intent.ACTION_MEDIA_MOUNTED intent,掃描Sdcard
4.當收到 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent,檢測沒有問題,將掃描單個文件。
MediaScannerService如何工作
實際上MediaScannerReceiver并不是真正處理掃描工作,它會啟動一個叫做MediaScannerService的服務。我們繼續看MediaProvider的manifest中關于service的部分。
MediaScannerService中的scanFile方法
MediaScannerService中的scan方法
ContentValues values = new ContentValues();
values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
Uri uri = Uri.parse("file://" + directories[0]);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
try {
if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
openDatabase(volumeName);
}
MediaScanner scanner = createMediaScanner();
scanner.scanDirectories(directories, volumeName);
} catch (Exception e) {
Log.e(TAG, "exception in MediaScanner.scan()", e);
}
getContentResolver().delete(scanUri, null, null);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
mWakeLock.release();
}
MediaScannerService中的createMediaScanner方法
return scanner;
}
從上面可以發現,真正工作的其實是android.media.MediaScanner.java 具體掃描過程就請點擊左側鏈接查看。
如何掃描一個剛創建的文件
這里介紹兩種方式來實現將新創建的文件加入媒體庫。
最簡單的方式
只需要發送一個正確的intent廣播到MediaScannerReceiver即可。
上面的極簡方法大多數情況下正常工作,但是有些情況下是不會工作的,稍后的部分會介紹。即使你使用上述方法成功了,還是建議你繼續閱讀稍后的為什么發廣播不成功的部分。
使用MediaScannerConnection
MediaScannerConnection的scanFile方法從2.2(API 8)開始引入。
創建一個MediaScannerConnection對象然后調用scanFile方法
很簡單,參考http://developer.android.com/reference/android/media/MediaScannerConnection.html
如何掃描多個文件
1.發送多個Intent.ACTION_MEDIA_SCANNER_SCAN_FILE廣播
2.使用MediaScannerConnection,傳入要加入的路徑的數組。
為什么發送MEDIA_SCANNER_SCAN_FILE廣播不生效
關于為什么有些設備上不生效,很多人認為是API原因,其實不是的,這其實和你傳入的文件路徑有關系。看一下接收者Receiver的onReceive代碼。
Log.d(TAG, "action: " + action + " path: " + path);
if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
// scan whenever any volume is mounted
scan(context, MediaProvider.EXTERNAL_VOLUME);
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
path != null && path.startsWith(externalStoragePath + "/")) {
scanFile(context, path);
}
}
}
}
所有的部分都正確除了傳入的路徑。因為你可能硬編碼了文件路徑。因為有一個這樣的判斷path.startsWith(externalStoragePath + "/"),這里我舉一個簡單的小例子。
我們看一下輸出日志,分析原因。
上述輸出分析,你發送的廣播,action是正確的,數據規則也是正確的,而且你的文件路徑也是存在的,但是,文件的路徑/sdcard/1390136305831_add.png并不是以外部存儲根路徑/mnt/sdcard/開頭。所以掃描操作沒有開始,導致文件沒有加入到媒體庫。所以,請檢查文件的路徑。
如何從多媒體庫中移除
如果我們刪除一個多媒體文件的話,也就意味我們還需要將這個文件從媒體庫中刪除掉。
能不能簡簡單單發廣播?
僅僅發一個廣播能解決問題么?我倒是希望可以,但是實際上是不工作的,查看如下代碼即可明白。
File file = new File(path);
if (!file.exists()) {
return null;
}
// lastModified is in milliseconds on Files.
long lastModifiedSeconds = file.lastModified() / 1000;
// always scan the file, so we can return the content://media Uri for existing files
return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
false, true, MediaScanner.isNoMediaPath(path));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
}
}
正如上述代碼,會對文件是否存在進行檢查,如果文件不存在,直接停止向下執行。所以這樣是不行的。那怎么辦呢?
}
上述代碼是可以工作的,直接從MediaProvider刪除即可。 具體的刪除代碼請參考Code Snippet for Media on Android
One More Thing
你可以通過查看/data/data/com.android.providers.media/databases/external.db(不同系統略有不同)文件可以了解更多的信息。
新聞熱點
疑難解答
圖片精選