[转载]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——————————