国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Android MediaScanner
http://www.apkbus.com/android-5376-1-1.html
MediaScanner分析

  一 MediaScannerService

  多媒體掃描是從MediaScannerService開始的。這是一個(gè)單獨(dú)的package。位于packagesprovidersMediaProvider:含以下java文件

java代碼:
  1. MediaProvider.java
  2. MediaScannerReceiver.java
  3. MediaScannerService.java
  4. MediaThumbRequest.java
復(fù)制代碼

  分析這個(gè)目錄的Android.mk文件,發(fā)現(xiàn)它運(yùn)行的進(jìn)程名字就是android.process.media。
application android:process=android.process.media

  1.1 MediaScannerReceiver

  這個(gè)類從BroadcastReceiver中派生,用來接收任務(wù)的。MediaScannerReceiver extends BroadcastReceiver
在它重載的onRecieve函數(shù)內(nèi)有以下幾種走向:

java代碼:
  1. if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {

  2. // 收到”啟動(dòng)完畢“廣播后,掃描內(nèi)部存儲(chǔ)
  3. scan(context, MediaProvider.INTERNAL_VOLUME);
  4. } else {
  5. if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&
  6. externalStoragePath.equals(path)) {
  7. /收到MOUNT信息后,掃描外部存儲(chǔ)
  8. scan(context, MediaProvider.EXTERNAL_VOLUME);
  9. }
  10. else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
  11. path != null && path.startsWith(externalStoragePath + "/")) {
  12. //收到請(qǐng)求要求掃描某個(gè)文件,注意不會(huì)掃描內(nèi)部存儲(chǔ)上的文件
  13. scanFile(context, path);

  14. }
復(fù)制代碼
下面是它調(diào)用的scan函數(shù):

java代碼:
  1. scan(Context context, String volume)

  2. Bundle args = new Bundle();
  3. args.putString("volume", volume);
  4. //直接啟動(dòng)MediaScannerService了,
  5. context.startService(new Intent(context, MediaScannerService.class).putExtras(args));
復(fù)制代碼

   總結(jié):

  MediaScannerReceiver是用來接收任務(wù)的,它收到廣播后,會(huì)啟動(dòng)MediaService進(jìn)行掃描工作。
  下面看看MediaScannerService.

  1.2 MediaScannerService

       MSS標(biāo)準(zhǔn)的從Service中派生下來,
  MediaScannerService extends Service implements Runnable
  //注意:是一個(gè)Runnable…,可能有線程之類的東西存在
  下面從Service的生命周期的角度來看看它的工作。

       1. onCreate

java代碼:
  1. public void onCreate()

  2. PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
  3. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
  4. //獲得電源鎖,防止在掃描過程中休眠
  5. //單獨(dú)搞一個(gè)線程去跑掃描工作,防止ANR
  6. Thread thr = new Thread(null, this, "MediaScannerService");
  7. thr.start();
復(fù)制代碼
  2. onStartCommand

java代碼:
  1. @Override
  2. public int onStartCommand(Intent intent, int flags, int startId){

  3. //注意這個(gè)handler,是在另外一個(gè)線程中創(chuàng)建的,往這個(gè)handler里sendMessage
  4. //都會(huì)在那個(gè)線程里邊處理
  5. //不明白的可以去查看handler和Looper機(jī)制
  6. //這里就是同步機(jī)制,等待mServiceHandler在另外那個(gè)線程創(chuàng)建完畢

  7. while (mServiceHandler == null) {
  8. synchronized (this) {
  9. try {
  10. wait(100);
  11. } catch (InterruptedException e) {
  12. }
  13. }
  14. }

  15. if (intent == null) {
  16. Log.e(TAG, "Intent is null in onStartCommand: ",new NullPointerException());
  17. return Service.START_NOT_STICKY;
  18. }
  19. Message msg = mServiceHandler.obtainMessage();
  20. msg.arg1 = startId;
  21. msg.obj = intent.getExtras();
  22. //把MediaScannerReceiver發(fā)出的消息傳遞到另外那個(gè)線程去處理。

  23. mServiceHandler.sendMessage(msg);
復(fù)制代碼

     3. run

java代碼:
  1. public void run(){

  2. // reduce priority below other background threads to avoid interfering
  3. // with other services at boot time.

  4. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +Process.THREAD_PRIORITY_LESS_FAVORABLE);

  5. //不明白的去看看Looper和handler的實(shí)現(xiàn)
  6. Looper.prepare();
  7. //把這個(gè)looper對(duì)象設(shè)置到線程本地存儲(chǔ)
  8. mServiceLooper = Looper.myLooper();
  9. mServiceHandler = new ServiceHandler();
  10. //創(chuàng)建handler,默認(rèn)會(huì)把這個(gè)looper
  11. //的消息隊(duì)列賦值給handler的消息隊(duì)列,這樣往handler中發(fā)送消息就是往這個(gè)線程的looper發(fā)
  12. Looper.loop();
  13. //消息循環(huán),內(nèi)部會(huì)處理消息隊(duì)列中的消息
  14. //也就是handleMessage函數(shù)
  15. }
復(fù)制代碼

    上面handler中加入了一個(gè)掃描請(qǐng)求(假設(shè)是外部存儲(chǔ)的),所以要分析handleMessage函數(shù)。

     4. handleMessage

java代碼:
  1. private final class ServiceHandler extends Handler{

  2. @Override
  3. public void handleMessage(Message msg){

  4. Bundle arguments = (Bundle) msg.obj;
  5. String filePath = arguments.getString("filepath");
  6. try {

  7. 這里不講了
  8. } else {
  9. String volume = arguments.getString("volume");
  10. String[] directories = null;
  11. if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
  12. //是掃描內(nèi)部存儲(chǔ)的請(qǐng)求?
  13. // scan internal media storage
  14. directories = new String[] {

  15. Environment.getRootDirectory() + "/media",
  16. };
  17. }

  18. else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
  19. //是掃描外部存儲(chǔ)的請(qǐng)求?獲取外部存儲(chǔ)的路徑
  20. directories = new String[] {
  21. Environment.getExternalStorageDirectory().getPath(),
  22. };
  23. }
  24. if (directories != null) {
  25. //真正的掃描開始了,上面只不過是把存儲(chǔ)路徑取出來罷了.
  26. scan(directories, volume);
  27. //掃描完了,就把service停止了
  28. stopSelf(msg.arg1);
  29. }

  30. };
復(fù)制代碼

    5. scan函數(shù)

java代碼:
  1. private void scan(String[] directories, String volumeName) {
  2. mWakeLock.acquire();
  3. //下面這三句話很深?yuàn)W…
  4. //從 getContentResolver獲得一個(gè)ContentResover,然后直接插入
  5. //根據(jù)AIDL,這個(gè)ContentResover的另一端是MediaProvider。只要去看看它的
  6. //insert函數(shù)就可以了
  7. //反正這里知道獲得了一個(gè)掃描URI即可。
  8. ContentValues values = new ContentValues();
  9. values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
  10. Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
  11. Uri uri = Uri.parse("file://" + directories[0]);
  12. //發(fā)送廣播,通知掃描開始了
  13. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));

  14. try {
  15. if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
  16. openDatabase(volumeName);
  17. }
  18. //創(chuàng)建真正的掃描器
  19. MediaScanner scanner = createMediaScanner();
  20. //交給掃描器去掃描文件夾 scanDirectories
  21. scanner.scanDirectories(directories, volumeName);
  22. } catch (Exception e) {
  23. Log.e(TAG, "exception in MediaScanner.scan()", e);
  24. }
  25. //刪除掃描路徑
  26. getContentResolver().delete(scanUri, null, null);
  27. //通知掃描完畢
  28. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
  29. mWakeLock.release();
  30. }
復(fù)制代碼
  說說上面那個(gè)深?yuàn)W的地方,在MediaProvider中重載了insert函數(shù),insert函數(shù)會(huì)調(diào)用insertInternal函數(shù)。
  如下:

java代碼:
  1. private Uri insertInternal(Uri uri, ContentValues initialValues) {
  2. long rowId;

  3. int match = URI_MATCHER.match(uri);
  4. // handle MEDIA_SCANNER before calling getDatabaseForUri()
  5. //剛才那個(gè)insert只會(huì)走下面這個(gè)分支,其實(shí)就是獲得一個(gè)地址….
  6. //太繞了

  7. if (match == MEDIA_SCANNER) {
  8. mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
  9. return MediaStore.getMediaScannerUri();
  10. }
復(fù)制代碼
   再看看它創(chuàng)建了什么樣的Scanner,這就是MSS中的createMediaScanner

java代碼:
  1. private MediaScanner createMediaScanner() {

  2. //下面這個(gè)MediaScanner在framework/base/中,待會(huì)再分析
  3. MediaScanner scanner = new MediaScanner(this);
  4. //設(shè)置當(dāng)前的區(qū)域,這個(gè)和字符編碼有重大關(guān)系。
  5. Locale locale = getResources().getConfiguration().locale;

  6. if (locale != null) {
  7. String language = locale.getLanguage();
  8. String country = locale.getCountry();
  9. String localeString = null;
  10. if (language != null) {
  11. if (country != null) {
  12. //給掃描器設(shè)置當(dāng)前國(guó)家和語言。
  13. scanner.setLocale(language + "_" + country);
  14. } else {
  15. scanner.setLocale(language);
  16. }
  17. }
  18. }
  19. return scanner;

  20. }
復(fù)制代碼
至此,MSS的任務(wù)完成了。接下來是MediaScanner的工作了。
6. 總結(jié)

       MSS的工作流程如下:

  1 單獨(dú)啟動(dòng)一個(gè)帶消息循環(huán)的工作線程。
  2 主線程接收系統(tǒng)發(fā)來的任務(wù),然后發(fā)送給工作線程去處理。
  3 工作線程接收任務(wù),創(chuàng)建一個(gè)MediaScanner去掃描。
  4 MSS順帶廣播一下掃描工作啟動(dòng)了,掃描工作完畢了。

       二 MediaScanner

  MediaScanner位置在
  frameworks asemedia下,包括jni和java文件。
  先看看java實(shí)現(xiàn)。
  這個(gè)類巨復(fù)雜,而且和MediaProvider交互頻繁。在分析的時(shí)候要時(shí)刻回到MediaProvider去看看。
  1. 初始化

java代碼:
  1. public class MediaScanner{

  2. static {
  3. //libmedia_jni.so的加載是在MediaScanner類中完成的
  4. //這么重要的so為何放在如此不起眼的地方加載???

  5. System.loadLibrary("media_jni");
  6. native_init();
  7. }

  8. public MediaScanner(Context c) {
  9. native_setup();//調(diào)用jni層的初始化,暫時(shí)不用看了,無非就是一些
  10. //初始化工作,待會(huì)在再進(jìn)去看看

  11. }
復(fù)制代碼


  剛才MSS中是調(diào)用scanDirectories函數(shù),我們看看這個(gè)。

       2. scanDirectories

java代碼:
  1. public void scanDirectories(String[] directories, String volumeName) {
  2. try {
  3. long start = System.currentTimeMillis();
  4. initialize(volumeName);//初始化

  5. prescan(null);//掃描前的預(yù)處理
  6. long prescan = System.currentTimeMillis();
  7. for (int i = 0; i < directories.length; i++) {

  8. //掃描文件夾,這里有一個(gè)很重要的參數(shù) mClient
  9. // processDirectory是一個(gè)native函數(shù)
  10. processDirectory(directories[i], MediaFile.sFileExtensions, mClient);
  11. }

  12. long scan = System.currentTimeMillis();
  13. postscan(directories);//掃描后處理
  14. long end = System.currentTimeMillis();

  15. 打印時(shí)間,異常處理沒了
  16. 下面簡(jiǎn)單講講initialize ,preScan和postScan都干嘛了。

  17. private void initialize(String volumeName) {

  18. //打開MediaProvider,獲得它的一個(gè)實(shí)例

  19. mMediaProvider = mContext.getContentResolver().acquireProvider("media");
  20. //得到一些uri
  21. mAudioUri = Audio.Media.getContentUri(volumeName);
  22. mVideoUri = Video.Media.getContentUri(volumeName);
  23. mImagesUri = Images.Media.getContentUri(volumeName);
  24. mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
  25. //外部存儲(chǔ)的話,可以支持播放列表之類的東西,搞了一些個(gè)緩存池之類的
  26. //如mGenreCache等
  27. if (!volumeName.equals("internal")) {
  28. // we only support playlists on external media
  29. mProcessPlaylists = true;
  30. mGenreCache = new HashMap();

  preScan,這個(gè)函數(shù)很復(fù)雜:

  大概就是創(chuàng)建一個(gè)FileCache,用來緩存掃描文件的一些信息,例如last_modified等。這個(gè)FileCache是從MediaProvider中已有信息構(gòu)建出來的,也就是歷史信息。后面根據(jù)掃描得到的新信息來對(duì)應(yīng)更新歷史信息。

     postScan,這個(gè)函數(shù)做一些清除工作,例如以前有video生成了一些縮略圖,現(xiàn)在video文件被干掉了,則對(duì)應(yīng)的縮略圖也要被干掉。

  另外還有一個(gè)mClient,這個(gè)是從MediaScannerClient派生下來的一個(gè)東西,里邊保存了一個(gè)文件的一些信息。后續(xù)再分析。

       在frameworks asemediajniandroid_media_MediaScanner.cpp中。

  剛才說到,具體掃描工作是在processDirectory函數(shù)中完成的。這個(gè)是一個(gè)native函數(shù)。
     三 MediaScanner JNI層分析

  MediaScanner JNI層內(nèi)容比較多,單獨(dú)搞一節(jié)分析吧。先看看android_media_MediaScanner這個(gè)文件。

  1. native_init函數(shù),jni對(duì)應(yīng)的函數(shù)如下

java代碼:
  1. static void

  2. android_media_MediaScanner_native_init(JNIEnv *env){

  3. jclass clazz;
  4. clazz = env->FindClass("android/media/MediaScanner");
  5. //得都JAVA類中mNativeContext這個(gè)成員id

  6. fields.context = env->GetFieldID(clazz, "mNativeContext", "I");

  7. //不熟悉JNI的自己去學(xué)習(xí)下吧
  8. }
復(fù)制代碼
2. native_setup函數(shù),jni對(duì)應(yīng)函數(shù)如下:

java代碼:
  1. android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz){

  2. //創(chuàng)建MediaScanner對(duì)象
  3. MediaScanner *mp = createMediaScanner();
  4. //太變態(tài)了,自己不保存這個(gè)對(duì)象指針.
  5. //卻把它設(shè)置到j(luò)ava對(duì)象的mNativeContext去保存
  6. env->SetIntField(thiz, fields.context, (int)mp);
  7. }

  8. //創(chuàng)建MediaScanner函數(shù)
  9. static MediaScanner *createMediaScanner() {

  10. #if BUILD_WITH_FULL_STAGEFRIGHT

  11. //使用google自己的

  12. return new StagefrightMediaScanner;

  13. #endif
  14. #ifndef NO_OPENCORE
  15. //使用opencore提供的.
  16. return new PVMediaScanner();
復(fù)制代碼

  3. processDirectories函數(shù),jni對(duì)應(yīng)如下:

java代碼:
  1. android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client){

  2. MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
  3. //每次都要回調(diào)到JAVA中去取這個(gè)Scanner!!

  4. const char *pathStr = env->GetStringUTFChars(path, NULL);
  5. const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);

  6. //又在C++這里搞一個(gè)client,然后把java的client放到C++Client中去保存
  7. //而且還是棧上的臨時(shí)變量..
  8. MyMediaScannerClient myClient(env, client);
  9. //scanner掃描文件夾,用得是C++的client

  10. mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
  11. env->ReleaseStringUTFChars(path, pathStr);
  12. env->ReleaseStringUTFChars(extensions, extensionsStr);
  13. }
復(fù)制代碼

到這里似乎就沒有了,那么掃描后的數(shù)據(jù)庫(kù)是怎么更新的呢?為什么要傳入一個(gè)client進(jìn)去呢?看來必須得trace到scanner中去才知道了
四 PVMediaScanner

  這個(gè)在externalopencoreandroidmediascanner.cpp中。

  1. processDirectory

java代碼:
  1. status_t MediaScanner::processDirectory(const char *path, const char* extensions,
  2. MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
  3. {

  4. InitializeForThread();
  5. int error = 0;
  6. status_t result = PVMFSuccess;
  7. //調(diào)用client的設(shè)置區(qū)域函數(shù)
  8. client.setLocale(mLocale);
  9. //掃描文件夾,咋還沒開始??
  10. result = doProcessDirectory(pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv);
復(fù)制代碼

   2. doProcessDirectory

java代碼:
  1. status_t MediaScanner::doProcessDirectory(char *path, int pathRemaining, const char* extensions,
  2. MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
  3. {

  4. //終于看到點(diǎn)希望了
  5. //打開這個(gè)文件夾,枚舉其中的內(nèi)容。
  6. //題外話,這個(gè)時(shí)候FileManager肯定刪不掉這個(gè)文件夾!!

  7. DIR* dir = opendir(path);

  8. while ((entry = readdir(dir))) {
  9. const char* name = entry->d_name;
  10. //不處理.和..文件夾


  11. if (isDirectory) {
  12. //不處理.開頭的文件夾。如果是文件夾,遞歸調(diào)用doProcessDirectory
  13. //深度優(yōu)先啊!
  14. int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);

  15. if (err) {
  16. LOGE("Error processing '%s' - skipping ", path);
  17. continue;
  18. }

  19. } else if (fileMatchesExtension(path, extensions)) {
  20. //是一個(gè)可以處理的文件,交給client處理
  21. //徹底瘋掉了….這是干嘛呢???
  22. client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
復(fù)制代碼

這里要解釋下,剛才createMediaScanner中,明明創(chuàng)建的是PVMediaScanner,為何這里看得是MediaScanner代碼呢?
  因?yàn)?strong>PVMediaScanner從MediaScanner中派生下來的,而且沒有重載processDirectory函數(shù)Eclaire沒有PVMediaScanner這樣的東西,估計(jì)是froyo又改了點(diǎn)啥吧。

       FT,processDirctory無非是列舉一個(gè)目錄內(nèi)容,然后又反回去調(diào)用client的scanFile處理了。為何搞這么復(fù)雜?只有回去看看C++的client干什么了。

       MediaScannerClient---JNI層

  JNI中的這個(gè)類是這樣的:

java代碼:
  1. class MyMediaScannerClient : public MediaScannerClient
  2. //這是它的scanFile實(shí)現(xiàn)

  3. virtual bool scanFile(const char* path, long long lastModified, long long fileSize){

  4. //再次崩潰了,C++的client調(diào)用了剛才傳進(jìn)去的java Client的
  5. //scanFile函數(shù)…不過這次還傳進(jìn)去了該文件的路徑,最后修改時(shí)間和文件大小。

  6. mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
  7. //想死,,,
  8. }


  沒辦法了,只能再去看看MediaScanner.java傳進(jìn)去的那個(gè)client了。

         MediaScannerClient----JAVA層
         這個(gè)類在MediaScanner.java中實(shí)現(xiàn)。

java代碼:
  1. private class MyMediaScannerClient implements MediaScannerClient:

  2. public void scanFile(String path, long lastModified, long fileSize) {
  3. //調(diào)用doScanFile..很煩..

  4. doScanFile(path, null, lastModified, fileSize, false);
  5. //下面是doScanFile

  6. public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, boolean scanAlways) {

  7. //預(yù)處理,看看之前創(chuàng)建的文件緩存中有沒有這個(gè)文件
  8. FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);

  9. // rescan for metadata if file was modified since last scan
  10. if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {

  11. //如果事先有這個(gè)文件的信息,則需要修改一些信息,如長(zhǎng)度,最后修改時(shí)間等
  12. //真正的掃描文件

  13. processFile(path, mimeType, this);
  14. //掃描完了,做最后處理
  15. endFile(entry, ringtones, notifications, alarms, music, podcasts);
  16. //processFile又是jni層的。
  17. //對(duì)應(yīng)android_media_MediaScanner_processFile函數(shù)

  18. android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client){

  19. MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
  20. //無語了,又搞一個(gè) MyMediaScannerClient
  21. MyMediaScannerClient myClient(env, client);
  22. mp->processFile(pathStr, mimeTypeStr, myClient);
  23. }

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Android MediaScanner 詳盡分析
android源碼解析------Media多媒體framework層分析
jni的異常處理
[深入理解Android卷一 全文
【轉(zhuǎn)】JNI 之二 :java & c/c++ 相互通信及調(diào)用
JNI官方規(guī)范中文版——如何使用JNI中的global reference和local reference
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服