Android でのマルチメディア ファイルのスキャンの詳細な説明

高洛峰
リリース: 2017-01-16 17:26:20
オリジナル
1635 人が閲覧しました

この記事では、システムのソース コードを分析し、プログラムによって作成されたマルチメディア ファイルをシステムのメディア ライブラリに追加する方法、メディア ライブラリから削除する方法、およびほとんどのプログラム開発者が問題にするメディア ライブラリに追加できない問題について説明します。よく遭遇します。これらの問題について、ソースコードの解析を通して一つ一つ解説していきます。

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 は、アクションとデータ ルールに準拠する正しいインテントのみを受信します。

MediaScannerReciever がインテントを処理する方法

1. アクション android.intent.action.BOOT_COMPLETED を受信した場合にのみ、内部ストレージ (非内蔵および外部 SD カード) をスキャンします
2. アクション android.intent を除きます。 action.BOOT_COMPLETED 他のすべてのインテントではデータを転送する必要があります。
3. Intent.ACTION_MEDIA_MOUNTED インテントを受信すると、SDcard をスキャンします。
4. Intent.ACTION_MEDIA_SCANNER_SCAN_FILE インテントを受信すると、検出に問題はなく、単一のファイルがスキャンされます。

MediaScannerService の仕組み

実際、MediaScannerReceiver は実際にはスキャン作業を処理するのではなく、MediaScannerService と呼ばれるサービスを開始します。 MediaProvider のマニフェストのサービス部分を続けて見てみましょう。

 <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&#39;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;
}
ログイン後にコピー

新しく作成したファイルをスキャンする方法

新しく作成したファイルをメディアライブラリに追加する2つの方法を次に示します。

最も簡単な方法

は、正しいインテント ブロードキャストを 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 を参照してください

複数のファイルをスキャンする方法

1複数の Intent を送信します。ACTION_MEDIA_SCANNER_SCAN_FILE ブロードキャスト
2. 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
ログイン後にコピー

上記の出力分析は、送信したブロードキャストとアクションが正しく、データ ルールも正しく、ファイル パスも存在することを示しています。ただし、ファイル パス /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 から直接削除するだけです。 特定の削除コードについては、「Android 上のメディアのコード スニペット」を参照してください

もう 1 つのこと

Android でのマルチメディア ファイルのスキャン操作の詳細な説明については、PHP 中国語 Web サイトの関連記事に注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート