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

打開APP
userphoto
未登錄

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

開通VIP
Android MediaScanner 詳盡分析

MediaScanner 分析

MediaScannerService

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

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

l          MediaProvider.java

l          MediaScannerReceiver.java

l          MediaScannerService.java

l          MediaThumbRequest.java

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

application android:process = android.process.media

1.1     MediaScannerReceiver

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

MediaScannerReceiver extends BroadcastReceiver

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

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

             // 收到 啟動完畢“廣播后,掃描內(nèi)部存儲

            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 + "/")) {

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

                    scanFile(context, path);

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

        }

…… 下面是它調(diào)用的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));

 

總結(jié):

MediaScannerReceiver 是用來接收任務(wù)的,它收到廣播后,會啟動 MediaService 進(jìn)行掃描工作。

下面看看 MediaScannerService.

1.2     MediaScannerService

MSS 標(biāo)準(zhǔn)的從 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);

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

      // 單獨(dú)搞一個線程去跑掃描工作,防止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 機(jī)制

// 這里就是同步機(jī)制,等待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 的實(shí)現(xiàn)

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

 

        mServiceLooper = Looper.myLooper();

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

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

 

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

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

}

上面 handler 中加入了一個掃描請求(假設(shè)是外部存儲的),所以要分析 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)) {

                     // 是掃描內(nèi)部存儲的請求?  

// 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ù)會調(diào)用 insertInternal 函數(shù)。

如下:

private Uri insertInternal(Uri uri, ContentValues initialValues) {

        long rowId;

        int match = URI_MATCHER.match(uri);

        // handle MEDIA_SCANNER before calling getDatabaseForUri()

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

// 太繞了?。。。。?/span>

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

// 設(shè)置當(dāng)前的區(qū)域,這個和字符編碼有重大關(guān)系。

        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) {

// 給掃描器設(shè)置當(dāng)前國家和語言。

                    scanner.setLocale(language + "_" + country);

                } else {

                    scanner.setLocale(language);

                }

            }   

        }

        return scanner;

}

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

6. 總結(jié)

MSS 的工作流程如下:

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

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

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

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

 

MediaScanner

MediaScanner 位置在

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

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

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

1.        初始化

public class MediaScanner

{

static {

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

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

        System.loadLibrary("media_jni");

        native_init();

}

public MediaScanner(Context c) {

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

// 初始化工作,待會在再進(jìn)去看看

……..

    }

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

2.        scanDirectories

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

        try {

            long start = System.currentTimeMillis();

            initialize(volumeName);// 初始化

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

            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 ,獲得它的一個實(shí)例

        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ù)很復(fù)雜:

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

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

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

 

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

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

 

  MediaScanner JNI 層分析

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

先看看 android_media_MediaScanner 這個文件。

1. native_init 函數(shù), jni 對應(yīng)的函數(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 的自己去學(xué)習(xí)下吧

}

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

android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)

{

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

    MediaScanner *mp = createMediaScanner();

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

// 卻把它設(shè)置到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 對應(yīng)如下:

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

{

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

// 每次都要回調(diào)到JAVA 中去取這個Scanner ?。?/span>

  ………

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

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

…….

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

<>

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Android MediaScanner
android源碼解析------Media多媒體framework層分析
RK-Android-Usb無法讀取以及原理分析
JNI學(xué)習(xí)
[深入理解Android卷一 全文
android MediaScanner
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服