首頁 Java java教程 Android中掃描多媒體檔案操作詳解

Android中掃描多媒體檔案操作詳解

Jan 16, 2017 pm 05:26 PM

這篇文章從系統原始碼分析,講述如何將程式創建的多媒體檔案加入系統的媒體庫,如何從媒體庫刪除,以及大多數程式開發者經常遇到的無法添加到媒體庫的問題等。本人將透過對原始碼的分析,一一解釋這些問題。

Android中的多媒體檔案掃描機制

Android提供了一個很棒的程式來處理將多媒體檔案加入的媒體庫中。這個程式就是MediaProvider,現在我們簡單看以下這個程式。首先來看看它的Receiver

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<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

1.當且僅當接收到action android.intent.action.BOOT_COMPLETED才掃描內部儲存(非內建和外置sdcard)
2.除了action為android.intent.action.BOOT_COMPLE 的以外的intent都必須要有資料傳遞。
3.當收到 Intent.ACTION_MEDIA_MOUNTED intent,掃描Sdcard
4.收到 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent,偵測沒有問題,將掃描單一檔案。

MediaScannerService如何運作

其實MediaScannerReceiver並不是真正處理掃描工作,它會啟動一個叫做MediaScannerService的服務。我們繼續看MediaProvider的manifest中關於service的部分。

1

2

3

4

5

<service android:name="MediaScannerService" android:exported="true">

       <intent-filter>

           <action android:name="android.media.IMediaScannerService" />

       </intent-filter>

   </service>

登入後複製

MediaScannerService中的scanFile方法

1

2

3

4

5

6

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方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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;

}

登入後複製

MediaScannerService中的createMediaScanner方法。庫。

最簡單的方式

只需要發送一個正確的intent廣播到MediaScannerReceiver即可。

1

2

3

4

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

1

2

3

4

5

6

7

8

9

10

11

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

如何掃描多個檔案

1.發送多個檔案.ACTION_MEDIA_SCANNER_SCAN_FILE廣播

2.使用MediaScannerConnection,傳入要加入的路徑的陣列。


為什麼發送MEDIA_SCANNER_SCAN_FILE廣播不生效

關於為什麼有些設備上不生效,很多人認為是API原因,其實不是的,這其實和你傳入的文件路徑有關係。來看看接收者Receiver的onReceive程式碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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 + "/"),這裡我舉一個簡單的小例子。

1

2

3

4

5

6

7

8

9

10

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);

登入後複製

我們看一下輸出日誌,分析原因。

1

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/開頭。所以掃描操作沒有開始,導致檔案沒有加入到媒體庫。所以,請檢查文件的路徑。

如何從多媒體庫中移除

如果我們刪除一個多媒體檔案的話,也就意味我們還需要將這個檔案從媒體庫中刪除掉。

能不能簡簡單單發廣播?

僅發一個廣播能解決問題麼?我倒是希望可以,但是實際上是不工作的,請查看如下程式碼即可明白。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 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;

    }

}

登入後複製

正如上述程式碼,會對檔案是否存在進行檢查,如果檔案不存在,直接停止向下執行。所以這樣是不行的。那怎麼辦呢?

1

2

3

4

5

6

7

8

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

One More Thing

更多Android中掃描多媒體文件操作詳解相關文章請關注PHP中文網!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱門文章

兩個點博物館:邦格荒地地點指南
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱門文章

兩個點博物館:邦格荒地地點指南
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱門文章標籤

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型? Java的類負載機制如何起作用,包括不同的類載荷及其委託模型? Mar 17, 2025 pm 05:35 PM

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?

2025年的前4個JavaScript框架:React,Angular,Vue,Svelte 2025年的前4個JavaScript框架:React,Angular,Vue,Svelte Mar 07, 2025 pm 06:09 PM

2025年的前4個JavaScript框架:React,Angular,Vue,Svelte

如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案? 如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案? Mar 17, 2025 pm 05:46 PM

如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?

如何在Java中實施功能編程技術? 如何在Java中實施功能編程技術? Mar 11, 2025 pm 05:51 PM

如何在Java中實施功能編程技術?

Node.js 20:關鍵性能提升和新功能 Node.js 20:關鍵性能提升和新功能 Mar 07, 2025 pm 06:12 PM

Node.js 20:關鍵性能提升和新功能

冰山:數據湖桌的未來 冰山:數據湖桌的未來 Mar 07, 2025 pm 06:31 PM

冰山:數據湖桌的未來

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射? 如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射? Mar 17, 2025 pm 05:43 PM

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存? 如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存? Mar 17, 2025 pm 05:44 PM

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?

See all articles