Settings.system是保存手机的一些用户设置项,像飞行模式,铃声设置,保证关机后用户设置的数据还存在;SystemProperties是中保存着系统的配置属性,MediaStore是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像的路径和详细详细信息,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作MediaStore中的数据则是在android多媒体扫描的时候添加进数据库的,首先在/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java会接收到相应文件扫描的广播,然后根据相应的广播进行扫描,主要是处理三种Broardcast请求,也有厂商自己自定义自己系类的广播扫描方式
BOOT_COMPLETED 系统启动之后,接到该Action调用 scan(context, MediaProvider.INTERNAL_VOLUME);和 scanUntilAllStorageMounted(context);将内部存储和外部存储的数据扫描到对应的数据库。
所以你在开发的时候,每次开模拟器或手机你注意Logcat信息,就可以发现:scanner Internal Volumn...等等相关信息
MEDIA_MOUNTED 外部存储卡挂载之后,接到该Action调用scanUntilAllStorageMounted(context)扫描外部Media;
MEDIA_SCANNER_SCAN_FILE 扫描某一个多媒体文件,接到该Action之后scanFile(context, path)(app端添加文件后发送广播,将相应的多媒体添加到数据库....);
但是不管是sscan(context, MediaProvider.INTERNAL_VOLUME); scanUntilAllStorageMounted(context);在或者scanFile(context, path)在MediaScannerReceiver中最后都通过context.startService(new Intent(context, MediaScannerService.class).putExtras(args));启动了MediaScannerService服务。
在packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java
继承了service实现了Runnable接口,在相应的run方法中
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();
}
在run方法中设置了线程的优先级,优先级比较低,主要为了避免跟其他服务抢夺资源。还有就是利用looper对ServiceHandler的消息进行循环控制。
每次在onStartCommand通过mServiceHandler刷新Handler 要发送的消息;
public int onStartCommand(Intent intent, int flags, int startId)
{
……….
Message msg = mServiceHandler.obtainMessage(what, startId, -1, arguments);
mServiceHandler.sendMessage(msg);
// Try again later if we are killed before we can finish scanning.
return Service.START_REDELIVER_INTENT;
}
接着看一下ServiceHandler的实现代码:
private final class ServiceHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
/// M: MediaScanner Performance turning {@
/// Add two message for shutdown threadpool
/// and handle scan finish request.
MtkLog.v(TAG, "handleMessage: what = " + msg.what + ", startId = " + msg.arg1 + ", arguments = " + msg.obj);
switch (msg.what) {
case MSG_SCAN_SINGLE_FILE:
handleScanSingleFile(msg);
break;
case MSG_SCAN_DIRECTORY:
handleScanDirectory(msg);
break;
case MSG_SHUTDOWN_THREADPOOL:
handleShutdownThreadpool();
break;
case MSG_SCAN_FINISH_WITH_THREADPOOL:
handleScanFinish();
break;
default:
MtkLog.w(TAG, "unsupport message " + msg.what);
break;
}
/// @}
}
};
在相应的case中定义了不同的扫描方式和当扫描结束等情况;
但都没有对文件进行扫描,相应的走到了scanner.scanSingleFile(canonicalPath, volumeName, mimeType)或者 scanner.scanDirectories(directories, volumeName);或mPreScanner.postScanAll(mMediaScannerThreadPool.getPlaylistFilePaths());
最终都指向了frameworks/base/media/java/android/media/MediaScanner.java,都是MediaScanner中的方法。
在MediaScanner对上层隐藏了,用了@hide的注解,开始的静态代码块中
static {
System.loadLibrary("media_jni");
native_init();
}
加载了media_jni库,做了jni本地方法的初始化,接着看MediaScannerService 调用的MediaScanner的scanDirectories和scanSingleFile完成最终的扫描的方法:
public void scanDirectories(String[] directories, String volumeName) {
try {
long start = System.currentTimeMillis();
initialize(volumeName);
prescan(null, true);
long prescan = System.currentTimeMillis();
if (ENABLE_BULK_INSERTS) {
// create MediaInserter for bulk inserts
mMediaInserter = new MediaInserter(mMediaProvider, mPackageName, 500);
}
for (int i = 0; i < directories.length; i++) {
processDirectory(directories[i], mClient);
}
if (ENABLE_BULK_INSERTS) {
// flush remaining inserts
mMediaInserter.flushAll();
mMediaInserter = null;
}
long scan = System.currentTimeMillis();
postscan(directories);
long end = System.currentTimeMillis();
分了initialize ,preScan和postScan三个函数
[java] view plaincopyprint?
private void initialize(String volumeName) {
//打开MediaProvider,获得它的一个实例
mMediaProvider = mContext.getContentResolver().acquireProvider("media");
//得到一些uri
mAudioUri = Audio.Media.getContentUri(volumeName);
mVideoUri = Video.Media.getContentUri(volumeName);
mImagesUri = Images.Media.getContentUri(volumeName);
mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
//外部存储的话,可以支持播放列表之类的东西,搞了一些个缓存池之类的
//如mGenreCache等
if (!volumeName.equals("internal")) {
// we only support playlists on external media
mProcessPlaylists = true;
mGenreCache = new HashMap<String, Uri>();
…
preScan,这个函数很复杂:
大概就是创建一个FileCache,用来缓存扫描文件的一些信息,例如last_modified等。这个FileCache是从MediaProvider中已有信息构建出来的,也就是历史信息。后面根据扫描得到的新信息来对应更新历史信息。
postScan,这个函数做一些清除工作,例如以前有video生成了一些缩略图,现在video文件被干掉了,则对应的缩略图也要被干掉。另外还有一个mClient,这个是从MediaScannerClient派生下来的一个东西,里边保存了一个文件的一些信息。
具体扫描工作是在processDirectory函数中完成的。这个是一个native函数。
在frameworks\base\media\jni\android_media_MediaScanner.cpp中,和frameworks/av/media/libmedia/MediaScanner.cpp一番转转后将扫描结果返回上层在frameworks/base/media/java/android/media/MediaScanner.java中的handleStringTag解析的结果返回到上层应用。