Android MediaScannerService源码分析
16lz
2021-01-23
1. 简介
MediaScannerService简称MSS, 是一个运行于后台的Service, 实现了Runnable接口.
MediaScannerReceiver接收广播, 然后由MSS具体完成工作. MSS中主要工作在ServiceHandler实现
2. 初始化
2.1 onCreate()
完成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);
2.2 run
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(); }
2.3 onStartCommand
(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);
2.4 onBind
@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); } };
3. ServiceHandler
处理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); } };}
4. scanFile & scan
scanFile 和scan 逻辑上基本相同, 都是调用framworks/base/media/java/android/media/MediaScanner.java完成具体工作
4.1 createMediaScanner
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; }
4.2 scanFile
核心:
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; } }
4.3 scan
核心语句
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(); } }
更多相关文章
- Android软键盘弹出时把布局顶上去,控件乱套解决方法
- Android命令行测试BT,WIFI,Sensor工作状态
- Android运行main方法后java虚拟机停止运行
- Android中拍照、图片、录音、视频和音频功能的方法和代码
- Android应用程序的调试方法
- android开关飞行模式的方法
- Android Http请求方法汇总
- 详解Android获取系统内核版本的方法与实现代码