Android開發——MediaProvider原始碼分析(2)
----------------------START---------------------------
在上一篇文章中說到系統當接收到掃描請求廣播的時候就會呼叫scan或者scanFile去掃描手機(手機記憶體和sdcard)中的媒體檔案。這兩個方法都是啟動MediaScannerService這個服務來完成掃描任務的。接下來我們來看看MediaScannerService是怎麼工作的……
4.MediaScannerService(MSS)
MSS實現了Runnable,所以必然的需要實現run方法了,程式碼如下:
1: public void run()
2: {
3: // reduce priority below other background threads to avoid interfering
4: // with other services at boot time.
5: Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +
6: Process.THREAD_PRIORITY_LESS_FAVORABLE);
7: Looper.prepare();
8:
9: mServiceLooper = Looper.myLooper();
10: mServiceHandler = new ServiceHandler();
11:
12: Looper.loop();
13: }
接著看一下ServiceHandler的實現程式碼:
1: private final class ServiceHandler extends Handler
2: {
3: @Override
4: public void handleMessage(Message msg)
5: {
6: Bundle arguments = (Bundle) msg.obj;
7: //獲取檔案路徑
8: String filePath = arguments.getString("filepath");
9:
10: try {
11: if (filePath != null) {
12: //檔案路徑不為空,則呼叫掃面當個檔案的方法
13: IBinder binder = arguments.getIBinder("listener");
14: IMediaScannerListener listener =
15: (binder == null ? null : IMediaScannerListener.Stub.asInterface(binder));
16: Uri uri = scanFile(filePath, arguments.getString("mimetype"));//掃描單個檔案
17: if (listener != null) {
18: //執行掃描完成方法
19: listener.scanCompleted(filePath, uri);
20: }
21: } else {
22: //如果檔案路徑為空,則獲取掃面手機記憶體或者sdcard
23: String volume = arguments.getString("volume");
24: String[] directories = null;
25: //內建卡
26: if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
27: // scan internal media storage
28: directories = new String[] {
29: Environment.getRootDirectory() + "/media",
30: };
31: }//外接卡
32: else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
33: // scan external storage
34: directories = new String[] {
35: Environment.getExternalStorageDirectory().getPath(),
36: };
37: }
38:
39: if (directories != null) {
40: if (Config.LOGD) Log.d(TAG, "start scanning volume " + volume);
41: //掃描
42: scan(directories, volume);
43: if (Config.LOGD) Log.d(TAG, "done scanning volume " + volume);
44: }
45: }
46: } catch (Exception e) {
47: Log.e(TAG, "Exception in handleMessage", e);
48: }
49:
50: stopSelf(msg.arg1);
51: }
52: };
在ServiceHandler中主要根據相關引數來呼叫不同的掃描方法。
那是在哪裡呼叫ServiceHandler傳送訊息的呢?請看如下程式碼:
1: @Override
2: public void onCreate() {
3: PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
4: mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
5: //啟用新執行緒,這樣就可以避免阻塞,執行run,初始化成員變數loop和handler
6: Thread thr = new Thread(null, this, "MediaScannerService");
7: thr.start();
8: }
9:
10: @Override
11: public int onStartCommand(Intent intent, int flags, int startId) {
12: while (mServiceHandler == null) {
13: synchronized (this) {
14: try {
15: wait(100);
16: } catch (InterruptedException e) {
17: }
18: }
19: }
20:
21: if (intent == null) {
22: Log.e(TAG, "Intent is null in onStartCommand: ", new NullPointerException());
23: return Service.START_NOT_STICKY;
24: }
25:
26: Message msg = mServiceHandler.obtainMessage();
27: msg.arg1 = startId;
28: msg.obj = intent.getExtras();
29: //ServiceHandler傳送訊息
30: mServiceHandler.sendMessage(msg);
31:
32: // Try again later if we are killed before we can finish scanning.
33: return Service.START_REDELIVER_INTENT;
34: }
35:
36: @Override
37: public void onDestroy() {
38: // Make sure thread has started before telling it to quit.
39: while (mServiceLooper == null) {
40: synchronized (this) {
41: try {
42: wait(100);
43: } catch (InterruptedException e) {
44: }
45: }
46: }
47: mServiceLooper.quit();
48: }
以上三個方法是屬於Service的生命週期的。當我們呼叫startService的時候,如果對應的Service還未建立就會呼叫onCreate方法===方法。每次startService的時候就呼叫onStartCommand,所以ServiceHandler就在此傳送訊息了。
最後,稍微看一下MSS裡面掃描方面。主要是呼叫MediaScanner對媒體檔案進行掃描分析的。至於MediaScanner的實現以後在分析。
1: private void openDatabase(String volumeName) {
2: try {
3: ContentValues values = new ContentValues();
4: values.put("name", volumeName);
5: getContentResolver().insert(Uri.parse("content://media/"), values);
6: } catch (IllegalArgumentException ex) {
7: Log.w(TAG, "failed to open media database");
8: }
9: }
10:
11: private void closeDatabase(String volumeName) {
12: try {
13: getContentResolver().delete(
14: Uri.parse("content://media/" + volumeName), null, null);
15: } catch (Exception e) {
16: Log.w(TAG, "failed to close media database " + volumeName + " exception: " + e);
17: }
18: }
19: //建立掃描器
20: private MediaScanner createMediaScanner() {
21: MediaScanner scanner = new MediaScanner(this);
22: Locale locale = getResources().getConfiguration().locale;
23: if (locale != null) {
24: String language = locale.getLanguage();
25: String country = locale.getCountry();
26: String localeString = null;
27: if (language != null) {
28: if (country != null) {
29: scanner.setLocale(language + "_" + country);
30: } else {
31: scanner.setLocale(language);
32: }
33: }
34: }
35:
36: return scanner;
37: }
38: //掃描目錄
39: private void scan(String[] directories, String volumeName) {
40: // don't sleep while scanning
41: mWakeLock.acquire();
42:
43: ContentValues values = new ContentValues();
44: values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
45: Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
46:
47: Uri uri = Uri.parse("file://" + directories[0]);
48: sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
49:
50: try {
51: if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
52: openDatabase(volumeName);
53: }
54:
55: MediaScanner scanner = createMediaScanner();
56: scanner.scanDirectories(directories, volumeName);
57: } catch (Exception e) {
58: Log.e(TAG, "exception in MediaScanner.scan()", e);
59: }
60:
61: getContentResolver().delete(scanUri, null, null);
62:
63: sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
64: mWakeLock.release();
65: }
66: //掃描檔案
67: private Uri scanFile(String path, String mimeType) {
68: String volumeName = MediaProvider.INTERNAL_VOLUME;
69: String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
70:
71: if (path.startsWith(externalStoragePath)) {
72: volumeName = MediaProvider.EXTERNAL_VOLUME;
73: openDatabase(volumeName);
74: }
75: MediaScanner scanner = createMediaScanner();
76: //掃描單個檔案
77: return scanner.scanSingleFile(path, volumeName, mimeType);
78: }
在MediaProvider中還有一個類:MediaThumbRequest,用來建立預覽圖的,比如視訊的預覽圖,圖片的預覽圖,音訊的專輯圖片…這些圖片的資訊也是儲存在資料庫的,有興趣的同學可以自己開啟資料庫看看裡面的表。如下圖:
囉哩囉唆的寫了兩篇文章,希望對大家有所幫助。
其中應該有不少是錯誤的觀點,望大家指正。
----------------------END------------------------------
相關文章
- Android開發——MediaProvider原始碼分析(1)AndroidIDE原始碼
- Android開發Handler原始碼分析Android原始碼
- iOS開發原始碼閱讀篇--FMDB原始碼分析2(FMResultSet)iOS原始碼
- Android開發AsyncTask原始碼分析【模板方法模式】Android原始碼模式
- Android開源原始碼分析Android原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- Android View 事件分發原始碼分析AndroidView事件原始碼
- 【Android原始碼】Fragment 原始碼分析Android原始碼Fragment
- 【Android原始碼】Intent 原始碼分析Android原始碼Intent
- Android開發Message原始碼分析【享元模式|物件池】Android原始碼模式物件
- 【Android原始碼】AlertDialog 原始碼分析Android原始碼
- Android 開源專案PhotoView原始碼分析AndroidView原始碼
- Mysql原始碼分析2MySql原始碼
- Android 常用開發工具類原始碼Android原始碼
- Android 開源專案原始碼解析 -->Android Universal Image Loader 原始碼分析(十四)Android原始碼
- Android Choreographer 原始碼分析Android原始碼
- Android RecycleView原始碼分析AndroidView原始碼
- Android——Handler原始碼分析Android原始碼
- 【Android原始碼】Handler 機制原始碼分析Android原始碼
- ecshop 二次開發,原始碼分析原始碼
- iOS開發原始碼閱讀篇--FMDB原始碼分析1(FMResultSet)iOS原始碼
- 使用eclipse來開發Android原始碼EclipseAndroid原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Android原始碼分析--CircleImageView 原始碼詳解Android原始碼View
- Redux原始碼分析(2) - createStoreRedux原始碼
- 3.24 vchain原始碼分析2AI原始碼
- RxJava2原始碼分析RxJava原始碼
- Android中IntentService原始碼分析AndroidIntent原始碼
- 【Android原始碼】LayoutInflater 分析Android原始碼
- Android JiaoZiVideoPlayer原始碼分析AndroidIDE原始碼
- Android的AsyncTask原始碼分析Android原始碼
- Android SharedPreferences 原始碼分析Android原始碼
- android view layout原始碼分析AndroidView原始碼
- Android 原始碼結構分析Android原始碼
- Android Volley原始碼分析Android原始碼
- Android 原始碼分析之旅3 1 訊息機制原始碼分析Android原始碼
- Struts2 原始碼分析-----工作原理分析原始碼