MediaScannerService简称MSS, 是一个运行于后台的Service, 实现了Runnable接口.
MediaScannerReceiver接收广播, 然后由MSS具体完成工作. MSS中主要工作在ServiceHandler实现
完成2项工作
(1)启动线程
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block.
Thread thr = new Thread(null, this, "MediaScannerService");
thr.start();
之后会调用run方法, 参见2.2
(2) 注册/监听SDCard 卸载事件
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
filter.addDataScheme("file");
filter.setPriority(100);
registerReceiver(mUnmountReceiver, filter);
MSS实现Runable的run方法, onCreate时会调用此方法启动线程.
run方法主要功能就是启动ServiceHandler
public void run()
{
Looper.prepare();
mServiceLooper = Looper.myLooper();
mServiceHandler = new ServiceHandler();
/// M: reduce thread priority after ServiceHandler have been created to avoid cpu starvation
/// which may cause ANR because create service handler too slow.
// reduce priority below other background threads to avoid interfering
// with other services at boot time.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_LESS_FAVORABLE);
Looper.loop();
}
(1) 等待ServiceHandler启动
while (mServiceHandler == null) {
synchronized (this) {
try {
wait(100);
} catch (InterruptedException e) {
MtkLog.e(TAG, "onStartCommand: InterruptedException!");
}
}
}
(2) 处理Intent消息
Bundle arguments = intent.getExtras();
int what;
if (arguments.getString("filepath") != null) {
what = MSG_SCAN_SINGLE_FILE;
} else if ( arguments.getString( "thumbPath" ) != null ) {
Log.d( TAG, "get do extract thumb action " );
what = MSG_EXTRACT_THUMBNAILS;
} else {
what = MSG_SCAN_DIRECTORY;
}
(3) 把消息传给ServiceHandler
Message msg = mServiceHandler.obtainMessage(what, startId, -1, arguments);
mServiceHandler.sendMessage(msg);
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
private final IMediaScannerService.Stub mBinder =
new IMediaScannerService.Stub() {
public void requestScanFile(String path, String mimeType, IMediaScannerListener listener)
{
if (false) {
Log.d(TAG, "IMediaScannerService.scanFile: " + path + " mimeType: " + mimeType);
}
Bundle args = new Bundle();
args.putString("filepath", path);
args.putString("mimetype", mimeType);
if (listener != null) {
args.putIBinder("listener", listener.asBinder());
}
startService(new Intent(MediaScannerService.this,
MediaScannerService.class).putExtras(args));
}
public void scanFile(String path, String mimeType) {
requestScanFile(path, mimeType, null);
}
};
处理2中类型
文件夹:
scan(directories, volume);
文件:
uri = scanFile(filePath, arguments.getString(“mimetype”));
private final class ServiceHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
Bundle arguments = (Bundle) msg.obj;
String filePath = arguments.getString("filepath");
try {
if (filePath != null) {
IBinder binder = arguments.getIBinder("listener");
IMediaScannerListener listener =
(binder == null ? null : IMediaScannerListener.Stub.asInterface(binder));
Uri uri = null;
try {
uri = scanFile(filePath, arguments.getString("mimetype"));
} catch (Exception e) {
Log.e(TAG, "Exception scanning file", e);
}
if (listener != null) {
listener.scanCompleted(filePath, uri);
}
} else {
String volume = arguments.getString("volume");
String[] directories = null;
if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
// scan internal media storage
directories = new String[] {
Environment.getRootDirectory() + "/media",
Environment.getOemDirectory() + "/media",
};
}
else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
// scan external storage volumes
directories = mExternalStoragePaths;
}
if (directories != null) {
if (false) Log.d(TAG, "start scanning volume " + volume + ": "
+ Arrays.toString(directories));
scan(directories, volume);
if (false) Log.d(TAG, "done scanning volume " + volume);
}
}
} catch (Exception e) {
Log.e(TAG, "Exception in handleMessage", e);
}
stopSelf(msg.arg1);
}
};
}
scanFile 和scan 逻辑上基本相同, 都是调用framworks/base/media/java/android/media/MediaScanner.java完成具体工作
framworks/base/media/java/android/media/MediaScanner.java
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;
}
核心:
scanner.scanSingleFile(canonicalPath, volumeName, mimeType);
private Uri scanFile(String path, String mimeType) {
String volumeName = MediaProvider.EXTERNAL_VOLUME;
openDatabase(volumeName);
MediaScanner scanner = createMediaScanner();
try {
// make sure the file path is in canonical form
String canonicalPath = new File(path).getCanonicalPath();
return scanner.scanSingleFile(canonicalPath, volumeName, mimeType);
} catch (Exception e) {
Log.e(TAG, "bad path " + path + " in scanFile()", e);
return null;
}
}
核心语句
scanner.scanDirectories(directories, volumeName);
private void scan(String[] directories, String volumeName) {
Uri uri = Uri.parse("file://" + directories[0]);
// don't sleep while scanning
mWakeLock.acquire();
try {
ContentValues values = new ContentValues();
values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
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);
} finally {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
mWakeLock.release();
}
}