Grokking Android

Getting Down to the Nitty Gritty of Android Development

Adding Files to Android’s Media Library Using the MediaScanner

By

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);
         }
      });
Parameters for the static scanFile() method
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!

Wolfram Rittmeyer lives in Germany and has been developing with Java for many years.

He has been interested in Android for quite a while and has been blogging about all kind of topics around Android.

You can find him on Google+ and Twitter.