[转载]Android开发——MediaProvider源码分析(2) – 努力吧,专注Android – 博客园.
欲读此文,先读上文:MediaProvider源码分析(1)
———————-START—————————
在 上一篇文章中说到系统当接收到扫描请求广播的时候就会调用scan或者scanFile去扫描手机(手机内存和sdcard)中的媒体文件。这两个方法都 是启动MediaScannerService这个服务来完成扫描任务的。接下来我们来看看MediaScannerService是怎么工作的……
4.MediaScannerService(MSS)
MSS实现了Runnable,所以必然的需要实现run方法了,代码如下:
public void run()
{
// 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.prepare();
mServiceLooper = Looper.myLooper();
mServiceHandler = new ServiceHandler();
Looper.loop();
}
在run方法中设置了线程的优先级,优先级比较低,主要为了避免跟其他服务抢夺资源。还有就是利用looper对ServiceHandler的消息进行循环控制。
接着看一下ServiceHandler的实现代码:
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 = scanFile(filePath, arguments.getString("mimetype"));//扫描单个文件
if (listener != null) {
//执行扫描完成方法
listener.scanCompleted(filePath, uri);
}
} else {
//如果文件路径为空,则获取扫面手机内存或者sdcard
String volume = arguments.getString("volume");
String[] directories = null;
//内置卡
if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
// scan internal media storage
directories = new String[] {
Environment.getRootDirectory() + "/media",
};
}//外置卡
else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
// scan external storage
directories = new String[] {
Environment.getExternalStorageDirectory().getPath(),
};
}
if (directories != null) {
if (Config.LOGD) Log.d(TAG, "start scanning volume " + volume);
//扫描
scan(directories, volume);
if (Config.LOGD) Log.d(TAG, "done scanning volume " + volume);
}
}
} catch (Exception e) {
Log.e(TAG, "Exception in handleMessage", e);
}
stopSelf(msg.arg1);
}
};
以上三个方法是属于Service的生命周期的。当我们调用startService的时候,如果对应的Service还未创建就会调用 onCreate方法===方法。每次startService的时候就调用onStartCommand,所以ServiceHandler就在此发送 消息了。
最后,稍微看一下MSS里面扫描方面。主要是调用MediaScanner对媒体文件进行扫描分析的。至于MediaScanner的实现以后在分析。
private void openDatabase(String volumeName) {
try {
ContentValues values = new ContentValues();
values.put("name", volumeName);
getContentResolver().insert(Uri.parse("content://media/"), values);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "failed to open media database");
}
}
private void closeDatabase(String volumeName) {
try {
getContentResolver().delete(
Uri.parse("content://media/" + volumeName), null, null);
} catch (Exception e) {
Log.w(TAG, "failed to close media database " + volumeName + " exception: " + e);
}
}
//创建扫描器
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;
}
//扫描目录
private void scan(String[] directories, String volumeName) {
// don't sleep while scanning
mWakeLock.acquire();
ContentValues values = new ContentValues();
values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
Uri uri = Uri.parse("file://" + directories[0]);
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);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
mWakeLock.release();
}
//扫描文件
private Uri scanFile(String path, String mimeType) {
String volumeName = MediaProvider.INTERNAL_VOLUME;
String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
if (path.startsWith(externalStoragePath)) {
volumeName = MediaProvider.EXTERNAL_VOLUME;
openDatabase(volumeName);
}
MediaScanner scanner = createMediaScanner();
//扫描单个文件
return scanner.scanSingleFile(path, volumeName, mimeType);
}
在MediaProvider中还有一个类:MediaThumbRequest,用来创建预览图的,比如视频的预览图,图片的预览图,音频的专辑图片…这些图片的信息也是保存在数据库的,有兴趣的同学可以自己打开数据库看看里面的表。如下图:
啰哩啰唆的写了两篇文章,希望对大家有所帮助。
其中应该有不少是错误的观点,望大家指正。
———————-END——————————
Mikel
