PKMS构造函数第二阶段的工作就是扫描系统中的APK了。由于需要逐个扫描文件,因此手机上装的程序越多,PKMS的工作量越大,系统启动速度也就越慢。
1. 系统库的dex优化
接着对PKMS构造函数进行分析,代码如下:
**PackageManagerService.java**
~~~
......
mRestoredSettings= mSettings.readLPw();//接第一段的结尾
longstartTime = SystemClock.uptimeMillis();//记录扫描开始的时间
//定义扫描参数
intscanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX;
if(mNoDexOpt) {
scanMode|= SCAN_NO_DEX; //在控制扫描过程中是否对APK文件进行dex优化
}
finalHashSet<String> libFiles = new HashSet<String>();
// mFrameworkDir指向/system/frameworks目录
mFrameworkDir = newFile(Environment.getRootDirectory(),"framework");
// mDalvikCacheDir指向/data/dalvik-cache目录
mDalvikCacheDir= new File(dataDir, "dalvik-cache");
booleandidDexOpt = false;
/*
获取Java启动类库的路径,在init.rc文件中通过BOOTCLASSPATH环境变量输出,该值如下
/system/framework/core.jar:/system/frameworks/core-junit.jar:
/system/frameworks/bouncycastle.jar:/system/frameworks/ext.jar:
/system/frameworks/framework.jar:/system/frameworks/android.policy.jar:
/system/frameworks/services.jar:/system/frameworks/apache-xml.jar:
/system/frameworks/filterfw.jar
该变量指明了framework所有核心库及文件位置
*/
StringbootClassPath = System.getProperty("java.boot.class.path");
if(bootClassPath != null) {
String[] paths = splitString(bootClassPath, ':');
for(int i=0; i<paths.length; i++) {
try{ //判断该jar包是否需要重新做dex优化
if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
/*
将该jar包文件路径保存到libFiles中,然后通过mInstall对象发送
命令给installd,让其对该jar包进行dex优化
*/
libFiles.add(paths[i]);
mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
didDexOpt = true;
}
} ......
}
} ......
/*
读者还记得mSharedLibrarires的作用吗?它保存的是platform.xml中声明的系统库的信息。
这里也要判断系统库是否需要做dex优化。处理方式同上
*/
if (mSharedLibraries.size() > 0) {
......
}
//将framework-res.apk添加到libFiles中。framework-res.apk定义了系统常用的
//资源,还有几个重要的Activity,如长按Power键后弹出的选择框
libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
//列举/system/frameworks目录中的文件
String[] frameworkFiles = mFrameworkDir.list();
if(frameworkFiles != null) {
......//判断该目录下的apk或jar文件是否需要做dex优化。处理方式同上
}
/*
上面代码对系统库(BOOTCLASSPATH指定,或 platform.xml定义,或
/system/frameworks目录下的jar包与apk文件)进行一次仔细检查,该优化的一定要优化。
如果发现期间对任何一个文件进行了优化,则设置didDexOpt为true
*/
if (didDexOpt) {
String[] files = mDalvikCacheDir.list();
if (files != null) {
/*
如果前面对任意一个系统库重新做过dex优化,就需要删除cache文件。原因和
dalvik虚拟机的运行机制有关。本书暂不探讨dex及cache文件的作用。
从删除cache文件这个操作来看,这些cache文件应该使用了dex优化后的系统库
所以当系统库重新做dex优化后,就需要删除旧的cache文件。可简单理解为缓存失效
*/
for (int i=0; i<files.length; i++) {
String fn = files[i];
if(fn.startsWith("data@app@")
||fn.startsWith("data@app-private@")) {
(newFile(mDalvikCacheDir, fn)).delete();
......
}
~~~
2. 扫描系统Package
清空cache文件后,PKMS终于进入重点段了。接下来看PKMS第二阶段工作的核心内容,即扫描Package,相关代码如下:
**PackageManagerService.java**
~~~
//创建文件夹监控对象,监视/system/frameworks目录。利用了Linux平台的inotify机制
mFrameworkInstallObserver = new AppDirObserver(
mFrameworkDir.getPath(),OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
/*
调用scanDirLI函数扫描/system/frameworks目录,这个函数很重要,稍后会再分析。
注意,在第三个参数中设置了SCAN_NO_DEX标志,因为该目录下的package在前面的流程
中已经过判断并根据需要做过dex优化了
*/
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR,scanMode | SCAN_NO_DEX, 0);
//创建文件夹监控对象,监视/system/app目录
mSystemAppDir = new File(Environment.getRootDirectory(),"app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
//扫描/system/app下的package
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
//监视并扫描/vendor/app目录
mVendorAppDir = new File("/vendor/app");
mVendorInstallObserver = new AppDirObserver(
mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
mVendorInstallObserver.startWatching();
//扫描/vendor/app下的package
scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
//和installd交互。以后单独分析installd
mInstaller.moveFiles();
~~~
由以上代码可知,PKMS将扫描以下几个目录。
- /system/frameworks:该目录中的文件都是系统库,例如framework.jar、services.jar、framework-res.apk。不过scanDirLI只扫描APK文件,所以framework-res.apk是该目录中唯一“受宠”的文件。
- /system/app:该目录下全是默认的系统应用,例如Browser.apk、SettingsProvider.apk等。
- /vendor/app:该目录中的文件由厂商提供,即厂商特定的APK文件,不过目前市面上的厂商都把自己的应用放在/system/app目录下。
>[warning] **注意**:本书把这三个目录称为系统Package目录,以区分后面的非系统Package目录。
PKMS调用scanDirLI函数进行扫描,下面来分析此函数。
(1) scanDirLI函数分析
scanDirLI函数的代码如下:
**PackageManagerService.java**
~~~
private void scanDirLI(File dir, int flags, intscanMode, long currentTime) {
String[] files = dir.list();//列举该目录下的文件
......
inti;
for(i=0; i<files.length; i++) {
File file = new File(dir, files[i]);
if (!isPackageFilename(files[i])) {
continue; //根据文件名后缀,判断是否为APK 文件。这里只扫描APK 文件
}
/*
调用scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类
Package,该类的实例代表一个APK文件,所以它就是和APK文件对应的数据结构
*/
PackageParser.Package pkg = scanPackageLI(file,
flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
if (pkg == null && (flags &PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError ==PackageManager.INSTALL_FAILED_INVALID_APK) {
//注意此处flags的作用,只有非系统Package扫描失败,才会删除该文件
file.delete();
}
}
}
~~~
接着来分析scanPackageLI函数。PKMS中有两个同名的scanPackageLI函数,后面会一一见到。先来看第一个也是最先碰到的scanPackageLI函数。
(2) 初会scanPackageLI函数
首次相遇的scanPackageLI函数的代码如下:
**PackageManagerService.java**
~~~
private PackageParser.Package scanPackageLI(FilescanFile, int parseFlags,
int scanMode, long currentTime)
{
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
StringscanPath = scanFile.getPath();
parseFlags |= mDefParseFlags;//默认的扫描标志,正常情况下为0
//创建一个PackageParser对象
PackageParser pp = new PackageParser(scanPath);
pp.setSeparateProcesses(mSeparateProcesses);// mSeparateProcesses为空
pp.setOnlyCoreApps(mOnlyCore);// mOnlyCore为false
/*
调用PackageParser的parsePackage函数解析APK文件。注意,这里把代表屏幕
信息的mMetrics对象也传了进去
*/
finalPackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags);
......
PackageSetting ps = null;
PackageSetting updatedPkg;
......
/*
这里略去一大段代码,主要是关于Package升级方面的工作。读者可能会比较好奇:既然是
升级,一定有新旧之分,如果这里刚解析后得到的Package信息是新,那么旧Package
的信息从何得来?还记得”readLPw的‘佐料’”这一小节提到的package.xml文件吗?此
文件中存储的就是上一次扫描得到的Package信息。对比这两次的信息就知道是否需要做
升级了。这部分代码比较繁琐,但不影响我们正常分析。感兴趣的读者可自行研究
*/
//收集签名信息,这部分内容涉及signature,本书暂不拟讨论(Signature和Android安全机制有关)。
if (!collectCertificatesLI(pp, ps, pkg,scanFile, parseFlags))
returnnull;
//判断是否需要设置PARSE_FORWARD_LOCK标志,这个标志针对资源文件和Class文件
//不在同一个目录的情况。目前只有/vendor/app目录下的扫描会使用该标志。这里不讨论
//这种情况。
if (ps != null &&!ps.codePath.equals(ps.resourcePath))
parseFlags|= PackageParser.PARSE_FORWARD_LOCK;
String codePath = null;
String resPath = null;
if((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
......//这里不考虑PARSE_FORWARD_LOCK的情况。
}else {
resPath = pkg.mScanPath;
}
codePath = pkg.mScanPath;//mScanPath指向该APK文件所在位置
//设置文件路径信息,codePath和resPath都指向APK文件所在位置
setApplicationInfoPaths(pkg, codePath, resPath);
//调用第二个scanPackageLI函数
return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE,
currentTime);
}
~~~
scanPackageLI函数首先调用PackageParser对APK文件进行解析。根据前面的介绍可知,PackageParser完成了从物理文件到对应数据结构的转换。下面来分析这个PackageParser。
(3) PackageParser分析
PackageParser主要负责APK文件的解析,即解析APK文件中的AndroidManifest.xml代码如下:
**PackageParser.java**
~~~
publicPackage parsePackage(File sourceFile, String destCodePath,
DisplayMetrics metrics, int flags) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = sourceFile.getPath();
......//检查是否为APK文件
XmlResourceParser parser = null;
AssetManager assmgr = null;
Resources res = null;
boolean assetError = true;
try{
assmgr = new AssetManager();
int cookie = assmgr.addAssetPath(mArchiveSourcePath);
if (cookie != 0) {
res = new Resources(assmgr, metrics, null);
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,Build.VERSION.RESOURCES_SDK_INT);
/*
获得一个XML资源解析对象,该对象解析的是APK中的AndroidManifest.xml文件。
以后再讨论AssetManager、Resource及相关的知识
*/
parser = assmgr.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME);
assetError = false;
} ......//出错处理
String[] errorText = new String[1];
Package pkg = null;
Exception errorException = null;
try {
//调用另外一个parsePackage函数
pkg = parsePackage(res, parser, flags, errorText);
} ......
......//错误处理
parser.close();
assmgr.close();
//保存文件路径,都指向APK文件所在的路径
pkg.mPath = destCodePath;
pkg.mScanPath = mArchiveSourcePath;
pkg.mSignatures = null;
return pkg;
}
~~~
以上代码中调用了另一个同名的PackageParser函数,此函数内容较长,但功能单一,就是解析AndroidManifest.xml中的各种标签,这里只提取其中相关的代码:
**PackageParser.java**
~~~
private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
AttributeSet attrs = parser;
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs= null;
mParseProviderArgs = null;
//得到Package的名字,其实就是得到AndroidManifest.xml中package属性的值,
//每个APK都必须定义该属性
String pkgName = parsePackageName(parser, attrs, flags, outError);
......
inttype;
......
//以pkgName名字为参数,创建一个Package对象。后面的工作就是解析XML并填充
//该Package信息
finalPackage pkg = new Package(pkgName);
boolean foundApp = false;
......//下面开始解析该文件中的标签,由于这段代码功能简单,所以这里仅列举相关函数
while(如果解析未完成){
......
StringtagName = parser.getName(); //得到标签名
if(tagName.equals("application")){
......//解析application标签
parseApplication(pkg,res, parser, attrs, flags, outError);
} elseif (tagName.equals("permission-group")) {
......//解析permission-group标签
parsePermissionGroup(pkg, res, parser, attrs, outError);
} elseif (tagName.equals("permission")) {
......//解析permission标签
parsePermission(pkg, res, parser, attrs, outError);
} else if(tagName.equals("uses-permission")){
//从XML文件中获取uses-permission标签的属性
sa= res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
//取出属性值,也就是对应的权限使用声明
String name = sa.getNonResourceString(com.android.internal.
R.styleable.AndroidManifestUsesPermission_name);
//添加到Package的requestedPermissions数组
if(name != null && !pkg.requestedPermissions.contains(name)) {
pkg.requestedPermissions.add(name.intern());
}
}elseif (tagName.equals("uses-configuration")){
/*
该标签用于指明本package对硬件的一些设置参数,目前主要针对输入设备(触摸屏、键盘
等)。游戏类的应用可能对此有特殊要求。
*/
ConfigurationInfocPref = new ConfigurationInfo();
......//解析该标签所支持的各种属性
pkg.configPreferences.add(cPref);//保存到Package的configPreferences数组
}
......//对其他标签解析和处理
}
~~~
上面代码展示了AndroidManifest.xml解析的流程,其中比较重要的函数是parserApplication,它用于解析application标签及其子标签(Android的四大组件在application标签中已声明)。
图4-5表示了PackageParser及其内部重要成员的信息。
:-: ![](http://img.blog.csdn.net/20150803110542379?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图4-5 PackageParser大家族
由图4-5可知:
- PackageParser定了相当多的内部类,这些内部类的作用就是保存对应的信息。解析AndroidManifest.xml文件得到的信息由Package保存。从该类的成员变量可看出,和Android四大组件相关的信息分别由activites、receivers、providers、services保存。由于一个APK可声明多个组件,因此activites和receivers等均声明为ArrayList。
- 以PackageParser.Activity为例,它从Component<ActivityIntentInfo>派生。Component是一个模板类,元素类型是ActivityIntentInfo,此类的顶层基类是IntentFilter。PackageParser.Activity内部有一个ActivityInfo类型的成员变量,该变量保存的就是四大组件中Activity的信息。细心的读者可能会有疑问,为什么不直接使用ActivityInfo,而是通过IntentFilter构造出一个使用模板的复杂类型PackageParser.Activity呢?原来,Package除了保存信息外,还需要支持Intent匹配查询。例如,设置Intent的Action为某个特定值,然后查找匹配该Intent的Activity。由于ActivityIntentInfo是从IntentFilter派生的,因此它它能判断自己是否满足该Intent的要求,如果满足,则返回对应的ActivityInfo。在后续章节会详细讨论根据Intent查询特定Activity的工作流程。
- PackageParser定了一个轻量级的数据结构PackageLite,该类仅存储Package的一些简单信息。我们在介绍Package安装的时候,会遇到PackageLite。
>[warning] **注意**:读者需要了解Java泛型类的相关知识。
(4) 与scanPackageLI再相遇
在PackageParser扫描完一个APK后,此时系统已经根据该APK中AndroidManifest.xm,创建了一个完整的Package对象,下一步就是将该Package加入到系统中。此时调用的函数就是另外一个scanPackageLI,其代码如下:
**PackageManagerService.java::scanPackageLI函数**
~~~
private PackageParser.PackagescanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
FilescanFile = new File(pkg.mScanPath);
......
mScanningPath = scanFile;
//设置package对象中applicationInfo的flags标签,用于标示该Package为系统
//Package
if((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
}
//①下面这句if判断极为重要,见下面的解释
if(pkg.packageName.equals("android")) {
synchronized (mPackages) {
if (mAndroidApplication != null) {
......
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
mAndroidApplication = pkg.applicationInfo;
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = mAndroidApplication.processName;
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme =
com.android.internal.R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
//mResoveInfo的activityInfo成员指向mResolveActivity
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
~~~
刚进入scanPackageLI函数,我们就发现了一个极为重要的内容,即单独判断并处理packageName为“android”的Package。和该Package对应的APK是framework-res.apk,有图为证,如图4-6所示为该APK的AndroidManifest.xml中的相关内容。
:-: ![](http://img.blog.csdn.net/20150803110628247?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图4-6 framework-res.apk的AndroidManifest.xml
实际上,framework-res.apk还包含了以下几个常用的Activity。
- ChooserActivity:当多个Activity符合某个Intent的时候,系统会弹出此Activity,由用户选择合适的应用来处理。
- RingtonePickerActivity:铃声选择Activity。
- ShutdownActivity:关机前弹出的选择对话框。
由前述知识可知,该Package和系统息息相关,因此它得到了PKMS的特别青睐,主要体现在以下几点。
- mPlatformPackage成员用于保存该Package信息。
- mAndroidApplication用于保存此Package中的ApplicationInfo。
- mResolveActivity指向用于表示ChooserActivity信息的ActivityInfo。
- mResolveInfo为ResolveInfo类型,它用于存储系统解析Intent(经IntentFilter的过滤)后得到的结果信息,例如满足某个Intent的Activity的信息。由前面的代码可知,mResolveInfo的activityInfo其实指向的就是mResolveActivity。
* * * * *
**注意**:在从PKMS中查询满足某个Intent的Activity时,返回的就是ResolveInfo,再根据ResolveInfo的信息得到具体的Activity。
此处保存这些信息,主要是为了提高运行过程中的效率。Goolge工程师可能觉得ChooserActivity使用的地方比较多,所以这里单独保存了此Activity的信息。
* * * * *
好,继续对scanPackageLI函数的分析。
**PackageManagerService::scanPackageLI函数**
~~~
......//mPackages用于保存系统内的所有Package,以packageName为key
if(mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
return null;
}
File destCodeFile = newFile(pkg.applicationInfo.sourceDir);
FiledestResourceFile = new File(pkg.applicationInfo.publicSourceDir);
SharedUserSettingsuid = null;//代表该Package的SharedUserSetting对象
PackageSetting pkgSetting = null;//代表该Package的PackageSetting对象
synchronized(mPackages) {
......//此段代码大约有300行左右,主要做了以下几方面工作
/*
①如果该Packge声明了” uses-librarie”话,那么系统要判断该library是否
在mSharedLibraries中
②如果package声明了SharedUser,则需要处理SharedUserSettings相关内容,
由Settings的getSharedUserLPw函数处理
③处理pkgSetting,通过调用Settings的getPackageLPw函数完成
④调用verifySignaturesLP函数,检查该Package的signature
*/
}
finallong scanFileTime = scanFile.lastModified();
finalboolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
//确定运行该package的进程的进程名,一般用packageName作为进程名
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
if(mPlatformPackage == pkg) {
dataPath = new File (Environment.getDataDirectory(),"system");
pkg.applicationInfo.dataDir = dataPath.getPath();
}else {
/*
getDataPathForPackage函数返回该package的目录
一般是/data/data/packageName/
*/
dataPath = getDataPathForPackage(pkg.packageName, 0);
if(dataPath.exists()){
......//如果该目录已经存在,则要处理uid的问题
} else {
......//向installd发送install命令,实际上就是在/data/data下
//建立packageName目录。后续将分析installd相关知识
int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
//为系统所有user安装此程序
mUserManager.installPackageForAllUsers(pkgName,
pkg.applicationInfo.uid);
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
} ......
if (pkg.applicationInfo.nativeLibraryDir == null &&
pkg.applicationInfo.dataDir!= null) {
......//为该Package确定native library所在目录
//一般是/data/data/packagename/lib
}
}
//如果该APK包含了native动态库,则需要将它们从APK文件中解压并复制到对应目录中
if(pkg.applicationInfo.nativeLibraryDir != null) {
try {
final File nativeLibraryDir = new
File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
//从2.3开始,系统package的native库统一放在/system/lib下。所以
//系统不会提取系统Package目录下APK包中的native库
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
NativeLibraryHelper.removeNativeBinariesFromDirLI(
nativeLibraryDir)){
} else if (nativeLibraryDir.getParentFile().getCanonicalPath()
.equals(dataPathString)) {
boolean isSymLink;
try {
isSymLink = S_ISLNK(Libcore.os.lstat(
nativeLibraryDir.getPath()).st_mode);
} ......//判断是否为链接,如果是,需要删除该链接
if (isSymLink) {
mInstaller.unlinkNativeLibraryDirectory(dataPathString);
}
//在lib下建立和CPU类型对应的目录,例如ARM平台的是arm/,MIPS平台的是mips/
NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile,
nativeLibraryDir);
} else {
mInstaller.linkNativeLibraryDirectory(dataPathString,
pkg.applicationInfo.nativeLibraryDir);
}
} ......
}
pkg.mScanPath= path;
if((scanMode&SCAN_NO_DEX) == 0) {
......//对该APK做dex优化
performDexOptLI(pkg,forceDex, (scanMode&SCAN_DEFER_DEX);
}
//如果该APK已经存在,要先杀掉运行该APK的进程
if((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
killApplication(pkg.applicationInfo.packageName,
pkg.applicationInfo.uid);
}
......
/*
在此之前,四大组件信息都属于Package的私有财产,现在需要把它们登记注册到PKMS内部的
财产管理对象中。这样,PKMS就可对外提供统一的组件信息,而不必拘泥于具体的Package
*/
synchronized(mPackages) {
if ((scanMode&SCAN_MONITOR) != 0) {
mAppDirs.put(pkg.mPath, pkg);
}
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
mPackages.put(pkg.applicationInfo.packageName,pkg);
//处理该Package中的Provider信息
int N =pkg.providers.size();
int i;
for (i=0;i<N; i++) {
PackageParser.Providerp = pkg.providers.get(i);
p.info.processName=fixProcessName(
pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
//mProvidersByComponent提供基于ComponentName的Provider信息查询
mProvidersByComponent.put(new ComponentName(
p.info.packageName,p.info.name), p);
......
}
//处理该Package中的Service信息
N =pkg.services.size();
r = null;
for (i=0;i<N; i++) {
PackageParser.Service s =pkg.services.get(i);
mServices.addService(s);
}
//处理该Package中的BroadcastReceiver信息
N =pkg.receivers.size();
r = null;
for (i=0;i<N; i++) {
PackageParser.Activity a =pkg.receivers.get(i);
mReceivers.addActivity(a,"receiver");
......
}
//处理该Package中的Activity信息
N = pkg.activities.size();
r =null;
for (i=0; i<N; i++) {
PackageParser.Activity a =pkg.activities.get(i);
mActivities.addActivity(a,"activity");//后续将详细分析该调用
}
//处理该Package中的PermissionGroups信息
N = pkg.permissionGroups.size();
......//permissionGroups处理
N =pkg.permissions.size();
......//permissions处理
N =pkg.instrumentation.size();
......//instrumentation处理
if(pkg.protectedBroadcasts != null) {
N = pkg.protectedBroadcasts.size();
for(i=0; i<N; i++) {
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
......//Package的私有财产终于完成了公有化改造
return pkg;
}
~~~
到此这个长达800行的代码就分析完了,下面总结一下Package扫描的流程。
(5) scanDirLI函数总结
scanDirLI用于对指定目录下的APK文件进行扫描,如图4-7所示为该函数的调用流程。
:-: ![](http://img.blog.csdn.net/20150803110657112?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图4-7 scanDirLI工作流程总结
图4-7比较简单,相关知识无须赘述。读者在自行分析代码时,只要注意区分这两个同名scanPackageLI函数即可。
扫描完APK文件后,Package的私有财产就充公了。PKMS提供了好几个重要数据结构来保存这些财产,这些数据结构的相关信息如图4-8所示。
:-: ![](http://img.blog.csdn.net/20150803110726269?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图4-8 PKMS中重要的数据结构
图4-8借用UML的类图来表示PKMS中重要的数据结构。每个类图的第一行为成员变量名,第二行为数据类型,第三行为注释说明。
3. 扫描非系统Package
非系统Package就是指那些不存储在系统目录下的APK文件,这部分代码如下:
**PackageManagerService.java::构造函数第三部分**
~~~
if (!mOnlyCore) {//mOnlyCore用于控制是否扫描非系统Package
Iterator<PackageSetting> psit =
mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
......//删除系统package中那些不存在的APK
}
mAppInstallDir = new File(dataDir,"app");
......//删除安装不成功的文件及临时文件
if (!mOnlyCore) {
//在普通模式下,还需要扫描/data/app以及/data/app_private目录
mAppInstallObserver = new AppDirObserver(
mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
mAppInstallObserver.startWatching();
scanDirLI(mAppInstallDir, 0, scanMode, 0);
mDrmAppInstallObserver = newAppDirObserver(
mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
mDrmAppInstallObserver.startWatching();
scanDirLI(mDrmAppPrivateInstallDir,
PackageParser.PARSE_FORWARD_LOCK,scanMode,0);
} else {
mAppInstallObserver = null;
mDrmAppInstallObserver = null;
}
~~~
结合前述代码,这里总结几个存放APK文件的目录。
- 系统Package目录包括:/system/frameworks、/system/app和/vendor/app。
- 非系统Package目录包括:/data/app、/data/app-private。
4. 第二阶段工作总结
PKMS构造函数第二阶段的工作任务非常繁重,要创建比较多的对象,所以它是一个耗时耗内存的操作。在工作中,我们一直想优化该流程以加快启动速度,例如延时扫描不重要的APK,或者保存Package信息到文件中,然后在启动时从文件中恢复这些信息以减少APK文件读取并解析XML的工作量。但是一直没有一个比较完满的解决方案,原因有很多。比如APK之间有着比较微妙的依赖关系,因此到底延时扫描哪些APK,尚不能确定。另外,笔者感到比较疑惑的一个问题是:对于多核CPU架构,PKMS可以启动多个线程以扫描不同的目录,但是目前代码中还没有寻找到相关的蛛丝马迹。难道此处真的就不能优化了吗?读者如果有更好的解决方案,不妨和大家分享一下。
- 前言
- 第1章 搭建Android源码工作环境
- 1.1 Android系统架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.2.3 利用Eclipse调试system_process
- 1.3 本章小结
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java层中的Binder架构分析
- 2.2.1 Binder架构总览
- 2.2.2 初始化Java层Binder框架
- 2.2.3 addService实例分析
- 2.2.4 Java层Binder架构总结
- 2.3 心系两界的MessageQueue
- 2.3.1 MessageQueue的创建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函数分析
- 2.3.4 MessageQueue总结
- 2.4 本章小结
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函数分析
- 3.2.2 Service群英会
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS构造函数分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings数据库
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService构造函数分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 复制数据到剪贴板
- 3.7.2 从剪切板粘贴数据
- 3.7.3 CBS中的权限管理
- 3.8 本章小结
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初识PackageManagerService
- 4.3 PKMS的main函数分析
- 4.3.1 构造函数分析之前期准备工作
- 4.3.2 构造函数分析之扫描Package
- 4.3.3 构造函数分析之扫尾工作
- 4.3.4 PKMS构造函数总结
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函数分析
- 4.4.4 APK 安装流程总结
- 4.4.5 Verification介绍
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介绍
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查询分析
- 4.5.4 queryIntentActivities总结
- 4.6 installd及UserManager介绍
- 4.6.1 installd介绍
- 4.6.2 UserManager介绍
- 4.7 本章学习指导
- 4.8 本章小结
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初识PowerManagerService
- 5.2.1 PMS构造函数分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete处理
- 5.2.5 初识PowerManagerService总结
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客户端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power类及LightService类介绍
- 5.3.4 WakeLock总结
- 5.4 userActivity及Power按键处理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按键处理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService总结
- 5.6 本章学习指导
- 5.7 本章小结
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初识ActivityManagerService
- 6.2.1 ActivityManagerService的main函数分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函数分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初识ActivityManagerService总结
- 6.3 startActivity分析
- 6.3.1 从am说起
- 6.3.2 AMS的startActivityAndWait函数分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息处理函数
- 6.4.4 应用进程处理广播分析
- 6.4.5 广播处理总结
- 6.5 startService之按图索骥
- 6.5.1 Service知识介绍
- 6.5.2 startService流程图
- 6.6 AMS中的进程管理
- 6.6.1 Linux进程管理介绍
- 6.6.2 关于Android中的进程管理的介绍
- 6.6.3 AMS进程管理函数分析
- 6.6.4 AMS进程管理总结
- 6.7 App的 Crash处理
- 6.7.1 应用进程的Crash处理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash处理总结
- 6.8 本章学习指导
- 6.9 本章小结
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的启动及创建
- 7.2.1 Context的getContentResolver函数分析
- 7.2.2 MediaStore.Image.Media的query函数分析
- 7.2.3 MediaProvider的启动及创建总结
- 7.3 SQLite创建数据库分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider创建数据库分析
- 7.3.3 SQLiteDatabase创建数据库的分析总结
- 7.4 Cursor 的query函数的实现分析
- 7.4.1 提取query关键点
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query关键点分析
- 7.4.4 Cursor query实现分析总结
- 7.5 Cursor close函数实现分析
- 7.5.1 客户端close的分析
- 7.5.2 服务端close的分析
- 7.5.3 finalize函数分析
- 7.5.4 Cursor close函数总结
- 7.6 ContentResolver openAssetFileDescriptor函数分析
- 7.6.1 openAssetFileDescriptor之客户端调用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函数分析
- 7.6.3 跨进程传递文件描述符的探讨
- 7.6.4 openAssetFileDescriptor函数分析总结
- 7.7 本章学习指导
- 7.8 本章小结
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 数据更新通知机制分析
- 8.2.1 初识ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 数据更新通知机制总结和深入探讨
- 8.3 AccountManagerService分析
- 8.3.1 初识AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析总结
- 8.4 数据同步管理SyncManager分析
- 8.4.1 初识SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 数据同步管理SyncManager分析总结
- 8.5 本章学习指导
- 8.6 本章小结