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 本章小结