Adding Files to Android’s Media Library Using the MediaScanner

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!

Share this article:

You can leave a response, or trackback from your own site.

8 Responses to “Adding Files to Android’s Media Library Using the MediaScanner”

  1. dentex says:

    Hi.
    I was looking for info on the “java.lang.IllegalStateException: not connected to MediaScannerService” exception that I see in some crash reports from my app. They are not many, but I don’t know what’s wrong in my code, because on my phone/emulator it works good.
    I’m using a simple method, not a class, to call the MediaScanner:

            public static void scanMedia(Context context, final File[] file, final String[] mime) {
    		msc = new MediaScannerConnection(context, new MediaScannerConnectionClient() {
    			public void onScanCompleted(String path, Uri uri) {
    				Utils.logger("d", "Scanned " + path + ":", DEBUG_TAG);
    				Utils.logger("d", "-> uri: " + uri, DEBUG_TAG);
    				msc.disconnect();  
    			}
    			public void onMediaScannerConnected() {
    				for (int i = 0; i < file.length; i++) {
    					msc.scanFile(file[i].getAbsolutePath(), mime[i]);
    				}
    			}
    		});
    		msc.connect();
    	}
    

    Trying to implement your solution, in order to (possibly) avoid the exception, I had some doubts for if/when calling onMediaScannerConnected() and onScanCompleted(String path, Uri uri).

    Thanks for any suggestion.

    Samuele.

  2. dentex says:

    I forgot: my calls to the method will look like:

    Utils.scanMedia(getApplicationContext(), new File[] {myVideo}, new String[] {“video/*”});

    or

    Utils.scanMedia(getApplicationContext(), new File[] {myOtherVideo, myAudio}, new String[] {“video/*”, “audio/*”});

  3. Manju says:

    Hi,
    I have a requirement to monitor couple of desired folders (including its sub folder) for any new media file added.

    1) Is it possible to scan desired folders for media files with a single call to scanMedia() method shown in above example ?

    2) does onScanCompleted() gets called when ever there is a new media file added for desired folder ?

    3) Should i have to use FileObserver over MediaScanner for my requirement ?
    If yes, how do i get events back when desired folder is moved to some other location ?

    -Thanks & regards,
    Manju

  4. Ahmed says:

    Fails to scan an m3u file , whether via a broadcast or scanFile static method

    Not sure what’s wrong ..
    is an m3u file even a valid file for media scanner ?

    When android reboots, it does recognise it. But not programatically..
    m3u file contains paths for music files on the phone and paths are correct.

    any ideas ?

    • No, not right away. It looks like the BroadcastReceiver isn’t working any more – I still have to investigate this, though. But since I heard this from someone else as well its likely true :-(

      I think the MediaScanner takes care of m3u files – but do not know for certain.

      • Kevin says:

        I am also having an issue with added m3u files that are being properly written (recognized on device reboot) not being recognized after a:

        Uri contentUri = Uri.fromFile(playlistFile);
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri);
        sendBroadcast(mediaScanIntent);

        Please, please help.

        Thanks

Leave a Reply

You can also subscribe without commenting.

Subscribe to RSS Feed My G+-Profile Follow me on Twitter!