在Android中,有几个比较重要的Service。

ActivityManagerService-------主要负责管理所有的Activity的逻辑

WindowManagerService-------主要负责Android中窗口相关的逻辑

PackageManagerService-------主要是用来处理apk的安装,卸载和应用程序信息的获取的

今天我们主要研究一下PackageManagerService,因为这个Service我们在平时开发过程中会经常的遇到,所以了解了它的启动流程,对于我们App的开发会有很大的帮助。说到这里估计有些同学就会说,我们平时开发很少用到PackageManagerService啊?确实我们很少直接使用PackageManagerService这个类,但是我们会经常使用PackageManager这个类吧,比如我们要拿到当前应用版本时,通常会使用如下代码:

PackageManagerpackageManager=getPackageManager();//getPackageName()是你当前类的包名,0代表是获取版本信息PackageInfopackInfo=packageManager.getPackageInfo(getPackageName(),0);

PackageManager是什么?它仅仅是一个接口而已,它的实现类是ApplicationPackageManager,但是当你去研究ApplicationPackageManager的源码的时候,你会发现,它的功能其实都是通过一个mPM的变量完成的,它的类型是IPackageManager类型,如果知道Android中Binder机制(关于Binder机制可以参考我的另外一篇文章:http://blog.csdn.net/yuanzeyao/article/details/12954641)的同学相信已经知道这个mPM是什么东西了,它就是PackageManagerService在客户端的一个代理,通过这个代理客户端可以调用到PackageManagerService中的一些方法,如获取某一个应用的版本号,其实版本号这些信息最终都是保存在PackageManagerService中的,我们只有通过mPM这个代理才能拿到这些信息。下面我给出一个类图,大致的描述一下PackageManager,ApplicationpackageManager和PackageManagerService的关系。

通过上面拿到版本号的例子,我们知道其实手机中所有App的信息都是保存在了PackageManagerService中的,我们今天研究PackageManagerService的目的其实就是熟悉PackageManagerService在启动的过程中到底做了什么,是如何保存这些信息的。PackageManagerService是通过SystemService启动的,主要是通过调用PackageManagerService的main函数开始,在main函数中,其实就是创建了一个PackageManagerService的实例并且在ServiceManager中注册,进入PackageManagerService的构造函数,你会发现这里做了很多重量级的操作,这个也是Android系统启动比较慢的一个主要原因。在开始学习之前我需要提醒大家,这里涉及到的数据结构非常多,我们没有必要知道每一个数据结构有什么,因为那样可能导致我们越陷越深,最终走不出来,我们可以先从宏观分析它的启动过程,然后再去分析它涉及的数据结构,所以为了让大家有一个比较清晰的轮廓,我先给出两个时序图,然后我们可以跟着这些时序图走,我们就永远不会迷路了。之所以给两个时序图,是因为我觉得PackageManagerServiec的启动过程大致可以分为两部分:

建议:看文章时,最好手边有一份Android源码,因为这里涉及到的代码非常多,我不方便将代码都贴出来

1、扫描xml文件并解析成对应的数据结构

2、扫描apk文件并解析成对应的数据结构

好了,我们先看第一阶段的时序图

根据图1-1,发现这里引入了一个数据结构Settings,这个类主要功能就是存储第一阶段扫描xml后解析结果的,具体的结构我们后面会分析的,创建了Setttings实例之后,调用addSharedUserLPw方法,当你查看该函数的实现时,你会发现这里有引入了一个SharedUserSetting类型的结构,这个我们暂且不用去分析,接下来就调用readPermissions去扫描/system/etc/permissions/中的所有的xml文件,解析结果都存储到了上面创建的Settings类型变量了,最后调用readLPw函数读取/data/system/packages.xml文件,第一阶段就这么简单,就是扫描xml并解析。接下来我们详细分析一下Settings这个数据结构吧,我先给出这个类的类图

这里我列出了Settings这个类中比较重要的字段:

mSettingFilename:这个字段就是/data/system/packages.xm文件

mBackupSettingsFilename:这个字段就是/data/system/packages_backup.xml文件,这个文件不一定存在,如果存在,那么说明上次跟新packages.xml文件出错了。

mPackageListFilename:这个字段是/data/system/packages.list

mPackages:这个字段是一个HashMap,key 是包名,值是一个新的数据结构PackageSetting,主要包含了一个app的基本信息,如安装位置,lib位置等等

mSharedUsers:这个字段也是一个HashMap,key是类似"android.ui.system"这样的字段,在Android中每一个应用都有一个uid,两个有相同uid的应用可以运行在同一个进程中的,所以为了让两个应用运行在用一个进程中,往往会在Androidmanifest.xml文件中设置shareUserId这个属性,这个属性就是一个字符串,但是我们知道在Linux系统中一个uid是一个整型,所以为了将字符串和整型对应起来,就有了SharedUserSetting类型,刚才说key是shareUserId这个属性的值,那么值就是SharedUserSetting类型了,ShareUserdSetting中除了name(其实就是key),uid(对应Linux系统的uid),还有一个列表字段,记录了当前系统中有相同shareUserId的应用。

mPermissions:这个字段主要保存的是/system/etc/permissions/platform.xml中的的permission标签的内容,因为Android系统是基于Linux系统的,所以也有用户组的概念,在platform.xml中定义了一些权限,并且指定了哪些用户组具有这些权限,一旦一个应用属于某一个用户组,那么它就拥有了这个用户组的所有权限

mPermissionTrees:这个字段对应packages.xml文件中的permission-trees标签


Settings的结构就简单的介绍到这里吧,下面开始第二个阶段:apk文件的扫描并解析。

在Android系统中,apk主要存在以下三个目录:

/system/app:存放的是系统app

/vender/app:存放时厂商预装app

/data/app:用户自己安装的app

其实还有一个地方会放一个apk文件,只不过这个apk文件很特殊,只有资源文件,没有代码,/system/framework/framework-res.apk


另外对于apk的解析,其实就是解析Androidmanifest.xml文件,例如版本号,包名,Application的各种属性,包含哪些Activity,Service等等,定义了哪些权限以及运行该应用需要哪些权限等等。同样也是先来看一看时序图吧,如下图:

根据图1-2,可以看出扫描并解析apk文件其实调用的就是scanDirLI方法,我们就进入该方法看看吧

privatevoidscanDirLI(Filedir,intflags,intscanMode,longcurrentTime){//拿到指定目录中所有的文件String[]files=dir.list();if(files==null){Log.d(TAG,"Nofilesinappdir"+dir);return;}inti;for(i=0;i<files.length;i++){Filefile=newFile(dir,files[i]);//过滤掉非apk文件if(!isPackageFilename(files[i])){//Ignoreentrieswhicharenotapk'scontinue;}PackageParser.Packagepkg=scanPackageLI(file,flags|PackageParser.PARSE_MUST_BE_APK,scanMode,currentTime);//如果解析失败,并且非系统app,那么删掉if(pkg==null&&(flags&PackageParser.PARSE_IS_SYSTEM)==0&&mLastScanError==PackageManager.INSTALL_FAILED_INVALID_APK){//DeletetheapkSlog.w(TAG,"Cleaningupfailedinstallof"+file);file.delete();}}}

其实这个方法的逻辑非常简单,首先拿到指定目录下面的所有文件,并过滤掉非apk文件,然后调用scanPackageLI方法进行解析,注意这里的scanPackageLI方法第一个参数是File,从图1-2可以看到后面还有一个scanPackageLI方法,它的第一个参数是PackageParser.Package。我们继续看第一个scanPackageLI做了什么吧,从时序图可以看到首先创建了PackageParser.Package对象,并调用了parsePackage方法,这里同样要注意的一点就是这个parsePackage方法第一个参数是File类型,后面同样也有一个重载的方法,第一个参数类型是Resources,我们先看第一个parsePackage,它的逻辑其实也很简单,就是通过AssertManager拿到了apk文件的Resource文件,然后调用重载的parsePackage方法,在重载的parsePackage方法中,通过传入的Resources对象,拿到Androidmanifest.xml文件,并进行解析,并将结果以PackageParser.Package的形式返回给PackageManagerService,PackageManagerService为了方便以后的访问,需要将这个Package对象保存起来,于是调用了第二个scanPackageLI方法,我们现在以Package例的activity数据为例,看看怎么保存Package中的数据

N=pkg.activities.size();r=null;for(i=0;i<N;i++){PackageParser.Activitya=pkg.activities.get(i);a.info.processName=fixProcessName(pkg.applicationInfo.processName,a.info.processName,pkg.applicationInfo.uid);mActivities.addActivity(a,"activity");if((parseFlags&PackageParser.PARSE_CHATTY)!=0){if(r==null){r=newStringBuilder(256);}else{r.append('');}r.append(a.info.name);}}

这里调用了mActivitys.addActivity方法,这里的mActivitys是ActivityIntentResolver类型,我们看看addActivity具体干了什么

publicfinalvoidaddActivity(PackageParser.Activitya,Stringtype){finalbooleansystemApp=isSystemApp(a.info.applicationInfo);//privatefinalHashMap<ComponentName,PackageParser.Activity>mActivities//=newHashMap<ComponentName,PackageParser.Activity>();mActivities.put(a.getComponentName(),a);if(DEBUG_SHOW_INFO)Log.v(TAG,""+type+""+(a.info.nonLocalizedLabel!=null?a.info.nonLocalizedLabel:a.info.name)+":");if(DEBUG_SHOW_INFO)Log.v(TAG,"Class="+a.info.name);finalintNI=a.intents.size();for(intj=0;j<NI;j++){PackageParser.ActivityIntentInfointent=a.intents.get(j);if(!systemApp&&intent.getPriority()>0&&"activity".equals(type)){intent.setPriority(0);Log.w(TAG,"Package"+a.info.applicationInfo.packageName+"hasactivity"+a.className+"withpriority>0,forcingto0");}if(DEBUG_SHOW_INFO){Log.v(TAG,"IntentFilter:");intent.dump(newLogPrinter(Log.VERBOSE,TAG),"");}if(!intent.debugCheck()){Log.w(TAG,"==>ForActivity"+a.info.name);}//addFilter(intent);}}

这里其实是将activity数据保存到了一个HashMap数据中,这里涉及到了一个Intent机制的一些代码,我将会在下面一篇文章中详解介绍怎么通过Intent启动Android中的组件的。这里就不再介绍了。

更多相关文章

  1. android 实现静默安装、卸载
  2. Android常用秘籍总结
  3. Android(安卓)Activity savedInstanceState
  4. Android系统信息与安全机制
  5. android ndk 使用第三方静态库
  6. Android(安卓)模拟器(emulator)无法启动AVD解决方案
  7. Android(安卓)HAL开发
  8. Android应用程序结构介绍
  9. [Android]定制RadioButton样式

随机推荐

  1. Android学习笔记(二)android studio基本控
  2. Android NDK 提供的交叉工具链手动编译源
  3. android 栈方式退出
  4. cocos2d-x android 调试
  5. Android MediaRecorder 小结
  6. Android app的电子书翻页卷曲功能
  7. 使用命令行ls命令的Android文件浏览控件,
  8. 如何启动 instrumentation ?(android )
  9. Android开发问题集结贴(持续更新)
  10. android 如何连接默认浏览器