Android掃描多媒體檔案剖析
這篇文章從系統原始碼分析,講述如何將程式建立的多媒體檔案加入系統的媒體庫,如何從媒體庫刪除,以及大多數程式開發者經常遇到的無法新增到媒體庫的問題等。本人將通過對原始碼的分析,一一解釋這些問題。
Android中的多媒體檔案掃描機制
Android提供了一個很棒的程式來處理將多媒體檔案加入的媒體庫中。這個程式就是MediaProvider,現在我們簡單看以下這個程式。首先看一下它的Receiver
<receiver android:name="MediaScannerReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <data android:scheme="file" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.MEDIA_UNMOUNTED" /> <data android:scheme="file" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" /> <data android:scheme="file" /> </intent-filter> </receiver>
MediaScannerReceiver只接收符合action和資料規則正確的intent。
MediaScannerReciever如何處理Intent
- 當且僅當接收到action android.intent.action.BOOT_COMPLETED才掃描內部儲存(非內建和外接sdcard)
- 除了action為android.intent.action.BOOT_COMPLETED 的以外的intent都必須要有資料傳遞。
- 當收到 Intent.ACTION_MEDIA_MOUNTED intent,掃描Sdcard
- 當收到 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent,檢測沒有問題,將掃描單個檔案。
MediaScannerService如何工作
實際上MediaScannerReceiver並不是真正處理掃描工作,它會啟動一個叫做MediaScannerService的服務。我們繼續看MediaProvider的manifest中關於service的部分。
<service android:name="MediaScannerService" android:exported="true"> <intent-filter> <action android:name="android.media.IMediaScannerService" /> </intent-filter> </service>
MediaScannerService中的scanFile方法
private Uri scanFile(String path, String mimeType) { String volumeName = MediaProvider.EXTERNAL_VOLUME; openDatabase(volumeName); MediaScanner scanner = createMediaScanner(); return scanner.scanSingleFile(path, volumeName, mimeType); }
MediaScannerService中的scan方法
private void scan(String[] directories, String volumeName) { // don't sleep while scanning mWakeLock.acquire(); 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方法
private MediaScanner createMediaScanner() { MediaScanner scanner = new MediaScanner(this); Locale locale = getResources().getConfiguration().locale; if (locale != null) { String language = locale.getLanguage(); String country = locale.getCountry(); String localeString = null; if (language != null) { if (country != null) { scanner.setLocale(language + "_" + country); } else { scanner.setLocale(language); } } } return scanner; }
從上面可以發現,真正工作的其實是android.media.MediaScanner.java 具體掃描過程就請點選左側連結檢視。
如何掃描一個剛建立的檔案
這裡介紹兩種方式來實現將新建立的檔案加入媒體庫。
最簡單的方式
只需要傳送一個正確的intent廣播到MediaScannerReceiver即可。
String saveAs = "Your_Created_File_Path" Uri contentUri = Uri.fromFile(new File(saveAs)); Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri); getContext().sendBroadcast(mediaScanIntent);
上面的極簡方法大多數情況下正常工作,但是有些情況下是不會工作的,稍後的部分會介紹。即使你使用上述方法成功了,還是建議你繼續閱讀稍後的為什麼發廣播不成功的部分。
使用MediaScannerConnection
public void mediaScan(File file) { MediaScannerConnection.scanFile(getActivity(), new String[] { file.getAbsolutePath() }, null, new OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { Log.v("MediaScanWork", "file " + path + " was scanned seccessfully: " + uri); } }); }
MediaScannerConnection的scanFile方法從2.2(API 8)開始引入。
建立一個MediaScannerConnection物件然後呼叫scanFile方法
很簡單,參考http://developer.android.com/reference/android/media/MediaScannerConnection.html
如何掃描多個檔案
- 傳送多個Intent.ACTION_MEDIA_SCANNER_SCAN_FILE廣播
- 使用MediaScannerConnection,傳入要加入的路徑的陣列。
為什麼傳送MEDIA_SCANNER_SCAN_FILE廣播不生效
關於為什麼有些裝置上不生效,很多人認為是API原因,其實不是的,這其實和你傳入的檔案路徑有關係。看一下接收者Receiver的onReceive程式碼。
public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Uri uri = intent.getData(); if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { // scan internal storage scan(context, MediaProvider.INTERNAL_VOLUME); } else { if (uri.getScheme().equals("file")) { // handle intents related to external storage String path = uri.getPath(); String externalStoragePath = Environment.getExternalStorageDirectory().getPath(); 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 + "/")
,這裡我舉一個簡單的小例子。
final String saveAs = "/sdcard/" + System.currentTimeMillis() + "_add.png"; Uri contentUri = Uri.fromFile(new File(saveAs)); Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri); getContext().sendBroadcast(mediaScanIntent); Uri uri = mediaScanIntent.getData(); String path = uri.getPath(); String externalStoragePath = Environment.getExternalStorageDirectory().getPath(); Log.i("LOGTAG", "Androidyue onReceive intent= " + mediaScanIntent + ";path=" + path + ";externalStoragePath=" + externalStoragePath);
我們看一下輸出日誌,分析原因。
LOGTAG Androidyue onReceive intent= Intent { act=android.intent.action.MEDIA_SCANNER_SCAN_FILE dat=file:///sdcard/1390136305831_add.png };path=/sdcard/1390136305831_add.png;externalStoragePath=/mnt/sdcard
上述輸出分析,你傳送的廣播,action是正確的,資料規則也是正確的,而且你的檔案路徑也是存在的,但是,檔案的路徑/sdcard/1390136305831_add.png並不是以外部儲存根路徑/mnt/sdcard/開頭。所以掃描操作沒有開始,導致檔案沒有加入到媒體庫。所以,請檢查檔案的路徑。
如何從多媒體庫中移除
如果我們刪除一個多媒體檔案的話,也就意味我們還需要將這個檔案從媒體庫中刪除掉。
能不能簡簡單單發廣播?
僅僅發一個廣播能解決問題麼?我倒是希望可以,但是實際上是不工作的,檢視如下程式碼即可明白。
// this function is used to scan a single file public Uri scanSingleFile(String path, String volumeName, String mimeType) { try { initialize(volumeName); prescan(path, true); 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; } }
正如上述程式碼,會對檔案是否存在進行檢查,如果檔案不存在,直接停止向下執行。所以這樣是不行的。那怎麼辦呢?
public void testDeleteFile() { String existingFilePath = "/mnt/sdcard/1390116362913_add.png"; File existingFile = new File(existingFilePath); existingFile.delete(); ContentResolver resolver = getActivity().getContentResolver(); resolver.delete(Images.Media.EXTERNAL_CONTENT_URI, Images.Media.DATA + "=?", new String[]{existingFilePath}); }
上述程式碼是可以工作的,直接從MediaProvider刪除即可。 具體的刪除程式碼請參考Code Snippet for Media on Android
相關文章
- 電腦掃描檔案怎麼掃描 win10電腦掃描檔案方法介紹Win10
- go掃描指定路徑下,檔案過多Go
- PDF檔案掃描文字識別軟體
- Android掃描檔案並統計各類檔案數目Android
- nmap掃描檔案提取工具
- 手機怎麼掃描檔案?
- win10系統怎麼掃描檔案 win10掃描檔案到電腦Win10
- 全面剖析Windows磁碟掃描功能(轉)Windows
- win10 如何用檔案掃描修復系統_w10系統如何掃描檔案Win10
- 怎麼用印表機掃描檔案到電腦 印表機怎麼掃描檔案成電子版
- 印表機掃描檔案到電腦怎麼操作 印表機怎麼掃描檔案成電子版
- 通過 Git 來管理多媒體檔案Git
- 透過 Git 來管理多媒體檔案Git
- Files.newDirectoryStream掃描/過濾目錄檔案
- win10印表機怎麼掃描 win10印表機掃描檔案到電腦Win10
- 解決MPLAYER播放不了多媒體檔案的方法。
- android藍芽BLE(一) —— 掃描Android藍芽
- Android漏洞掃描工具Code ArbiterAndroid
- 掃描技術和掃描工具
- C++記憶體掃描C++記憶體
- yii框架,掃描目錄下檔案入資料庫框架資料庫
- Win10系統掃描磁碟時將檔案掃沒了如何解決Win10
- Android實現掃描二維碼Android
- Android二維碼生成與掃描Android
- ExactScan文件掃描工具 ExactScan pro 萬 能掃描器整合軟體下載
- 用Python實現批次掃描域名是否存在指定檔案Python
- 漏洞掃描軟體Nessus使用教程
- AWVS掃描器掃描web漏洞操作Web
- Java 在Word中嵌入多媒體(視訊、音訊)檔案Java音訊
- 利用ATL實現QuickTime多媒體檔案播放 (轉)UI
- “快檔通”掃描識別系統
- C# 多工網段掃描練習C#
- win10系統掃描器提示掃描不到掃描器如何解決Win10
- Android自定義View 雷達掃描效果AndroidView
- Android入門(十六):手機多媒體Android
- Android車載多媒體與MediaSession框架AndroidSession框架
- 掃描王 for Mac專業圖片掃描工具Mac
- Nmap掃描教程之基礎掃描詳解