一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Android - Android中掃描多媒體文件操作詳解

Android中掃描多媒體文件操作詳解

2021-03-12 15:46Android開發(fā)網(wǎng) Android

這篇文章主要介紹了Android中掃描多媒體文件操作詳解,本文講解了Android中的多媒體文件掃描機(jī)制、如何掃描一個(gè)剛創(chuàng)建的文件、如何掃描多個(gè)文件,需要的朋友可以參考下

這篇文章從系統(tǒng)源代碼分析,講述如何將程序創(chuàng)建的多媒體文件加入系統(tǒng)的媒體庫(kù),如何從媒體庫(kù)刪除,以及大多數(shù)程序開發(fā)者經(jīng)常遇到的無(wú)法添加到媒體庫(kù)的問題等。本人將通過對(duì)源代碼的分析,一一解釋這些問題。

Android中的多媒體文件掃描機(jī)制

Android提供了一個(gè)很棒的程序來(lái)處理將多媒體文件加入的媒體庫(kù)中。這個(gè)程序就是MediaProvider,現(xiàn)在我們簡(jiǎn)單看以下這個(gè)程序。首先看一下它的Receiver

 

復(fù)制代碼 代碼如下:

    <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和數(shù)據(jù)規(guī)則正確的intent。

MediaScannerReciever如何處理Intent

1.當(dāng)且僅當(dāng)接收到action android.intent.action.BOOT_COMPLETED才掃描內(nèi)部存儲(chǔ)(非內(nèi)置和外置sdcard)
2.除了action為android.intent.action.BOOT_COMPLETED 的以外的intent都必須要有數(shù)據(jù)傳遞。
3.當(dāng)收到 Intent.ACTION_MEDIA_MOUNTED intent,掃描Sdcard
4.當(dāng)收到 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent,檢測(cè)沒有問題,將掃描單個(gè)文件。

MediaScannerService如何工作

實(shí)際上MediaScannerReceiver并不是真正處理掃描工作,它會(huì)啟動(dòng)一個(gè)叫做MediaScannerService的服務(wù)。我們繼續(xù)看MediaProvider的manifest中關(guān)于service的部分。

 

復(fù)制代碼 代碼如下:

 <service android:name="MediaScannerService" android:exported="true">
        <intent-filter>
            <action android:name="android.media.IMediaScannerService" />
        </intent-filter>
    </service>

 

MediaScannerService中的scanFile方法

 

復(fù)制代碼 代碼如下:

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

 

復(fù)制代碼 代碼如下:


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

 

復(fù)制代碼 代碼如下:


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

 

從上面可以發(fā)現(xiàn),真正工作的其實(shí)是android.media.MediaScanner.java 具體掃描過程就請(qǐng)點(diǎn)擊左側(cè)鏈接查看。

如何掃描一個(gè)剛創(chuàng)建的文件

這里介紹兩種方式來(lái)實(shí)現(xiàn)將新創(chuàng)建的文件加入媒體庫(kù)。

最簡(jiǎn)單的方式

只需要發(fā)送一個(gè)正確的intent廣播到MediaScannerReceiver即可。

 

復(fù)制代碼 代碼如下:

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

 

上面的極簡(jiǎn)方法大多數(shù)情況下正常工作,但是有些情況下是不會(huì)工作的,稍后的部分會(huì)介紹。即使你使用上述方法成功了,還是建議你繼續(xù)閱讀稍后的為什么發(fā)廣播不成功的部分。

使用MediaScannerConnection

 

復(fù)制代碼 代碼如下:

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)開始引入。

創(chuàng)建一個(gè)MediaScannerConnection對(duì)象然后調(diào)用scanFile方法

很簡(jiǎn)單,參考http://developer.android.com/reference/android/media/MediaScannerConnection.html

如何掃描多個(gè)文件

1.發(fā)送多個(gè)Intent.ACTION_MEDIA_SCANNER_SCAN_FILE廣播
2.使用MediaScannerConnection,傳入要加入的路徑的數(shù)組。

為什么發(fā)送MEDIA_SCANNER_SCAN_FILE廣播不生效

關(guān)于為什么有些設(shè)備上不生效,很多人認(rèn)為是API原因,其實(shí)不是的,這其實(shí)和你傳入的文件路徑有關(guān)系??匆幌陆邮照逺eceiver的onReceive代碼。

 

復(fù)制代碼 代碼如下:


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

 

所有的部分都正確除了傳入的路徑。因?yàn)槟憧赡苡簿幋a了文件路徑。因?yàn)橛幸粋€(gè)這樣的判斷path.startsWith(externalStoragePath + "/"),這里我舉一個(gè)簡(jiǎn)單的小例子。

 

復(fù)制代碼 代碼如下:

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

 

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

 

復(fù)制代碼 代碼如下:

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

 

上述輸出分析,你發(fā)送的廣播,action是正確的,數(shù)據(jù)規(guī)則也是正確的,而且你的文件路徑也是存在的,但是,文件的路徑/sdcard/1390136305831_add.png并不是以外部存儲(chǔ)根路徑/mnt/sdcard/開頭。所以掃描操作沒有開始,導(dǎo)致文件沒有加入到媒體庫(kù)。所以,請(qǐng)檢查文件的路徑。

如何從多媒體庫(kù)中移除

如果我們刪除一個(gè)多媒體文件的話,也就意味我們還需要將這個(gè)文件從媒體庫(kù)中刪除掉。

能不能簡(jiǎn)簡(jiǎn)單單發(fā)廣播?

僅僅發(fā)一個(gè)廣播能解決問題么?我倒是希望可以,但是實(shí)際上是不工作的,查看如下代碼即可明白。

 

復(fù)制代碼 代碼如下:


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

 

正如上述代碼,會(huì)對(duì)文件是否存在進(jìn)行檢查,如果文件不存在,直接停止向下執(zhí)行。所以這樣是不行的。那怎么辦呢?

 

復(fù)制代碼 代碼如下:


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刪除即可。 具體的刪除代碼請(qǐng)參考Code Snippet for Media on Android

One More Thing

你可以通過查看/data/data/com.android.providers.media/databases/external.db(不同系統(tǒng)略有不同)文件可以了解更多的信息。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 天堂网在线网站成人午夜网站 | 性的张力| 国产在线欧美精品 | 日本-区二区三区免费精品 日本破处 | 日本免费久久久久久久网站 | 国产精品拍拍拍福利在线观看 | 草莓视频幸福宝 | 黑人巨大和日本娇小中出 | 午夜AV亚洲一码二中文字幕青青 | yy6080午夜国产免费福利 | 亚洲成综合| 8x8x极品国产在线 | 肉色欧美久久久久久久蜜桃 | 成人免费观看一区二区 | 午夜a一级毛片 | 日韩黄色影视 | 91av爱爱| 特黄aa级毛片免费视频播放 | 动漫美女羞羞视频 | 色综合中文字幕天天在线 | 91素人约啪 | 国产无限 | 免费午夜剧场 | 美女做又爽又黄又猛 | 丝瓜草莓香蕉绿巨人幸福宝 | 日本大尺度动漫在线观看缘之空 | 韩国三级在线观看 完整版 韩国三级视频网站 | 洗濯屋动漫在线观看 | 日韩免费观看成第15集 | 国产一区视频在线免费观看 | 国产日本韩国不卡在线视频 | 91国语精品自产拍在线观看一 | 四色6677最新永久网站 | tubehdxx丝袜正片 | 日韩精品成人 | 金牛网155755水心论坛黄大父母 | 丰腴尤物贵妇浪荡小说 | 色天天久久 | 国产在线精品亚洲第一区香蕉 | 免费一级特黄特色大片在线 | 精品国产一级在线观看 |