PackageManagerService的启动过程分析

来源:互联网 发布:linux more less 编辑:程序博客网 时间:2024/05/14 08:22

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/42215521


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

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

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

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

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

PackageManager packageManager = getPackageManager();// getPackageName()是你当前类的包名,0代表是获取版本信息PackageInfo packInfo = 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

根据图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


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

 private void scanDirLI(File dir, int flags, int scanMode, long currentTime)  {//拿到指定目录中所有的文件        String[] files = dir.list();        if (files == null) {            Log.d(TAG, "No files in app dir " + dir);            return;        }        int i;        for (i=0; i<files.length; i++) {            File file = new File(dir, files[i]);//过滤掉非apk文件            if (!isPackageFilename(files[i])) {                // Ignore entries which are not apk's                continue;            }            PackageParser.Package pkg = 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) {                // Delete the apk                Slog.w(TAG, "Cleaning up failed install of " + 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.Activity a = 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 = new StringBuilder(256);            } else {                r.append(' ');            }                    r.append(a.info.name);    }}
这里调用了mActivitys.addActivity方法,这里的mActivitys是ActivityIntentResolver类型,我们看看addActivity具体干了什么

public final void addActivity(PackageParser.Activity a, String type) {            final boolean systemApp = isSystemApp(a.info.applicationInfo);//private final HashMap<ComponentName, PackageParser.Activity> mActivities            //    = new HashMap<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);            final int NI = a.intents.size();            for (int j=0; j<NI; j++) {                PackageParser.ActivityIntentInfo intent = a.intents.get(j);                if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {                    intent.setPriority(0);                    Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "                            + a.className + " with priority > 0, forcing to 0");                }                if (DEBUG_SHOW_INFO) {                    Log.v(TAG, "    IntentFilter:");                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");                }                if (!intent.debugCheck()) {                    Log.w(TAG, "==> For Activity " + a.info.name);                }//                addFilter(intent);            }        }

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

3 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 红米手机小米账号密码忘了怎么办 小米5splus没系统卡米怎么办 注册谷歌账号输入手机好怎么办 谷歌注册电话号码用了太多次怎么办 内存卡用了深度清理的软件怎么办 华为畅享5s密码忘了怎么办 小米5s进水了一直开机关机怎么办 手机菜单键功能键返回键失灵怎么办 小米5s更新系统发热严重怎么办 小米手机4G网速不好怎么办力 红米5 plus开不开机怎么办 小米3s手机触屏部分失灵怎么办 魅族手机屏幕锁密码忘了怎么办 手机没设置魅族账号密码忘了怎么办 魅族手机格式化密码忘了怎么办 魅族手机忘记密码了怎么解锁怎么办 手机设置的应用加密忘记密码怎么办 手机上设置应用加密忘记密码怎么办 魅蓝flyme密码忘了怎么办图片 魅族手机经常自动账号锁屏怎么办 魅族锁定后又不知道密码怎么办 魅族手机锁屏锁定了怎么办 魅族手机已锁定怎么办密码忘了 京东抢到了小米8不发货怎么办 第一次网上预约没有就诊卡号怎么办 京东定金交了未发货怎么办 买了没有预售许可证的房子怎么办 买了没有预售证的房子怎么办 苹果手机发烫容易变3g网怎么办 魅族手机有指纹和密码怎么办刷机 魅族手机指纹解锁密码忘了怎么办 魅蓝5s运存占用太多怎么办 魅蓝e2手机照片被删了怎么办 魅蓝e2不小心删除了照片怎么办 魅蓝3s返回键失灵怎么办 糖猫电话手表屏碎了怎么办 魅蓝手机没下安装包强制更新怎么办 老婆赌博输了30多万现在怎么办啊 红米nt2手机通话声音小怎么办? 微信退出后重新登录忘记密码怎么办 微退出后再登录忘记密码了怎么办