I once tried to use MediaScanner to resolve problems; however it turned out to be a failure. Now I make it.This post is to write down why I failed and how I work it out now. I think it could be deeper that other posts.
##Android Media Scanning Mechanism
Android provides a great application for developers to add created media files to add them into the library. The application is called MediaProvider. Now let’s have a glance of MediaProvider.
The receiver part of its manifest
The MediaScannerReceiver will receive the above intents with right action and data scheme.
###How the MediaScannerRecieve handles the intent
It will scan internal storage only after receiving the action android.intent.action.BOOT_COMPLETED
All intent but the android.intent.action.BOOT_COMPLETED intent should carry the file scheme data
It will scan external storage when receiving the Intent.ACTION_MEDIA_MOUNTED intent.
It will scan the single file when receiving the Intent.ACTION_MEDIA_SCANNER_SCAN_FILE intent.
###How the MediaScannerService works
Actually receiver does not do scanning. It will start a service called MediaScannerService.
The service part of its manifest
privatevoidscan(String[]directories,StringvolumeName){// don't sleep while scanningmWakeLock.acquire();ContentValuesvalues=newContentValues();values.put(MediaStore.MEDIA_SCANNER_VOLUME,volumeName);UriscanUri=getContentResolver().insert(MediaStore.getMediaScannerUri(),values);Uriuri=Uri.parse("file://"+directories[0]);sendBroadcast(newIntent(Intent.ACTION_MEDIA_SCANNER_STARTED,uri));try{if(volumeName.equals(MediaProvider.EXTERNAL_VOLUME)){openDatabase(volumeName);}MediaScannerscanner=createMediaScanner();scanner.scanDirectories(directories,volumeName);}catch(Exceptione){Log.e(TAG,"exception in MediaScanner.scan()",e);}getContentResolver().delete(scanUri,null,null);sendBroadcast(newIntent(Intent.ACTION_MEDIA_SCANNER_FINISHED,uri));mWakeLock.release();}
Actually the scan code is not really in the MediaScannerService
##How To Scan A Created File
Now I am going to introduce two ways to add a created file into the media library.
###The Simplest Method
Just send a broadcast, as we have posted above. Just send a broadcast intent to MediaScannerReceiver.
The above method maybe has been seen thousands of times. Actually it should work. However I failed and failed in sending broadcast intent. In the following section. I will point out why the sending broadcast not works. Even though you get well on with sending broadcast,it’s strongly recommended to read the Section Why Sending MEDIA_SCANNER_SCAN_FILE broadcast not works.
###Use MediaScannerConnection
1234567891011
publicvoidmediaScan(Filefile){MediaScannerConnection.scanFile(getActivity(),newString[]{file.getAbsolutePath()},null,newOnScanCompletedListener(){@OverridepublicvoidonScanCompleted(Stringpath,Uriuri){Log.v("MediaScanWork","file "+path+" was scanned seccessfully: "+uri);}});}
Use the second method by filled the second params with an array of paths.
##Why Sending MEDIA_SCANNER_SCAN_FILE broadcast not works
Actually someone may think sending ACTION_MEDIA_SCANNER_SCAN_FILE works on some devices but not on other devices. Actually it’s. Is it a API limit?
No, It’s has someting to do with your file path.
Take a look at this
1234567891011121314151617181920212223
publicvoidonReceive(Contextcontext,Intentintent){Stringaction=intent.getAction();Uriuri=intent.getData();if(action.equals(Intent.ACTION_BOOT_COMPLETED)){// scan internal storagescan(context,MediaProvider.INTERNAL_VOLUME);}else{if(uri.getScheme().equals("file")){// handle intents related to external storageStringpath=uri.getPath();StringexternalStoragePath=Environment.getExternalStorageDirectory().getPath();Log.d(TAG,"action: "+action+" path: "+path);if(action.equals(Intent.ACTION_MEDIA_MOUNTED)){// scan whenever any volume is mountedscan(context,MediaProvider.EXTERNAL_VOLUME);}elseif(action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)&&path!=null&&path.startsWith(externalStoragePath+"/")){scanFile(context,path);}}}}
Every part is right except the intent data. I mean the file path. You may hardcode the filepath. This is my example
So in the sending broadcast, your action is right, your data schema OK, your data path not null;but your path /sdcard/1390136305831_add.png does not startswith the externalStoragePath /mnt/sdcard/
And so the scan file is actually not called.
In conclusion your hardcoding path results in the failure.
##Remove From Media Library
If we deleted a file ,it means that we need to remove the file from the media library.
###Simply sending a broadcast?
Can we simple sending a broadcast to the MediaScannerReceiver? I also wish it could. But actually it does not work.
Look at this code for the explanation.
12345678910111213141516171819202122
// this function is used to scan a single filepublicUriscanSingleFile(Stringpath,StringvolumeName,StringmimeType){try{initialize(volumeName);prescan(path,true);Filefile=newFile(path);if(!file.exists()){returnnull;}// lastModified is in milliseconds on Files.longlastModifiedSeconds=file.lastModified()/1000;// always scan the file, so we can return the content://media Uri for existing filesreturnmClient.doScanFile(path,mimeType,lastModifiedSeconds,file.length(),false,true,MediaScanner.isNoMediaPath(path));}catch(RemoteExceptione){Log.e(TAG,"RemoteException in MediaScanner.scanFile()",e);returnnull;}}
As the above code points out, It does have a check before the real scanning.
Then how should I do?