久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Android MediaScanner 詳盡分析...

 昵稱128678 2011-06-10

MediaScanner 分析

MediaScannerService

多媒體掃描是從 MediaScannerService 開始的,。這是一個單獨的 package ,。位于

packages\providers\MediaProvider :含以下 java 文件

l          MediaProvider.java

l          MediaScannerReceiver.java

l          MediaScannerService.java

l          MediaThumbRequest.java

分析這個目錄的 Android.mk 文件,發(fā)現(xiàn)它運行的進程名字就是 android.process.media ,。

application android:process = android.process.media

1.1     MediaScannerReceiver

這個類從 BroadcastReceiver 中派生,,用來接收任務的。

MediaScannerReceiver extends BroadcastReceiver

在它重載的onRecieve 函數(shù)內有以下幾種走向:

if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {

             // 收到 啟動完畢“廣播后,,掃描內部存儲

            scan(context, MediaProvider.INTERNAL_VOLUME);

        } else {

            ……….

                if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&

                        externalStoragePath.equals(path)) {

               / 收到MOUNT 信息后,,掃描外部存儲

                    scan(context, MediaProvider.EXTERNAL_VOLUME);

                }

  else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&

                        path != null && path.startsWith(externalStoragePath + "/")) {

               // 收到請求要求掃描某個文件,,注意不會掃描內部存儲上的文件

                    scanFile(context, path);

        …………………………..

        }

…… 下面是它調用的scan 函數(shù):

scan(Context context, String volume)

Bundle args = new Bundle();

        args.putString("volume", volume);

// 直接啟動MediaScannerService 了,

         context.startService(

                new Intent(context, MediaScannerService.class).putExtras(args));

 

總結:

MediaScannerReceiver 是用來接收任務的,,它收到廣播后,,會啟動 MediaService 進行掃描工作。

下面看看 MediaScannerService.

1.2     MediaScannerService

MSS 標準的從 Service 中派生下來,,

MediaScannerService extends Service implements Runnable

// 注意:是一個Runnable… ,,可能有線程之類的東西存在

下面從 Service 的生命周期的角度來看看它的工作。

1. onCreate

public void onCreate()

       PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);

        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

      // 獲得電源鎖,,防止在掃描過程中休眠

      // 單獨搞一個線程去跑掃描工作,,防止ANR

     Thread thr = new Thread(null, this, "MediaScannerService");

        thr.start();

2. onStartCommand

@Override

    public int onStartCommand(Intent intent, int flags, int startId)

    {

      // 注意這個handler ,是在另外一個線程中創(chuàng)建的,,往這個handlersendMessage

     // 都會在那個線程里邊處理

  // 不明白的可以去查看handlerLooper 機制

// 這里就是同步機制,,等待mServiceHandler 在另外那個線程創(chuàng)建完畢

while (mServiceHandler == null) {

            synchronized (this) {

                try {

                     wait(100);

                } catch (InterruptedException e) {

                }

            }

        }

 

        if (intent == null) {

            Log.e(TAG, "Intent is null in onStartCommand: ",

                new NullPointerException());

             return Service.START_NOT_STICKY;

        }

 

        Message msg = mServiceHandler.obtainMessage();

        msg.arg1 = startId;

        msg.obj = intent.getExtras();

//MediaScannerReceiver 發(fā)出的消息傳遞到另外那個線程去處理。

        mServiceHandler.sendMessage(msg);

      ………….

基本上 MSR(MediaScannerReceiver) 發(fā)出的請求都會傳到 onStartCommand 中處理,。如果有多個存儲的話,,也只能一個一個掃描了。

下面看看那個線程的主函數(shù)

3. 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);

// 不明白的去看看Looperhandler 的實現(xiàn)

        Looper.prepare();// 把這個looper 對象設置到線程本地存儲

 

        mServiceLooper = Looper.myLooper();

        mServiceHandler = new ServiceHandler();// 創(chuàng)建handler ,,默認會把這個looper

// 的消息隊列賦值給handler 的消息隊列,,這樣往handler 中發(fā)送消息就是往這個線程的looper 發(fā)

 

        Looper.loop();// 消息循環(huán),內部會處理消息隊列中的消息

// 也就是handleMessage 函數(shù)

}

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

4. handleMessage

private final class ServiceHandler extends Handler

    {

        @Override

        public void handleMessage(Message msg)

        {

            Bundle arguments = (Bundle) msg.obj;

            String filePath = arguments.getString("filepath");

           

            try {

……… 這里不講了

                } 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",

                        };

                    }

                    else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {

                      // 是掃描外部存儲的請求? 獲取外部存儲的路徑 

                       directories = new String[] {

                                 Environment.getExternalStorageDirectory().getPath(),

                                };

                    }

                    if (directories != null) {

// 真正的掃描開始了,,上面只不過是把存儲路徑取出來罷了.

                        scan(directories, volume);

                         …..

// 掃描完了,,就把service 停止了

            stopSelf(msg.arg1);

        }

};

5. scan 函數(shù)

private void scan(String[] directories, String volumeName) {

       mWakeLock.acquire();

// 下面這三句話很深奧

// getContentResolver 獲得一個ContentResover ,然后直接插入

// 根據(jù)AIDL ,,這個ContentResover 的另一端是MediaProvider ,。只要去看看它的

//insert 函數(shù)就可以了

// 反正這里知道獲得了一個掃描URI 即可。

  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]);

// 發(fā)送廣播,,通知掃描開始了

        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));

       

        try {

            if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {

                 openDatabase(volumeName);   

            }

// 創(chuàng)建真正的掃描器

            MediaScanner scanner = createMediaScanner();

// 交給掃描器去掃描文件夾  scanDirectories

            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();

}

說說上面那個深奧的地方,,在 MediaProvider 中重載了 insert 函數(shù), insert 函數(shù)會調用 insertInternal 函數(shù),。

如下:

private Uri insertInternal(Uri uri, ContentValues initialValues) {

        long rowId;

        int match = URI_MATCHER.match(uri);

        // handle MEDIA_SCANNER before calling getDatabaseForUri()

// 剛才那個insert 只會走下面這個分支,,其實就是獲得一個地址….

// 太繞了!?。,。?!

        if (match == MEDIA_SCANNER) {

            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);

            return MediaStore.getMediaScannerUri();

        }

……..

再看看它創(chuàng)建了什么樣的 Scanner ,,這就是 MSS 中的 createMediaScanner

private MediaScanner createMediaScanner() {

// 下面這個MediaScannerframework/base/ 中,,待會再分析

        MediaScanner scanner = new MediaScanner(this);

// 設置當前的區(qū)域,這個和字符編碼有重大關系,。

        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;

}

至此, MSS 的任務完成了,。接下來是 MediaScanner 的工作了,。

6. 總結

MSS 的工作流程如下:

l          1 單獨啟動一個帶消息循環(huán)的工作線程。

l          2 主線程接收系統(tǒng)發(fā)來的任務,,然后發(fā)送給工作線程去處理,。

l          3 工作線程接收任務,創(chuàng)建一個 MediaScanner 去掃描,。

l          4 MSS 順帶廣播一下掃描工作啟動了,,掃描工作完畢了。

 

MediaScanner

MediaScanner 位置在

frameworks\base\media\ 下,,包括 jni java 文件,。

先看看 java 實現(xiàn)。

這個類巨復雜,,而且和 MediaProvider 交互頻繁,。在分析的時候要時刻回到 MediaProvider 去看看。

1.        初始化

public class MediaScanner

{

static {

//libmedia_jni.so 的加載是在MediaScanner 類中完成的

// 這么重要的so 為何放在如此不起眼的地方加載,?,??

        System.loadLibrary("media_jni");

        native_init();

}

public MediaScanner(Context c) {

        native_setup();// 調用jni 層的初始化,,暫時不用看了,,無非就是一些

// 初始化工作,待會在再進去看看

……..

    }

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

2.        scanDirectories

public void scanDirectories(String[] directories, String volumeName) {

        try {

            long start = System.currentTimeMillis();

            initialize(volumeName);// 初始化

            prescan(null);// 掃描前的預處理

            long prescan = System.currentTimeMillis();

 

            for (int i = 0; i < directories.length; i++) {

// 掃描文件夾,,這里有一個很重要的參數(shù) mClient

// processDirectory 是一個native 函數(shù)

                processDirectory(directories[i], MediaFile.sFileExtensions, mClient);

            }

            long scan = System.currentTimeMillis();

            postscan(directories);// 掃描后處理

            long end = System.currentTimeMillis();

           ….. 打印時間,,異常處理 沒了

下面簡單講講 initialize preScan postScan 都干嘛了,。

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 ,,這個函數(shù)很復雜:

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

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

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

 

剛才說到,,具體掃描工作是在 processDirectory 函數(shù)中完成的,。這個是一個 native 函數(shù)。

frameworks\base\media\jni\android_media_MediaScanner.cpp 中,。

 

  MediaScanner JNI 層分析

MediaScanner JNI 層內容比較多,,單獨搞一節(jié)分析吧。

先看看 android_media_MediaScanner 這個文件,。

1. native_init 函數(shù),, jni 對應的函數(shù)如下

static void

android_media_MediaScanner_native_init(JNIEnv *env)

{

     jclass clazz;

clazz = env->FindClass("android/media/MediaScanner");

// 得都JAVA 類中mNativeContext 這個成員id

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

   // 不熟悉JNI 的自己去學習下吧

}

3.        native_setup 函數(shù), jni 對應函數(shù)如下:

android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)

{

// 創(chuàng)建MediaScanner 對象

    MediaScanner *mp = createMediaScanner();

// 太變態(tài)了,,自己不保存這個對象指針.

// 卻把它設置到java 對象的mNativeContext 去保存

    env->SetIntField(thiz, fields.context, (int)mp);

}

// 創(chuàng)建MediaScanner 函數(shù)

static MediaScanner *createMediaScanner() {

#if BUILD_WITH_FULL_STAGEFRIGHT

   ..

// 使用google 自己的

  return new StagefrightMediaScanner;

   #endif

#ifndef NO_OPENCORE

// 使用opencore 提供的

….

    return new PVMediaScanner();

#endif

 

4.        processDirectories 函數(shù),, jni 對應如下:

android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)

{

    MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);

// 每次都要回調到JAVA 中去取這個Scanner !,!

  ………

    const char *pathStr = env->GetStringUTFChars(path, NULL);

    const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);

…….

// 又在C++ 這里搞一個client ,,然后把javaclient 放到C++Client 中去保存

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報,。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多