When you add files to Android's filesystem these files are not picked up by the MedaScanner automatically. But often they should be.
As far as I can tell from a cursory search through Android's codebase, Android runs a full media scan only on reboot and when (re)mounting the sd card. This might sound bad at first - but think about it. A full scan is taking quite some time and you do not want this to happen arbitrarily, maybe even while the user is using his device heavily.
This means, any file, that must be available in the media library instantaneously, has to be added by you. You have the responsibility to get this right. Even more so, as new devices might support MTP, which means that all files - not only media files - have to made known. Read more about this in the section "Not only relevant for media files".
If you want your files to be added to the media library, you can do so either by using the MediaStore content provider, or by using the MediaScanner. In this post I deal with the MediaScanner only. In a later post I will cover how to use the MediaStore provider for making your files known.
Adding the files using a broadcast
The simplest way to do so is by sending a broadcast:
Intent intent =
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
sendBroadcast(intent);
This works perfectly well and you should be fine with this solution most of the time.
But if you want to be more in control of what is happening, you could use one of the following two methods.
Using the static scanFile() method
If you simply need to know when the files have been added, you could use MediaScannerConnection's static method scanFile()
together with a MediaScannerConnection.OnScanCompletedListener.
The static method scanFile()
is badly named, as it actually takes an array of paths and thus can be used to add multiple files at once and not just one - but it nevertheless does what we want 🙂
Here's how to use this method:
MediaScannerConnection.scanFile(
getApplicationContext(),
new String[]{file.getAbsolutePath()},
null,
new OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
Log.v("grokkingandroid",
"file " + path + " was scanned seccessfully: " + uri);
}
});
Parameter | Use |
---|---|
context | The application context |
paths | A String array containing the paths of the files you want to add |
mimeTypes | A String array containing the mime types of the files |
callback | A MediaScannerConnection.OnScanCompletedListener to be notified when the scan is completed |
The OnScanCompletedListener
itself must implement the onScanCompleted()
method. This method gets the filename and the URI for the MediaStore.Files provider passed in as parameters.
Creating an instance of MediaScannerConnection
This is the most complex way to make your files known. But it gives you even more control. You need to implement the MediaScannerConnection.MediaScannerConnectionClient for callbacks.
Your MediaScannerConnectionClient's
implementation is not only called whenever a scan has been completed, but also as soon as the connection has been established. Since this might take a little while, as described in the next section, you might be interested in this callback. The way the API is constructed, you actually need to use this callback method to start the scan.
The following snippet shows you a sample implementation of the MediaScannerConnectionClient
interface.
final class MyMediaScannerConnectionClient
implements MediaScannerConnectionClient {
private String mFilename;
private String mMimetype;
private MediaScannerConnection mConn;
public MyMediaScannerConnectionClient
(Context ctx, File file, String mimetype) {
this.mFilename = file.getAbsolutePath();
mConn = new MediaScannerConnection(ctx, this);
mConn.connect();
}
@Override
public void onMediaScannerConnected() {
mConn.scanFile(mFilename, mMimetype);
}
@Override
public void onScanCompleted(String path, Uri uri) {
mConn.disconnect();
}
}
In this implementation I create the MediaScannerConnection
within the constructor and here I also call its connect()
method.
Note also that I start the scan within the onMediaScannerConnected()
callback method.
This way the usage of this interface is as simple as it can get:
MediaScannerConnectionClient client =
new MyMediaScannerConnectionClient(
getApplicationContext(), file, null);
Establishing a connection might take a while
Be aware that the connection is not established right away. That's why the following snippet would cause trouble (that's the first time I noticed problematic code in Mark Murphy's otherwise highly recommended book):
/////// Do not do this ! ///////
MediaScannerConnection c =
new MediaScannerConnection(
getApplicationContext(), null);
c.connect();
c.scanFile(file.getAbsolutePath(), null);
c.disconnect();
/////// Do not do this ! ///////
This snippet would fail with an IllegalStateException
:
java.lang.IllegalStateException: not connected to MediaScannerService
When doing some tests it took on my Optimus One (with an sd card) and on my Galaxy Nexus (with an extra partition) about 20 to 40 milliseconds to establish the connection - on my Archos tablet (also extra partition) about 100 milliseconds.
The scan itself takes even longer - which took me by surprise. On my Archos and Optimus One about 200 milliseconds - on my Nexus even up to 900 (!) milliseconds on its worst run.
Not only relevant for media files
With devices that use MTP instead of the traditional USB storage protocol, the MediaScanner is also used to make arbitrary files accessible via MTP. Thus any files and folders that you do not add to the MediaScanner are invisible between full scans if you plug in your device to your computer!
With the introduction of Honeycomb Google has started to push for MTP. Even if not all handset makers follow Google's decision, some might - and Google's own Nexus line definitely does so.
This means that you should use the MediaScanner also for any files that the user might want to download to his computer. It could be anything, e.g. CSV backup files, PDF files and so on. If the user might want to use them on a traditional computer, you have to make these files known using the methods described above.
Happy coding!