03-AndroidManifest.xml文件解析分析
启动 app 时 AndroidManifest.xml 文件是何时解析的?
Questtions
- 找不到解析入口在哪里?解决思路:
- LoadedApk.makeApplication() 方法里有个 clsName, 可以尝试反向查找 clsName 在哪里赋值的【未果】
- 网上搜一下【未果】
- 剩下的一种可能: 当应用下载安装完之后相关的信息就存储起来了,等到用的时候直接从缓存查询。
- 猜测:Launcher 维护了一个数据库,库里放的是桌面上所有展示应用的部分关键信息, 当用户点击桌面图标启动应用时,拿这部分关键信息取数据库或哪里取出应用的其他信息, 然后做后续的启动工作。
- 如果上面的猜测成立,问题就能解决
- 如果是上面的可能,就要看 apk 安装流程的源码了
时序图
PackageManagerService
- 重要字段
1 2 3
// packageName -> Package 映射 final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>(); final PackageHandler mHandler = new PackageHander(); // 子线程
- PMS 的构造器里做了巨多(600多行代码)的工作,其中就包括扫描 /data/app/ 下所有符合‘包名-flag/base.apk’的文件 , 然后由 ParallelPackageParser(内部有一个线程池)调用 PackageParser 解析每一个 apk 并缓存解析出来的 Package 文件等待下次使用。
- PMS 初始化是在 SystemServer.main() 方法里执行的
- 我们平时对 PMS 的调用都是跨进程调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# 可以看到,所有的文件属性都是 d 即目录,符合‘/data/app/包名-flag/’格式 root@NX529J:/data/app # ls -al drwxr-xr-x cn.nubia.accounts-1 drwxr-xr-x cn.nubia.browser-1 drwxr-xr-x cn.nubia.calendar.preset-1 drwxr-xr-x cn.nubia.neopush-1 drwxr-xr-x cn.nubia.neoshare-2 drwxr-xr-x cn.nubia.paycomponent-1 drwxr-xr-x cn.nubia.soundrecorder.preset-1 drwxr-xr-x cn.nubia.thememanager-1 drwxr-xr-x com.dodola.breakpad-1 drwxr-xr-x com.sleticalboy.dailywork-1 drwxr-xr-x com.sleticalboy.ic-2 drwxr-xr-x com.sleticalboy.noc-2 drwxr-xr-x com.sleticalboy.okhttp25.test-1 drwxr-xr-x com.sleticalboy.tinker-1 drwxr-xr-x com.speedsoftware.rootexplorer-1 drwxr-xr-x com.willme.topactivity-1 drwxr-xr-x uk.co.senab.photoview.sample-2 # 下边随便进入一个目录看下,任何一个应用安装完成之后都会有一个 base.apk 文件 # lib 是一个目录,里面放的是打包到 apk 中的 .so 文件,如果一个应用中没有 .so 那么这个目录将会是个空目录 root@NX529J:/data/app/com.speedsoftware.rootexplorer-1 # ls -al -rw-r--r-- 4573584 base.apk drwxr-xr-x lib
PackageParser
- 一些静态常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/** File name in an APK for the Android manifest. */ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; private static final String TAG_MANIFEST = "manifest"; private static final String TAG_APPLICATION = "application"; private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; private static final String TAG_OVERLAY = "overlay"; private static final String TAG_KEY_SETS = "key-sets"; private static final String TAG_PERMISSION_GROUP = "permission-group"; private static final String TAG_PERMISSION = "permission"; private static final String TAG_PERMISSION_TREE = "permission-tree"; private static final String TAG_USES_PERMISSION = "uses-permission"; private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23"; private static final String TAG_USES_CONFIGURATION = "uses-configuration"; private static final String TAG_USES_FEATURE = "uses-feature"; private static final String TAG_FEATURE_GROUP = "feature-group"; private static final String TAG_USES_SDK = "uses-sdk"; private static final String TAG_SUPPORT_SCREENS = "supports-screens"; private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; private static final String TAG_INSTRUMENTATION = "instrumentation"; private static final String TAG_ORIGINAL_PACKAGE = "original-package"; private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; private static final String TAG_SUPPORTS_INPUT = "supports-input"; private static final String TAG_EAT_COMMENT = "eat-comment"; private static final String TAG_PACKAGE = "package"; private static final String TAG_RESTRICT_UPDATE = "restrict-update"; private static final String TAG_USES_SPLIT = "uses-split";
- 几个比较重要的函数
- parseBaseApk()
- parseBaseApkChild()
- parseClusterPackage()
- parseApkLite(File apkFile, int flags) 解析包名和 application 中的一些信息(split name、安装位置)
- parseBaseApkCommon() 解析 instrumentation/application/四大组件
- parseBaseApplication() 解析 application xml tree
- buildClassName() 三种 case
- .HelloWorld 省略包名带‘.’
- HelloWorld 省略包名和‘.’
- com.xxx.HelloWorld 全名
- parseActivity() 解析 Activity 和 BroadcastReceiver
- parseService() 解析 Service
- parseProvider() 解析 ContentProvider
- parseActivityAlias()
- parseMetaData()
- parseAllMetaData()
- parseUsesStaticLibrary()
- parseInstrumentation()
- parsePermission()
- parsePermissionGroup()
- parsePermissionTree()
- parseUsesPermission()
- parseKeySets()
- parseIntent()
- parsePublicKey()
- parseVerifer()
- parsePackage(pdgFile, flag, usesCache)
- 会先从缓存取,如果取出的不为空则直接返回
- 如果不存在,则解析 apk 文件为 Package 对象,然后缓存,最后返回
- parsePackage(pdgFile, flag, /*false*/)
- 调用1:PackageUtils.getPackageInfo() <- PackageInstallerActivity.processPackageUri()
- 调用2:PMS.scanPackageLI() <- PMS构造器
- 调用3:PMS.installPackageLI() <- installPackageTracedLI() <- processPendingInstall()
- 这个方法应该是系统启动的时候初始化 PMS 时调用到的
Settings
Intent
- Intent.parseIntent()
1 2 3 4 5 6 7 8 9 10 11 12
public static @NonNull Intent parseIntent(@NonNull Resources resources, @NonNull XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { Intent intent = new Intent(); // ... String packageName = sa.getString(com.android.internal.R.styleable.Intent_targetPackage); String className = sa.getString(com.android.internal.R.styleable.Intent_targetClass); if (packageName != null && className != null) { intent.setComponent(new ComponentName(packageName, className)); } // ... return intent; }
涉及到的源文件及路径
- PackageManager.java
frameworks/base/core/java/android/content/pm/PackageManager.java
- PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
- PackageParser.java
frameworks/base/core/java/android/content/pm/PackageParser.java
- PackageItemInfo.java
frameworks/base/core/java/android/content/pm/PackageItemInfo.java
- PermissionInfo.java
- PermissionGroupInfo.java
- InstrumentationInfo.java
- ApplicationInfo.java
- ComponentInfo.java
- ActivityInfo.java
- ServiceInfo.java
- ProviderInfo.java
- LauncherApps.java
launcher 相关
- LauncherApplication.java
- 注册了一个 LauncherApps.Callback, 当桌面图标发生变化时通知 LauncherModel 刷新数据
- 注册了一个 ContentObserver, 当收藏发生变化时通知 LauncherModel 刷新数据
- Launcher.java: Launcher 入口页面
- ItemInfo.java
- ShortcutInfo.java
- ApplicationInfo.java
- LauncherModel.java
- LauncherModel extends BroadcastReceiver {}
- LauncherModel.LoaderTask implements Runnable {}
- InstallShortcutReceiver.java 安装新应用时会收到广播向数据库添加新数据
- WidgetPreviewLoader.java 数据库类
- AllAppsList.java
- IconCache.java
- LauncherProvider.java
Package installer 相关
- InstallStart.java 系统安装 apk 入口页面, 会根据 packageUri.getSchema() 判断打开 InstallStaging 还是 PackageInstallerActivity 页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
< activity android:name=".InstallStart" android:exported="true" android:excludeFromRecents="true"> <intent-filter android:priority="1"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:scheme="content" /> <data android:mimeType="application/vnd.android.package-archive" /> </intent-filter> <intent-filter android:priority="1"> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:scheme="package" /> <data android:scheme="content" /> </intent-filter> <intent-filter android:priority="1"> <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> < /activity>
- InstallStaging.java 负责开启 AsyncTask 拷贝一份临时的 apk 文件,然后将文件路径转换成 android.net.Uri 传递给 PackageInstallerActivity
- PackageInstallerActivity.java 根据 packageUri.getSchema() 处理相应的逻辑
- package:
- PackageManager.getPackageInfo()
- mAppSnippet = …
- file:
- PackageParser.Package parsed = PackageUtil.getPackageInfo() 解析加缓存
- mPkgInfo = PackageParser.generatePackageInfo(parsed, …)
- mAppSnippet = …
- 其他:
- package:
DownloadManager 相关
- DownloadManager.java
- 其实是通过 ContentProvider 实现的 crud 操作
- DownloadProvider.java /*extends ContentProvider*/ insert() 时会添加一条任务
- DownloadJobService.java 下载任务管理
- DownloadThread.java 网络获取数据操作
- DownloadReceiver.java 接收下载完成的广播,如果是 apk,打开相应的安装页面