1. PVMS的processDirectory分析
來看PVMediaScanner(以后簡稱為PVMS,它就是Native層的MS)的processDirectory函數。這個函數是由它的基類MS實現的。注意,源碼中有兩個MediaScanner.cpp,它們的位置分別是:
- framework/base/media/libmedia
- external/opencore/android/
看libmedia下的那個MediaScanner.cpp,其中processDirectory函數的代碼如下所示:
**MediaScanner.cpp**
~~~
status_t MediaScanner::processDirectory(constchar *path,
const char *extensions, MediaScannerClient&client,
ExceptionCheckexceptionCheck, void *exceptionEnv) {
......//做一些準備工作
client.setLocale(locale()); //給Native的MyMSC設置locale信息
//調用doProcessDirectory函數掃描文件夾
status_tresult = doProcessDirectory(pathBuffer,pathRemaining,
extensions, client,exceptionCheck, exceptionEnv);
free(pathBuffer);
returnresult;
}
//下面直接看這個doProcessDirectory函數
status_t MediaScanner::doProcessDirectory(char*path, int pathRemaining,
const char *extensions,MediaScannerClient&client,
ExceptionCheck exceptionCheck,void *exceptionEnv) {
......//忽略.nomedia文件夾
DIR*dir = opendir(path);
......
while((entry = readdir(dir))) {
//枚舉目錄中的文件和子文件夾信息
const char* name = entry->d_name;
......
int type = entry->d_type;
......
if(type == DT_REG || type == DT_DIR) {
int nameLength = strlen(name);
bool isDirectory = (type == DT_DIR);
......
strcpy(fileSpot, name);
if (isDirectory) {
......
//如果是子文件夾,則遞歸調用doProcessDirectory
int err = doProcessDirectory(path, pathRemaining - nameLength - 1,
extensions, client, exceptionCheck, exceptionEnv);
......
} else if (fileMatchesExtension(path, extensions)) {
//如果該文件是MS支持的類型(根據文件的后綴名來判斷)
struct stat statbuf;
stat(path, &statbuf); //取出文件的修改時間和文件的大小
if (statbuf.st_size > 0) {
//如果該文件大小非零,則調用MyMSC的scanFile函數!!?
client.scanFile(path,statbuf.st_mtime, statbuf.st_size);
}
if (exceptionCheck && exceptionCheck(exceptionEnv)) gotofailure;
}
}
}
......
}
~~~
假設正在掃描的媒體文件的類型是屬于MS支持的,那么,上面代碼中最不可思議的是,它竟然調用了MSC的scanFile來處理這個文件,也就是說,MediaScanner調用MediaScannerClient的scanFile函數。這是為什么呢?還是來看看這個MSC的scanFile吧。
2. MyMSC的scanFile分析
(1)JNI層的scanFile
其實,在調用processDirectory時,所傳入的MSC對象的真實類型是MyMediaScannerClient,下面來看它的scanFile函數,代碼如下所示:
**android_media_MediaScanner.cpp**
~~~
virtual bool scanFile(const char* path, longlong lastModified,
long long fileSize)
{
jstring pathStr;
if((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
//mClient是Java層的那個MyMSC對象,這里調用它的scanFile函數
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr,lastModified, fileSize);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
}
~~~
太沒有天理了!Native的MyMSCscanFile主要的工作就是調用Java層MyMSC的scanFile函數。這又是為什么呢?
(2)Java層的scanFile
現在只能來看Java層的這個MyMSC對象了,它的scanFile代碼如下所示:
**MediaScanner.java**
~~~
public void scanFile(String path, longlastModified, long fileSize) {
......
//調用doScanFile函數
doScanFile(path, null, lastModified, fileSize, false);
}
//直接來看doScanFile函數
publicUri doScanFile(String path, String mimeType, long lastModified,
long fileSize, boolean scanAlways) {
/*
上面參數中的scanAlways用于控制是否強制掃描,有時候一些文件在前后兩次掃描過程中沒有
發生變化,這時候MS可以不處理這些文件。如果scanAlways為true,則這些沒有變化
的文件也要掃描。
*/
Uriresult = null;
long t1 = System.currentTimeMillis();
try{
/*
beginFile的主要工作,就是將保存在mFileCache中的對應文件信息的
mSeenInFileSystem設為true。如果這個文件之前沒有在mFileCache中保存,
則會創建一個新項添加到mFileCache中。另外它還會根據傳入的lastModified值
做一些處理,以判斷這個文件是否在前后兩次掃描的這個時間段內被修改,如果有修改,則
需要重新掃描
*/
FileCacheEntryentry = beginFile(path, mimeType,lastModified, fileSize);
if(entry != null && (entry.mLastModifiedChanged || scanAlways)) {
String lowpath = path.toLowerCase();
......
if (!MediaFile.isImageFileType(mFileType)) {
//如果不是圖片,則調用processFile進行掃描,而圖片不需要掃描就可以處理
//注意在調用processFile時把這個Java的MyMSC對象又傳了進去。
processFile(path, mimeType, this);
}
//掃描完后,需要把新的信息插入數據庫,或者要將原有的信息更新,而endFile就是做這項工作的。
result = endFile(entry, ringtones, notifications,alarms, music, podcasts);
}
} ......
return result;
}
~~~
下面看這個processFile,這又是一個native的函數。
上面代碼中的beginFile和endFile函數比較簡單,讀者可以自行研究。
(3)JNI層的processFile分析
MediaScanner的代碼有點繞,是不是?總感覺我們像追兵一樣,追著MS在赤水來回地繞,現在應該是二渡赤水了。來看這個processFile函數,代碼如下所示:
**android_media_MediaScanner.cpp**
~~~
android_media_MediaScanner_processFile(JNIEnv*env, jobject thiz,
jstring path, jstring mimeType, jobject client)
{
//Native的MS還是那個MS,其真實類型是PVMS。
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz,fields.context);
//又構造了一個新的Native的MyMSC,不過它指向的Java層的MyMSC沒有變化。
MyMediaScannerClient myClient(env, client);
//調用PVMS的processFile處理這個文件。
mp->processFile(pathStr,mimeTypeStr, myClient);
}
~~~
看來,現在得去看看PVMS的processFile函數了。
3. PVMS的processFile分析
(1)掃描文件
這是我們第一次進入到PVMS的代碼中進行分析:
**PVMediaScanner.cpp**
~~~
status_t PVMediaScanner::processFile(const char*path, const char* mimeType,
MediaScannerClient& client)
{
status_t result;
InitializeForThread();
//調用Native MyMSC對象的函數做一些處理
client.setLocale(locale());
/*
beginFile由基類MSC實現,這個函數將構造兩個字符串數組,一個叫mNames,另一個叫mValues。
這兩個變量的作用和字符編碼有關,后面會碰到。
*/
client.beginFile();
......
constchar* extension = strrchr(path, '.');
//根據文件后綴名來做不同的掃描處理
if(extension && strcasecmp(extension, ".mp3") == 0) {
result = parseMP3(path, client);//client又傳進去了,我們看看對MP3文件的處理
......
}
/*
endFile會根據client設置的區域信息來對mValues中的字符串做語言轉換,例如一首MP3
中的媒體信息是韓文,而手機設置的語言為簡體中文,endFile會盡量對這些韓文進行轉換。
不過語言轉換向來是個大難題,不能保證所有語言的文字都能相互轉換。轉換后的每一個value都
會調用handleStringTag做后續處理。
*/
client.endFile();
......
}
~~~
下面再到parseMP3這個函數中去看看,它的代碼如下所示:
**PVMediaScanner.cpp**
~~~
static PVMFStatus parseMP3(const char *filename,MediaScannerClient& client)
{
//對MP3文件進行解析,得到諸如duration、流派、標題的TAG(標簽)信息。在Windows平臺上
//可通過千千靜聽軟件查看MP3文件的所有TAG信息
......
//MP3文件已經掃描完了,下面將這些TAG信息添加到MyMSC中,一起看看
if(!client.addStringTag("duration", buffer))
......
}
~~~
(2)添加TAG信息
文件掃描完了,現在需要把文件中的信息通過addStringTag函數告訴給MyMSC。下面來看addStringTag的工作。這個函數由MyMSC的基類MSC處理。
**MediaScannerClient.cpp**
~~~
bool MediaScannerClient::addStringTag(constchar* name, const char* value)
{
if(mLocaleEncoding != kEncodingNone) {
bool nonAscii = false;
const char* chp = value;
char ch;
while ((ch = *chp++)) {
if (ch & 0x80) {
nonAscii = true;
break;
}
}
/*
判斷name和value的編碼是不是ASCII,如果不是的話則保存到
mNames和mValues中,等到endFile函數的時候再集中做字符集轉換。
*/
if(nonAscii) {
mNames->push_back(name);
mValues->push_back(value);
return true;
}
}
//如果字符編碼是ASCII的話,則調用handleStringTag函數,這個函數由子類MyMSC實現。
returnhandleStringTag(name, value);
}
~~~
**android_media_MediaScanner.cpp::MyMediaScannerClient類**
~~~
virtual bool handleStringTag(const char* name,const char* value)
{
......
//調用Java層MyMSC對象的handleStringTag進行處理
mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr,valueStr);
}
~~~
**MediaScanner.java**
~~~
publicvoid handleStringTag(String name, String value) {
//保存這些TAG信息到MyMSC對應的成員變量中去。
if (name.equalsIgnoreCase("title") ||name.startsWith("title;")) {
mTitle = value;
} else if (name.equalsIgnoreCase("artist") ||
name.startsWith("artist;")) {
mArtist = value.trim();
} else if (name.equalsIgnoreCase("albumartist") ||
name.startsWith("albumartist;")) {
mAlbumArtist = value.trim();
}
......
}
~~~
到這里,一個文件的掃描就算做完了。不過,讀者還記得是什么時候把這些信息保存到數據庫的嗎?
是在Java層MyMSC對象的endFile中,這時它會把文件信息組織起來,然后存入媒體數據庫。
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結