Android scanning multimedia file operation details

  • 2020-06-12 10:35:35
  • OfStack

This article from the system source code analysis, describes how the program to create multimedia files into the system's media library, how to delete from the media library, and most developers often encounter the problem of not being able to add to the media library. I will through the source code analysis, 11 to explain these problems.

Multimedia file scanning mechanism in Android

Android provides a great program for adding multimedia files to media libraries. This program is MediaProvider, but let's take a quick look at the following program. So let's look at 1 for 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 only accepts intent that meets the correct action and data rules.

How does MediaScannerReciever handle Intent

1 if and only if received action android. intent. action. BOOT_COMPLETED only scanning internal storage (inner and outer sdcard)
2. Except for android action. intent. action. BOOT_COMPLETED intent outside must be data transfer.
3. When Intent.ACTION_ES41en_ES42en intent is received, scan Sdcard
4. When Intent.es47EN_ES48en_ES49en_ES50en_ES52en is received, no problem detected and a single file will be scanned.

How does MediaScannerService work

MediaScannerReceiver doesn't actually do the scanning; it launches a service called MediaScannerService. Let's move on to service in manifest.


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

The scanFile method in MediaScannerService


private Uri scanFile(String path, String mimeType) {
    String volumeName = MediaProvider.EXTERNAL_VOLUME;
    openDatabase(volumeName);
    MediaScanner scanner = createMediaScanner();
    return scanner.scanSingleFile(path, volumeName, mimeType);
}

The scan method in MediaScannerService


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

The createMediaScanner method in MediaScannerService


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

Can be found from the above, the real work is actually android. media. MediaScanner. java specific please click the link to see the scanning process.

How do I scan a newly created file

There are two ways to add newly created files to the media library.

The easiest way

Just send a correct intent broadcast to 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);

The above minimalism works most of the time, but in some cases it doesn't, as will be covered in a later section. Even if you are successful with the above method, I suggest you read on for a later section on why broadcasting didn't work.

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

The scanFile method of MediaScannerConnection was introduced from 2.2 (API 8).

Create 1 MediaScannerConnection object and call the scanFile method

Is very simple, reference http: / / developer android. com/reference/android/media/MediaScannerConnection html

How do I scan multiple files

1. Send multiple ES138en.es139EN_ES140en_ES141en_ES142en_ES143en broadcasts
2. Using MediaScannerConnection, pass in an array of paths to be added.

Why does sending MEDIA_SCANNER_SCAN_FILE broadcast not work

As for why some devices don't work, many people think it's API, but it's not, it's actually related to the file path that you pass in. Look at the onReceive code for receiver Receiver.


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

Everything is correct except the path passed in. Because you may have hardcoded the file path. Since there is one such judgment, path.startsWith(externalStoragePath + "/"), Let me give a simple little example.


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

Let's take a look at the output log and analyze the reason.


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

The above output analysis shows that the broadcast you sent, action is correct, the data rules are correct, and your file path exists, but the file path /sdcard/ 1390136305831_ES177en.png does not start with the external storage root path /mnt/sdcard/. So the scan operation did not start, resulting in the file not being added to the media library. So, check the path to the file.

How do I remove a multimedia library

If we delete a multimedia file, we also need to delete it from the media library.

Can you simply broadcast it?

Will just one broadcast solve the problem? I wish I could, but it doesn't actually work, just look at the following code.


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

As in the code above, the file is checked for existence, and if it does not exist, it simply stops executing downward. So that's not going to work. So what do you do?


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

The above code works and can be removed directly from MediaProvider. Please refer to Code Snippet for Media on Android for the specific deletion code

One More Thing

You can use the view/data/data/com android. providers. media/databases/external db (different system is slightly different) files can understand more information.


Related articles: