html5中文学习网

您的位置: 首页 > android » 正文

Android中扫描多媒体文件操作详解_Android

[ ] 已经帮助:人解决问题

这篇文章从系统源代码分析,讲述如何将程序创建的多媒体文件加入系统的媒体库,如何从媒体库删除,以及大多数程序开发者经常遇到的无法添加到媒体库的问题等。本人将通过对源代码的分析,一一解释这些问题。DGxHTML5中文学习网 - HTML5先行者学习网

Android中的多媒体文件扫描机制DGxHTML5中文学习网 - HTML5先行者学习网

Android提供了一个很棒的程序来处理将多媒体文件加入的媒体库中。这个程序就是MediaProvider,现在我们简单看以下这个程序。首先看一下它的ReceiverDGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
    <receiver android:name="MediaScannerReceiver">DGxHTML5中文学习网 - HTML5先行者学习网
        <intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
            <action android:name="android.intent.action.BOOT_COMPLETED" />DGxHTML5中文学习网 - HTML5先行者学习网
        </intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
        <intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
            <action android:name="android.intent.action.MEDIA_MOUNTED" />DGxHTML5中文学习网 - HTML5先行者学习网
            <data android:scheme="file" />DGxHTML5中文学习网 - HTML5先行者学习网
        </intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
        <intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
            <action android:name="android.intent.action.MEDIA_UNMOUNTED" />DGxHTML5中文学习网 - HTML5先行者学习网
            <data android:scheme="file" />DGxHTML5中文学习网 - HTML5先行者学习网
        </intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
        <intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
            <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />DGxHTML5中文学习网 - HTML5先行者学习网
            <data android:scheme="file" />DGxHTML5中文学习网 - HTML5先行者学习网
        </intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
    </receiver>DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerReceiver只接收符合action和数据规则正确的intent。DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerReciever如何处理IntentDGxHTML5中文学习网 - HTML5先行者学习网

1.当且仅当接收到action android.intent.action.BOOT_COMPLETED才扫描内部存储(非内置和外置sdcard)DGxHTML5中文学习网 - HTML5先行者学习网
2.除了action为android.intent.action.BOOT_COMPLETED 的以外的intent都必须要有数据传递。DGxHTML5中文学习网 - HTML5先行者学习网
3.当收到 Intent.ACTION_MEDIA_MOUNTED intent,扫描SdcardDGxHTML5中文学习网 - HTML5先行者学习网
4.当收到 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent,检测没有问题,将扫描单个文件。DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerService如何工作DGxHTML5中文学习网 - HTML5先行者学习网

实际上MediaScannerReceiver并不是真正处理扫描工作,它会启动一个叫做MediaScannerService的服务。我们继续看MediaProvider的manifest中关于service的部分。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
 <service android:name="MediaScannerService" android:exported="true">DGxHTML5中文学习网 - HTML5先行者学习网
        <intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
            <action android:name="android.media.IMediaScannerService" />DGxHTML5中文学习网 - HTML5先行者学习网
        </intent-filter>DGxHTML5中文学习网 - HTML5先行者学习网
    </service>DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerService中的scanFile方法DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
private Uri scanFile(String path, String mimeType) {DGxHTML5中文学习网 - HTML5先行者学习网
    String volumeName = MediaProvider.EXTERNAL_VOLUME;DGxHTML5中文学习网 - HTML5先行者学习网
    openDatabase(volumeName);DGxHTML5中文学习网 - HTML5先行者学习网
    MediaScanner scanner = createMediaScanner();DGxHTML5中文学习网 - HTML5先行者学习网
    return scanner.scanSingleFile(path, volumeName, mimeType);DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerService中的scan方法DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
private void scan(String[] directories, String volumeName) {DGxHTML5中文学习网 - HTML5先行者学习网
    // don't sleep while scanningDGxHTML5中文学习网 - HTML5先行者学习网
    mWakeLock.acquire();DGxHTML5中文学习网 - HTML5先行者学习网

    ContentValues values = new ContentValues();DGxHTML5中文学习网 - HTML5先行者学习网
    values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);DGxHTML5中文学习网 - HTML5先行者学习网
    Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);DGxHTML5中文学习网 - HTML5先行者学习网

    Uri uri = Uri.parse("file://" + directories[0]);DGxHTML5中文学习网 - HTML5先行者学习网
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));DGxHTML5中文学习网 - HTML5先行者学习网

    try {DGxHTML5中文学习网 - HTML5先行者学习网
        if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {DGxHTML5中文学习网 - HTML5先行者学习网
            openDatabase(volumeName);DGxHTML5中文学习网 - HTML5先行者学习网
        }DGxHTML5中文学习网 - HTML5先行者学习网

        MediaScanner scanner = createMediaScanner();DGxHTML5中文学习网 - HTML5先行者学习网
        scanner.scanDirectories(directories, volumeName);DGxHTML5中文学习网 - HTML5先行者学习网
    } catch (Exception e) {DGxHTML5中文学习网 - HTML5先行者学习网
        Log.e(TAG, "exception in MediaScanner.scan()", e);DGxHTML5中文学习网 - HTML5先行者学习网
    }DGxHTML5中文学习网 - HTML5先行者学习网

    getContentResolver().delete(scanUri, null, null);DGxHTML5中文学习网 - HTML5先行者学习网

    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));DGxHTML5中文学习网 - HTML5先行者学习网
    mWakeLock.release();DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网

DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerService中的createMediaScanner方法DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
private MediaScanner createMediaScanner() {DGxHTML5中文学习网 - HTML5先行者学习网
        MediaScanner scanner = new MediaScanner(this);DGxHTML5中文学习网 - HTML5先行者学习网
        Locale locale = getResources().getConfiguration().locale;DGxHTML5中文学习网 - HTML5先行者学习网
        if (locale != null) {DGxHTML5中文学习网 - HTML5先行者学习网
            String language = locale.getLanguage();DGxHTML5中文学习网 - HTML5先行者学习网
            String country = locale.getCountry();DGxHTML5中文学习网 - HTML5先行者学习网
            String localeString = null;DGxHTML5中文学习网 - HTML5先行者学习网
            if (language != null) {DGxHTML5中文学习网 - HTML5先行者学习网
                if (country != null) {DGxHTML5中文学习网 - HTML5先行者学习网
                    scanner.setLocale(language + "_" + country);DGxHTML5中文学习网 - HTML5先行者学习网
                } else {DGxHTML5中文学习网 - HTML5先行者学习网
                    scanner.setLocale(language);DGxHTML5中文学习网 - HTML5先行者学习网
                }DGxHTML5中文学习网 - HTML5先行者学习网
            }DGxHTML5中文学习网 - HTML5先行者学习网
        }DGxHTML5中文学习网 - HTML5先行者学习网

        return scanner;DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网

DGxHTML5中文学习网 - HTML5先行者学习网

从上面可以发现,真正工作的其实是android.media.MediaScanner.java 具体扫描过程就请点击左侧链接查看。DGxHTML5中文学习网 - HTML5先行者学习网

如何扫描一个刚创建的文件DGxHTML5中文学习网 - HTML5先行者学习网

这里介绍两种方式来实现将新创建的文件加入媒体库。DGxHTML5中文学习网 - HTML5先行者学习网

最简单的方式DGxHTML5中文学习网 - HTML5先行者学习网

只需要发送一个正确的intent广播到MediaScannerReceiver即可。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
String saveAs = "Your_Created_File_Path"DGxHTML5中文学习网 - HTML5先行者学习网
Uri contentUri = Uri.fromFile(new File(saveAs));DGxHTML5中文学习网 - HTML5先行者学习网
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri);DGxHTML5中文学习网 - HTML5先行者学习网
getContext().sendBroadcast(mediaScanIntent);DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

上面的极简方法大多数情况下正常工作,但是有些情况下是不会工作的,稍后的部分会介绍。即使你使用上述方法成功了,还是建议你继续阅读稍后的为什么发广播不成功的部分。DGxHTML5中文学习网 - HTML5先行者学习网

使用MediaScannerConnectionDGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
public void mediaScan(File file) {DGxHTML5中文学习网 - HTML5先行者学习网
    MediaScannerConnection.scanFile(getActivity(),DGxHTML5中文学习网 - HTML5先行者学习网
            new String[] { file.getAbsolutePath() }, null,DGxHTML5中文学习网 - HTML5先行者学习网
            new OnScanCompletedListener() {DGxHTML5中文学习网 - HTML5先行者学习网
                @OverrideDGxHTML5中文学习网 - HTML5先行者学习网
                public void onScanCompleted(String path, Uri uri) {DGxHTML5中文学习网 - HTML5先行者学习网
                    Log.v("MediaScanWork", "file " + pathDGxHTML5中文学习网 - HTML5先行者学习网
                            + " was scanned seccessfully: " + uri);DGxHTML5中文学习网 - HTML5先行者学习网
                }DGxHTML5中文学习网 - HTML5先行者学习网
            });DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

MediaScannerConnection的scanFile方法从2.2(API 8)开始引入。DGxHTML5中文学习网 - HTML5先行者学习网

创建一个MediaScannerConnection对象然后调用scanFile方法DGxHTML5中文学习网 - HTML5先行者学习网

很简单,参考http://developer.android.com/reference/android/media/MediaScannerConnection.htmlDGxHTML5中文学习网 - HTML5先行者学习网

如何扫描多个文件DGxHTML5中文学习网 - HTML5先行者学习网

1.发送多个Intent.ACTION_MEDIA_SCANNER_SCAN_FILE广播DGxHTML5中文学习网 - HTML5先行者学习网
2.使用MediaScannerConnection,传入要加入的路径的数组。DGxHTML5中文学习网 - HTML5先行者学习网

为什么发送MEDIA_SCANNER_SCAN_FILE广播不生效DGxHTML5中文学习网 - HTML5先行者学习网

关于为什么有些设备上不生效,很多人认为是API原因,其实不是的,这其实和你传入的文件路径有关系。看一下接收者Receiver的onReceive代码。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
public void onReceive(Context context, Intent intent) {DGxHTML5中文学习网 - HTML5先行者学习网
    String action = intent.getAction();DGxHTML5中文学习网 - HTML5先行者学习网
    Uri uri = intent.getData();DGxHTML5中文学习网 - HTML5先行者学习网
    if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {DGxHTML5中文学习网 - HTML5先行者学习网
        // scan internal storageDGxHTML5中文学习网 - HTML5先行者学习网
        scan(context, MediaProvider.INTERNAL_VOLUME);DGxHTML5中文学习网 - HTML5先行者学习网
    } else {DGxHTML5中文学习网 - HTML5先行者学习网
        if (uri.getScheme().equals("file")) {DGxHTML5中文学习网 - HTML5先行者学习网
            // handle intents related to external storageDGxHTML5中文学习网 - HTML5先行者学习网
            String path = uri.getPath();DGxHTML5中文学习网 - HTML5先行者学习网
            String externalStoragePath = Environment.getExternalStorageDirectory().getPath();DGxHTML5中文学习网 - HTML5先行者学习网

            Log.d(TAG, "action: " + action + " path: " + path);DGxHTML5中文学习网 - HTML5先行者学习网
            if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {DGxHTML5中文学习网 - HTML5先行者学习网
                // scan whenever any volume is mountedDGxHTML5中文学习网 - HTML5先行者学习网
                scan(context, MediaProvider.EXTERNAL_VOLUME);DGxHTML5中文学习网 - HTML5先行者学习网
            } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&DGxHTML5中文学习网 - HTML5先行者学习网
                    path != null && path.startsWith(externalStoragePath + "/")) {DGxHTML5中文学习网 - HTML5先行者学习网
                scanFile(context, path);DGxHTML5中文学习网 - HTML5先行者学习网
            }DGxHTML5中文学习网 - HTML5先行者学习网
        }DGxHTML5中文学习网 - HTML5先行者学习网
    }DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网

DGxHTML5中文学习网 - HTML5先行者学习网

所有的部分都正确除了传入的路径。因为你可能硬编码了文件路径。因为有一个这样的判断path.startsWith(externalStoragePath + "/"),这里我举一个简单的小例子。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
final String saveAs = "/sdcard/" + System.currentTimeMillis() + "_add.png";DGxHTML5中文学习网 - HTML5先行者学习网
Uri contentUri = Uri.fromFile(new File(saveAs));DGxHTML5中文学习网 - HTML5先行者学习网
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri);DGxHTML5中文学习网 - HTML5先行者学习网
getContext().sendBroadcast(mediaScanIntent);DGxHTML5中文学习网 - HTML5先行者学习网
Uri uri = mediaScanIntent.getData();DGxHTML5中文学习网 - HTML5先行者学习网
String path = uri.getPath();DGxHTML5中文学习网 - HTML5先行者学习网
String externalStoragePath = Environment.getExternalStorageDirectory().getPath();DGxHTML5中文学习网 - HTML5先行者学习网
Log.i("LOGTAG", "Androidyue onReceive intent= " + mediaScanIntentDGxHTML5中文学习网 - HTML5先行者学习网
                        + ";path=" + path + ";externalStoragePath=" +DGxHTML5中文学习网 - HTML5先行者学习网
                        externalStoragePath);DGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

我们看一下输出日志,分析原因。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
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/sdcardDGxHTML5中文学习网 - HTML5先行者学习网
DGxHTML5中文学习网 - HTML5先行者学习网

上述输出分析,你发送的广播,action是正确的,数据规则也是正确的,而且你的文件路径也是存在的,但是,文件的路径/sdcard/1390136305831_add.png并不是以外部存储根路径/mnt/sdcard/开头。所以扫描操作没有开始,导致文件没有加入到媒体库。所以,请检查文件的路径。DGxHTML5中文学习网 - HTML5先行者学习网

如何从多媒体库中移除DGxHTML5中文学习网 - HTML5先行者学习网

如果我们删除一个多媒体文件的话,也就意味我们还需要将这个文件从媒体库中删除掉。DGxHTML5中文学习网 - HTML5先行者学习网

能不能简简单单发广播?DGxHTML5中文学习网 - HTML5先行者学习网

仅仅发一个广播能解决问题么?我倒是希望可以,但是实际上是不工作的,查看如下代码即可明白。DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
// this function is used to scan a single fileDGxHTML5中文学习网 - HTML5先行者学习网
public Uri scanSingleFile(String path, String volumeName, String mimeType) {DGxHTML5中文学习网 - HTML5先行者学习网
    try {DGxHTML5中文学习网 - HTML5先行者学习网
        initialize(volumeName);DGxHTML5中文学习网 - HTML5先行者学习网
        prescan(path, true);DGxHTML5中文学习网 - HTML5先行者学习网

        File file = new File(path);DGxHTML5中文学习网 - HTML5先行者学习网
        if (!file.exists()) {DGxHTML5中文学习网 - HTML5先行者学习网
            return null;DGxHTML5中文学习网 - HTML5先行者学习网
        }DGxHTML5中文学习网 - HTML5先行者学习网

        // lastModified is in milliseconds on Files.DGxHTML5中文学习网 - HTML5先行者学习网
        long lastModifiedSeconds = file.lastModified() / 1000;DGxHTML5中文学习网 - HTML5先行者学习网

        // always scan the file, so we can return the content://media Uri for existing filesDGxHTML5中文学习网 - HTML5先行者学习网
        return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),DGxHTML5中文学习网 - HTML5先行者学习网
                false, true, MediaScanner.isNoMediaPath(path));DGxHTML5中文学习网 - HTML5先行者学习网
    } catch (RemoteException e) {DGxHTML5中文学习网 - HTML5先行者学习网
        Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);DGxHTML5中文学习网 - HTML5先行者学习网
        return null;DGxHTML5中文学习网 - HTML5先行者学习网
    }DGxHTML5中文学习网 - HTML5先行者学习网
}DGxHTML5中文学习网 - HTML5先行者学习网

DGxHTML5中文学习网 - HTML5先行者学习网

正如上述代码,会对文件是否存在进行检查,如果文件不存在,直接停止向下执行。所以这样是不行的。那怎么办呢?DGxHTML5中文学习网 - HTML5先行者学习网

复制代码 代码如下:
DGxHTML5中文学习网 - HTML5先行者学习网
public void testDeleteFile() {DGxHTML5中文学习网 - HTML5先行者学习网
    String existingFilePath = "/mnt/sdcard/1390116362913_add.png";DGxHTML5中文学习网 - HTML5先行者学习网
    File  existingFile = new File(existingFilePath);DGxHTML5中文学习网 - HTML5先行者学习网
    existingFile.delete();DGxHTML5中文学习网 - HTML5先行者学习网
    ContentResolver resolver = getActivity().getContentResolver();DGxHTML5中文学习网 - HTML5先行者学习网
    resolver.delete(Images.Media.EXTERNAL_CONTENT_URI, Images.Media.DATA + "=?", new String[]{existingFilePath});DGxHTML5中文学习网 - HTML5先行者学习网

}DGxHTML5中文学习网 - HTML5先行者学习网

DGxHTML5中文学习网 - HTML5先行者学习网

上述代码是可以工作的,直接从MediaProvider删除即可。 具体的删除代码请参考Code Snippet for Media on AndroidDGxHTML5中文学习网 - HTML5先行者学习网

One More ThingDGxHTML5中文学习网 - HTML5先行者学习网

你可以通过查看/data/data/com.android.providers.media/databases/external.db(不同系统略有不同)文件可以了解更多的信息。DGxHTML5中文学习网 - HTML5先行者学习网

(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助