查看原文
其他

“终于懂了”系列:APK安装过程 完全解析!

JsonChao 2022-07-18

Editor's Note

非常细致的 APK 安装解析文章,值得一看~

The following article is from 胡飞洋 Author 胡飞洋


一、背景

最近在了解插件化技术:把未安装的插件apk 集成到 宿主App中,以取得减少宿主APK包体积等优点。也就是说,一个完整的APK 虽然不经过安装过程,但使用了插件化技术后却可以在宿主中使用其功能。

那么对应的,一个APK的完整安装过程是怎样的呢?用户通常能体验到的安装过程有两种:

  1. 在手机厂商的应用市场中 选择目标App自动安装;

image.png

先下载APK,然后是安装过程,过程中没有跳转到其他页面,可以说是一键安装

  1. 浏览器、QQ、抖音等 应用里面下载的APK包,点击后用户确认安装:

    image.png

这种通常是用户下载一个APK文件,然后手动点击,跳转到安装页面,提示这个APK的下载信息等,然后是安装包详情页面,展示 版本号、所需权限 等一些信息,最后是安装中的页面。我使用的是小米手机,对应的Activity信息如下,这其中PermissionInfoActivity是点击“应用权限”的权限详情页面。

1com.miui.packageinstaller/com.miui.packageInstaller.NewPackageInstallerActivity: +220ms
2com.miui.packageinstaller/com.miui.packageInstaller.NormalInstallReadyActivity: +97ms
3com.miui.packageinstaller/com.android.packageinstaller.miui.PermissionInfoActivity: +97ms
4com.miui.packageinstaller/com.miui.packageInstaller.InstallProgressActivity: +99ms

发现整个安装过程是执行在小米系统miui的"com.miui.packageinstaller"中,那么可想而知在原生Android系统也有对应的Activity。

APK包体越大,安装时间就越长,因为需要解析和校验的越多。无论哪种方式,安装完成后都会通知到 桌面应用Launcher,Launcher在桌面上添加一个App图标。这就是用户感受到的安装过程。

好了,APK安装过程 就讲完了!

—— 并没有😄,APK安装具体过程是怎样的?我们是Android应用开发者,read the source code!

阅读源码是一件痛苦的事情,一般需要结合其他资料一起理解。并且,重点掌握整体流程即可。

二、认识 PMS

我们先来了解一个类:PackageManagerService(PMS),通过名字 你可能会想起 和四大组件紧密相关的 ActivityManagerService(AMS),它俩都是运行在SystemServer进程,App端和AMS、PMS进行交互都是通过Binder跨进程完成。AMS负责Activity等四大组件的管理,而PMS则负责 包package 的管理,APK的全称是Android Package,即PMS的作用就是管理APK。

除了这里的PMS,在之前相关文章中还介绍了 ActivityManagerService(AMS)、WindowManagerService(WMS)。

2.1 PMS的使用

在平时开发中,我们会需要获取当前已安装的包的相关信息,例如已安装应用列表等,就可以在Activity中这么写:

1PackageManager packageManager = getPackageManager();
2// 获取所有已安装程序的包信息
3List<PackageInfo> packages = packageManager.getInstalledPackages(0);

getPackageManager() 实际是Context的方法,具体实现是 ContextImpl 中:

1@Override
2public PackageManager getPackageManager() {
3    if (mPackageManager != null) {
4        return mPackageManager;
5    }
6
7    final IPackageManager pm = ActivityThread.getPackageManager();
8    final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
9    if (pm != null && permissionManager != null) {
10        // Doesn't matter if we make more than one instance.
11        return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
12    }
13
14    return null;
15}

注意获取到的 PackageManager 实际是 ApplicationPackageManager,构造方法中传入了 通过 ActivityThread 获取的 IPackageManager、IPermissionManager。并且查看ApplicationPackageManager,会发现 基本所有方法都是对 传入的 pm的封装:

1public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
2    try {
3        //调用PM的对应方法
4        ParceledListSlice<PackageInfo> parceledList =
5                mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
6        if (parceledList == null) {
7            return Collections.emptyList();
8        }
9        return parceledList.getList();
10    } catch (RemoteException e) {
11        throw e.rethrowFromSystemServer();
12    }
13}

我们再看看 ActivityThread.getPackageManager() 如何获取 IPackageManager :

1public static IPackageManager getPackageManager() {
2    if (sPackageManager != null) {
3        return sPackageManager;
4    }
5    final IBinder b = ServiceManager.getService("package");
6    sPackageManager = IPackageManager.Stub.asInterface(b);
7    return sPackageManager;
8}

到这里如果你对AMS的跨进程(IPC)比较熟,就会知道 这里同样是 为了IPC,即获取 SystemServer中 PMS 在App端的代理 IPackageManager。也就说,上面在Activity中通过getPackageManager()获取已安装应用列表,最后是IPC到了系统进程,在 PMS 中执行。类图如下:

image.png

2.2 PMS的初始化

那么 PMS 什么时候完成初始化的呢?因为 所有App运行都需要 这些系统服务,所以必然是 系统开机的时候 完成 PMS、AMS这些的初始化:

1//SystemServer.java
2/**
3 * The main entry point from zygote.
4 */

5public static void main(String[] args) {
6    new SystemServer().run();
7}
8
9private void run() {
10        ...
11        // Here we go!
12        Slog.i(TAG, "Entered the Android system server!");
13        ...
14        // Prepare the main looper thread (this thread).
15        Looper.prepareMainLooper();
16        ...
17    // Start services. 
18        startBootstrapServices(t); //系统开启中 具有复杂相互依赖性的 关键服务
19        startCoreServices(t); //没有复杂依赖的核心服务
20        startOtherServices(t); //其他服务
21        ...
22    // Loop forever.
23    Looper.loop();
24    throw new RuntimeException("Main thread loop unexpectedly exited");
25}

在开机系统启动时,zygote 进程会fork一个 SystemServer 进程然后执行上面main方法,这里进行大量的初始化,其中就包括 启动各种服务。这里我们看startBootstrapServices():

1private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
2    ...
3    // Installer 
4    Installer installer = mSystemServiceManager.startService(Installer.class);
5    ...
6    //AMS
7    mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
8    mActivityManagerService.setInstaller(installer);
9    ...
10    //PMS
11    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
12    ...
13}

发现这里开启了 AMS、PMS,并且都使用了Installer。Installer 看名字像是安装器,后面再做介绍。PMS的main方法内用构造方法创建了PMS实例,而它的构造方法内容特别多,我们关注其中一句调用scanDirTracedLI():

1/** data/app,已安装app的目录 */
2private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app");
3
4public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest){
5    ...
6    //读取packages.xml中 上次的包信息,解析后将数据存放到mSettings中
7    mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false));
8    ...
9    //扫描 data/app
10    scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,packageParser, executorService);
11    ...
12    //把新的 mSettings 内容更新到 packages.xml
13    mSettings.writeLPr();
14    ...
15}
  • packages.xml:记录系统中 所有安装的应用信息,包括基本信息、签名和权限。

  • mSettings:用来保存和PMS相关的一些设置,它保存的内容在解析应用时会用到。

先读取packages.xml文件,解析后将数据存放到mSettings中,代表上次启动时的应用包信息。然后扫描所有APK目录并解析APK,最后更新packages.xml文件。而 packages.xml文件 是在 Settings 构造方法中创建。

data/app 是用户已安装App所在的目录,另外还有system/app存放 系统App。PMS构造方法中会对 这两个目录在内的多个目录进行扫描,我们这里可以猜想到这是开机时对所有已安装App的初始化。scanDirTracedLI()又走到scanDirLI(),那么就来详细看看怎么扫描的:

1private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
2        PackageParser2 packageParser, ExecutorService executorService) 
{
3    ...
4    ParallelPackageParser parallelPackageParser = new ParallelPackageParser(packageParser, executorService);
5    //并行解析所有package
6    for (File file : files) {
7        parallelPackageParser.submit(file, parseFlags);
8    }
9    ...
10    //处理解析结果
11    for (; fileCount > 0; fileCount--) {
12        ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
13        addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime, null);
14    }
15    ...
16}

使用 ParallelPackageParser 对目录内的所有apk进行解析 然后使用 addForInitLI()处理解析结果parseResult。ParallelPackageParser 实际是对 PackageParser2 的使用上的封装,APK解析操作会走到
PackageParser2 的 parsePackage():

1//PackageParser2.java
2public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) {
3    ...
4    ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
5    ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
6    ...
7    return parsed;
8}

这里parsingUtils 是 ParsingPackageUtils 的实例,继续看parsingUtils.parsePackage():

1//ParsingPackageUtils.java
2public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,int flags){
3    //两个分支 都会走到parseBaseApk()
4    if (packageFile.isDirectory()) { //是目录
5        return parseClusterPackage(input, packageFile, flags);
6    } else {
7        return parseMonolithicPackage(input, packageFile, flags);
8    }
9}

两个分支 都会走到parseBaseApk():

1private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,String codePath, AssetManager assets, int flags) {
2    ...
3    XmlResourceParser parser = assets.openXmlResourceParser(cookie,PackageParser.ANDROID_MANIFEST_FILENAME)) {
4    final Resources res = new Resources(assets, mDisplayMetrics, null);
5    ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,parser, flags);
6    ...
7}

这里创建了 XmlResourceParser 实例,适用于解析APK的AndroidManifest.xml 文件,然后调用了重载的parseBaseApk():

1private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
2        String codePath, Resources res, XmlResourceParser parser, int flags)
{
3    ...
4    //创建ParsingPackage实例 用于承接解析结果
5    final ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath, manifestArray, isCoreApp);
6    //解析apk 结果存入 pkg,包括 所有的activity、sevice等信息
7    final ParseResult<ParsingPackage> result = parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
8    return input.success(pkg);
9    ...
10}

parseBaseApkTags()内部使用 XmlResourceParser 查找 AndroidManifest.xml中的 Application 标签,然后调用 parseBaseApplication() 来解析 APK中的基础 Application 节点树:

1private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,  ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags){
2   ...
3   //解析Application标签的属性:theme、AllowBackup 这些
4   parseBaseAppBasicFlags(pkg, sa);
5   ...
6    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
7       ...
8        final ParseResult result;
9        String tagName = parser.getName();
10        boolean isActivity = false;
11        switch (tagName) {
12            case "activity":
13                isActivity = true;
14            case "receiver":
15                ParseResult<ParsedActivity> activityResult = ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg, res, parser, flags, PackageParser.sUseRoundIcon, input); 
16                if (activityResult.isSuccess()) {
17                    ParsedActivity activity = activityResult.getResult();
18                    if (isActivity) {
19                        hasActivityOrder |= (activity.getOrder() != 0);
20                        pkg.addActivity(activity); //添加Activity
21                    } else {
22                        hasReceiverOrder |= (activity.getOrder() != 0);
23                        pkg.addReceiver(activity);//添加Receiver
24                    }
25                } 
26                result = activityResult;
27                break;
28            case "service":
29                ParseResult<ParsedService> serviceResult =
30                        ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser, flags, PackageParser.sUseRoundIcon, input);
31                if (serviceResult.isSuccess()) {
32                    ParsedService service = serviceResult.getResult();
33                    hasServiceOrder |= (service.getOrder() != 0);
34                    pkg.addService(service); //添加Service
35                } 
36                result = serviceResult;
37                break;
38            case "provider":
39                ParseResult<ParsedProvider> providerResult = ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, flags, PackageParser.sUseRoundIcon, input);
40                if (providerResult.isSuccess()) {
41                    pkg.addProvider(providerResult.getResult());//添加Provider
42                } 
43                result = providerResult;
44                break;
45            ...
46        }...
47    }
48...
49    return input.success(pkg);
50}

这里就是解析Application的子节点——四大组件,并把信息存入ParsingPackage实例pkg中,最后转成 ParallelPackageParser.ParseResult。那么再回到 PMS构造方法,解析后调用了 addForInitLI(),这个方法逻辑复杂,有很多对系统应用的校验(包名修改、版本升级、签名手收集和校验),但我们只关注下面内容:

1//PMS.java
2private AndroidPackage addForInitLI(ParsedPackage parsedPackage,@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,@Nullable UserHandle user){
3    ...
4    //继续扫描
5    final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
6    if (scanResult.success) {
7        synchronized (mLock) {
8            boolean appIdCreated = false;
9            try {
10                final String pkgName = scanResult.pkgSetting.name;
11                final Map<String, ReconciledPackage> reconcileResult =  reconcilePackagesLocked(new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap(pkgName, getSettingsVersionForPackage(parsedPackage)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))),mSettings.mKeySetManagerService);
12                appIdCreated = optimisticallyRegisterAppId(scanResult);
13                //提交包扫描结果、更新系统状态
14                commitReconciledScanResultLocked(reconcileResult.get(pkgName), mUserManager.getUserIds());
15            } 
16           ...
17        }
18    }...
19    return scanResult.pkgSetting.pkg;
20}

调用scanPackageNewLI()继续扫描,主要是更新 packageSetting(PackageSetting主要包含了一个APP的基本信息,如安装位置,lib位置等信息)。然后调用reconcilePackagesLocked()进行一致化处理,调用commitReconciledScanResultLocked() 提交包扫描结果、更新系统状态,内部又调用了commitPackageSettings():

1//向系统添加扫描完成的包。那么这个包对于系统成为可用状态。
2private void commitPackageSettings(AndroidPackage pkg,@Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,inal @ScanFlags int scanFlags, booleanchatty,ReconciledPackage reconciledPkg) {
3    final String pkgName = pkg.getPackageName();
4    ...
5    synchronized (mLock) {
6        //添加到mSettings
7        mSettings.insertPackageSettingLPw(pkgSetting, pkg);
8        //添加到mPackages
9        mPackages.put(pkg.getPackageName(), pkg);
10        //添加组件到mComponentResolver:所有的四大组件信息添加到内部数据结构中
11        mComponentResolver.addAllComponents(pkg, chatty);
12        ...
13        //记录权限到PermissionManagerService中
14        mPermissionManager.addAllPermissions(pkg, chatty);
15        ...
16        //添加mInstrumentation
17        for (i = 0; i < collectionSize; i++) {
18            ParsedInstrumentation a = pkg.getInstrumentations().get(i);
19            a.setPackageName(pkg.getPackageName());
20            mInstrumentation.put(a.getComponentName(), a);...
21        }
22        ...
23}

也就是包信息记录在了PMS的属性中。也就是说,系统启动后,包信息记录在了PMS管理的内存中。

小结一下,系统启动后创建并启动了PMS,并且PMS完成了对所有存在APK的目录进行了扫描,解析所有APK的AndroidManifest.xml,然后进一步扫描APK 最后提交包扫描结果到 PMS 的属性中。

以上就是对 开机启动时 APK安装流程 的简单理解。那么,下面就来看看 APK安装普通流程。

三、APK的安装过程

上面提到,普通的应用安装过程 是执行在小米系统miui的"com.miui.packageinstaller"中,安装过程涉及几个Activity,我们来看看 如何使用代码 安装一个APK文件,以及原生Android系统是哪些Activity。

3.1 发起

安装APK的代码如下:

1File file = new File(getExternalFilesDir(null), "xxx.apk");
2
3Intent intent = new Intent(Intent.ACTION_VIEW);
4intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5Uri apkUri =FileProvider.getUriForFile(this"com.xxx.xxx.fileprovider", file);
6intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
7intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
8startActivity(intent);

Action是Intent.ACTION_VIEW,type是"application/vnd.android.package-archive",这次使用 api 30的 原生模拟器,Activity信息如下:

1com.google.android.packageinstaller/com.android.packageinstaller.InstallStaging: +349ms
2com.google.android.packageinstaller/com.android.packageinstaller.PackageInstallerActivity: +1s516ms
3com.android.settings/.Settings$ManageAppExternalSourcesActivity: +385ms
4com.google.android.packageinstaller/com.android.packageinstaller.InstallInstalling: +296ms
5com.google.android.packageinstaller/com.android.packageinstaller.InstallSuccess: +375ms

过程截图如下:

image.png
  1. InstallStaging,对应图1:从content URI 读取 apk 文件

  2. PackageInstallerActivity,对应 图2、图4,解析apk 提示未知来源安装的设置,以及展示APK icon和名称 并 提示安装确认。

  3. ManageAppExternalSourcesActivity,图3,设置页面-允许未知来源安装

  4. InstallInstalling,图5,安装中

  5. InstallSuccess,图6,安装完成。

我们从InstallStaging开始看起:

1public class InstallStaging extends AlertActivity {
2    private @Nullable StagingAsyncTask mStagingTask;
3    private @Nullable File mStagedFile;
4    protected void onCreate(@Nullable Bundle savedInstanceState) {
5        super.onCreate(savedInstanceState); ...
6        setupAlert(); //展示ui ...
7    }
8    protected void onResume() {...
9            mStagingTask = new StagingAsyncTask(); 
10            mStagingTask.execute(getIntent().getData()); //开启AsyncTask 读取 apk文件
11        }
12    }
13    protected void onSaveInstanceState(Bundle outState) {
14        outState.putString(STAGED_FILE, mStagedFile.getPath());
15    }
16    ...
17    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
18        protected Boolean doInBackground(Uri... params) {...
19            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
20                ...
21                try (OutputStream out = new FileOutputStream(mStagedFile)) {
22                    byte[] buffer = new byte[1024 * 1024];
23                    int bytesRead;
24                    while ((bytesRead = in.read(buffer)) >= 0) {...
25                        out.write(buffer, 0, bytesRead);...
26        }
27        protected void onPostExecute(Boolean success) {
28            if (success) {
29                // Now start the installation again from a file
30                Intent installIntent = new Intent(getIntent());
31                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
32                installIntent.setData(Uri.fromFile(mStagedFile));
33                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
34                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
35                }
36                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
37                startActivity(installIntent); //读取完成后,启动 DeleteStagedFileOnResult 
38                InstallStaging.this.finish();
39            } ...
40        }
41    }
42}

只做了一件事,开启AsyncTask 读取 apk文件,然后 启动了activity DeleteStagedFileOnResult,这是个跳板页面,里面直接透传 intent 启动了 PackageInstallerActivity

1public class PackageInstallerActivity extends AlertActivity {
2    ...
3    private int mSessionId = -1;
4    PackageManager mPm;
5    IPackageManager mIpm;...
6    PackageInstaller mInstaller;
7    PackageInfo mPkgInfo; //解析apk得到的PackageInfo
8    ...
9    protected void onCreate(Bundle icicle) {
10    ...
11        mPm = getPackageManager(); //ApplicationPackageManager
12        mInstaller = mPm.getPackageInstaller(); // mInstaller,安装器
13
14        final Intent intent = getIntent();
15        final Uri packageUri;
16        ...
17        boolean wasSetUp = processPackageUri(packageUri); //解析APK
18        if (!wasSetUp) {
19            return;
20        }
21
22        // 初始化ui
23        bindUi();
24        //检查"未知来源安装",展示 安装确认 提示
25        checkIfAllowedAndInitiateInstall();
26    }
27...
28private boolean processPackageUri(final Uri packageUri) {
29    mPackageURI = packageUri;
30
31    final String scheme = packageUri.getScheme();
32
33    switch (scheme) {
34    ...
35        case ContentResolver.SCHEME_FILE: {
36            File sourceFile = new File(packageUri.getPath());
37            //解析APK,注意参数是 GET_PERMISSIONS
38            mPkgInfo = PackageUtil.getPackageInfo(this, sourceFile,PackageManager.GET_PERMISSIONS); 
39            //获取apk摘要:图标、名字
40            mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
41            ...
42        } break;
43    ...
44    }
45    return true;
46}
47
48
49//点击”安装“,跳转 InstallInstalling - 开始安装
50private void startInstall() {
51    // Start subactivity to actually install the application
52    Intent newIntent = new Intent();
53    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
54    newIntent.setData(mPackageURI);
55    newIntent.setClass(this, InstallInstalling.class);
56    String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME);
57    ...
58    if (installerPackageName != null) {
59        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
60    }
61    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
62    startActivity(newIntent);
63    finish();
64}

整体逻辑比较清晰:

  1. 在 PackageInstallerActivity 的 onCreate() 初始化mPm、mInstaller,

  2. 使用 PackageUtil.getPackageInfo() 解析APK权限信息(可用于安装前展示给用户),注意参数flags 是 PackageManager.GET_PERMISSIONS;使用PackageUtil.getAppSnippet()获取apk摘要:图标、名字。

  3. 然后使用 checkIfAllowedAndInitiateInstall() 是检查APK来源,展示"未知来源APK安装"的对话框,当点击"settings"按钮后跳转到设置页.

  4. 打开允许未知来源安装 后回到 PackageInstallerActivity,在 onActivityResult()中,展示"确认安装"提示,点击”安装“,跳转 InstallInstalling - 开始安装.

我们先看包信息 PackageInfo 的解析过程——PackageUtil.getPackageInfo():

1//PackageUtil.java
2public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
3    ...
4    return context.getPackageManager().getPackageArchiveInfo(sourceFile.getAbsolutePath(),flags);
5}

继续看PackageManager的getPackageArchiveInfo():

1//PackageManager.java
2public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,@PackageInfoFlags int flags) {
3    ...
4    ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
5    ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
6            new File(archiveFilePath), 0, collectCertificates);
7    ...
8    return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 00null,
9            new PackageUserState(), UserHandle.getCallingUserId());
10}

调用了 ParsingPackageUtils 的静态方法parseDefault(),里面创建了 ParsingPackageUtils 实例,继续调用它的 parsePackage()。上面介绍的 PMS 构造方法中 解析APK 也是 ParsingPackageUtils的 parsePackage(),只是这里的 参数flags 是 PackageManager.GET_PERMISSIONS,只解析了权限信息。这里不再跟踪进去了。

最终解析 获取到了apk所有信息的 PackageInfo,然后 点击”安装“ 跳转到了 InstallInstalling。

3.2 Session:发送包到PMS

直接看 InstallInstalling:

1//两个逻辑:发送包到PMS、处理从PMS得到的安装结果
2public class InstallInstalling extends AlertActivity {
3    ...
4    //发送包到installer
5    private InstallingAsyncTask mInstallingTask;
6    //安装mSessionId
7    private int mSessionId;
8    ...
9    protected void onCreate(@Nullable Bundle savedInstanceState) {
10        ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
11        mPackageURI = getIntent().getData();
12        ...
13        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
14         ...
15        //安装结果监听。在收到安装结果的广播后 会调用此 Observer
16        mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);
17        ...
18        //IPC:PackageInstaller 内部会通过 IPackageInstaller 走到PackageInstallerService 的 createSession 方法来创建
19        mSessionId = getPackageManager().getPackageInstaller().createSession(params);
20        ...
21        }
22    }
23    ...
24    protected void onResume() {
25        super.onResume();
26        if (mInstallingTask == null) {
27            PackageInstaller installer = getPackageManager().getPackageInstaller();
28            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
29            if (sessionInfo != null && !sessionInfo.isActive()) {
30            //开启任务发送包信息到 session中,实际是
31                mInstallingTask = new InstallingAsyncTask();
32                mInstallingTask.execute();
33            }...
34        }
35    }
36    ...
37    private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
38        if (statusCode == PackageInstaller.STATUS_SUCCESS) {
39            launchSuccess(); //打开安装成功页面 InstallSuccess
40        } else {
41            launchFailure(legacyStatus, statusMessage); //安装失败页面 InstallFailed
42        }
43    }
44    ...
45    //发送包到 installer
46    private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {
47        @Override
48        protected PackageInstaller.Session doInBackground(Void... params) {
49            PackageInstaller.Session session;
50            ...
51            session = getPackageManager().getPackageInstaller().openSession(mSessionId);
52            try {
53                File file = new File(mPackageURI.getPath());
54                try (InputStream in = new FileInputStream(file)) {
55                    long sizeBytes = file.length();
56
57                    //从session获取的输出流
58                    try (OutputStream out = session.openWrite("PackageInstaller"0, sizeBytes)) {
59                        byte[] buffer = new byte[1024 * 1024];
60                        while (true) {
61                            int numRead = in.read(buffer);
62                            ...
63                            out.write(buffer, 0, numRead);
64                            if (sizeBytes > 0) {
65                                float fraction = ((float) numRead / (float) sizeBytes);
66                                session.addProgress(fraction);
67                    }
68                }
69                return session;
70            }
71            ...
72        }
73        @Override
74        protected void onPostExecute(PackageInstaller.Session session) {
75            if (session != null) {
76                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
77                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
78                broadcastIntent.setPackage(getPackageName());
79                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
80
81                PendingIntent pendingIntent = PendingIntent.getBroadcast(
82                        InstallInstalling.this,mInstallId,broadcastIntent,
83                        PendingIntent.FLAG_UPDATE_CURRENT);
84
85                //包写入session后,进行提交
86                session.commit(pendingIntent.getIntentSender());
87                mCancelButton.setEnabled(false);
88                setFinishOnTouchOutside(false);
89                ...
90            }
91}

整体过程如下:

  1. onCreate 中通过 PackageInstaller 创建Session 并返回 mSessionId

  2. onResume 中开启InstallingAsyncTask,把包信息写入mSessionId对应的session,然后提交

  3. onCreate 中添加了 安装结果的监听,在收到安装结果的广播后 会调用此 跳转到对应结果页面。

PackageInstaller,APK安装器,是在ApplicationPackageManager的 getPackageInstaller()中创建:

1//是在ApplicationPackageManager的.java
2public PackageInstaller getPackageInstaller() {
3    synchronized (mLock) {
4        if (mInstaller == null) {
5            mInstaller = new PackageInstaller(mPM.getPackageInstaller(), mContext.getPackageName(), getUserId());
6            ...
7        }
8        return mInstaller;
9    }
10}

是对 mPM.getPackageInstaller() 获取的 IPackageInstaller实例的封装,而 IPackageInstaller 在系统服务端的具体实现是 PackageInstallerService。

且 PackageInstallerService 的实例是在 PMS 的构造方法中创建的,初始化时会读取 /data/system 目录下的 install_sessions.xml 文件,这个文件中保存了系统中未完成的 Install Session。PMS 会根据文件的内容创建 PackageInstallerSession 对象并插入到 mSessions 中。

PackageInstaller 提供了 应用安装、更新、移除的能力,当然具体实现是 IPC 到了 PackageInstallerService中。

Session,是与 mSessionId绑定的安装会话,代表一个进行中的安装。Session类是对 IPackageInstaller.openSession(sessionId) 获取的 PackageInstallerSession(系统服务端)的封装。Session 管理安装的参数,并提供将安装包临时复制到特定路径(data/app-staging)的能力。

Session的创建和打开 具体实现是在 PackageInstallerService中,主要是 初始化apk的安装信息及环境,并创建一个sessionId,将安装Session与sessionId 进行绑定。(代码较多,就不放了)

我们重点看 把包信息写入mSessionId对应的session,然后提交,回到 InstallInstalling 中,看到在 InstallingAsyncTask 中执行了对 APK包文件 的读取和写入操作,写入的输出流是 通过Session获取,也即是把 将APK文件写入了Session 。完成后 又调用了 session.commit(),具体实现是在PackageInstallerSession, 来看看:

1//PackageInstallerSession.java
2public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
3    ...
4    dispatchStreamValidateAndCommit();
5}
6
7private void dispatchStreamValidateAndCommit() {
8    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
9}

发送了一个handler消息,最后执行在 handleStreamValidateAndCommit()中,然后里面又发送了消息MSG_INSTALL,这个执行在handleInstall():

1private final Handler.Callback mHandlerCallback = new Handler.Callback() {
2    @Override
3    public boolean handleMessage(Message msg) {
4        switch (msg.what) {
5            case MSG_STREAM_VALIDATE_AND_COMMIT:
6                handleStreamValidateAndCommit(); //
7                break;
8            case MSG_INSTALL:
9                handleInstall(); //
10                break;
11            ...
12        }
13        return true;
14    }
15};
16
17private void handleInstall() {
18    ...
19    List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
20
21    installNonStagedLocked(childSessions);
22    ...
23}
24
25private void installNonStagedLocked(List<PackageInstallerSession> childSessions) {
26    final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked();
27     ...
28     //安装走到了 PMS 中
29     mPm.installStage(installingSession);
30  ...
31}

最后,安装过程走到了 PMS 的 installStage()。

目前为止,只是通过 PackageInstaller 维持了安装 Session,把安装包写入到 Session中,真正的安装过程是 PMS 来执行。

3.3 PMS中的安装

那就直接看 PMS 的 installStage():

1//PMS
2void installStage(ActiveInstallSession activeInstallSession) {
3    ...
4    final Message msg = mHandler.obtainMessage(INIT_COPY);
5    final InstallParams params = new InstallParams(activeInstallSession);
6    msg.obj = params;
7    mHandler.sendMessage(msg);
8}

mHandler 处理INIT_COPY是 params.startCopy():

1//HandlerParams.java
2final void startCopy() {
3    handleStartCopy();
4    handleReturnCode();
5}

调用了两个方法实现是在 InstallParams 中:

1public void handleStartCopy() {
2    int ret = PackageManager.INSTALL_SUCCEEDED;
3    ...
4    PackageInfoLite pkgLite = null;
5    //解析包 返回最小的细节:pkgName、versionCode、安装所需空间大小、获取安装位置等
6    pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,origin.resolvedPath, installFlags, packageAbiOverride);
7    ...
8    if (ret == PackageManager.INSTALL_SUCCEEDED) {
9        //以下是 获取安装位置失败情况
10        int loc = pkgLite.recommendedInstallLocation;
11        if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {//安装位置无效
12            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
13        } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {//已存在包
14            ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
15        } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {//空间不够
16            ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
17        } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {//apk无效
18            ret = PackageManager.INSTALL_FAILED_INVALID_APK;
19        } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {//uri无效
20            ret = PackageManager.INSTALL_FAILED_INVALID_URI;
21        } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {//
22            ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
23        } else {
24            ...
25    }
26
27    final InstallArgs args = createInstallArgs(this);//安装参数
28    mVerificationCompleted = true;
29    mIntegrityVerificationCompleted = true;
30    mArgs = args;
31    //如果
32    if (ret == PackageManager.INSTALL_SUCCEEDED) {
33        final int verificationId = mPendingVerificationToken++;
34
35        //apk完整性校验
36        if (!origin.existing) {
37            PackageVerificationState verificationState =new PackageVerificationState(this);
38            mPendingVerification.append(verificationId, verificationState);
39
40            sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
41            ret = sendPackageVerificationRequest(verificationId, pkgLite, verificationState);
42            ...
43        }
44    ...
45    mRet = ret; //赋值 returnCode
46}

解析包返回最小的细节:pkgName、versionCode、安装所需空间大小;确认包安装位置;校验APK完整性。获取结果mRet。然后就看 handleReturnCode():

1    void handleReturnCode() {
2        if (mVerificationCompleted && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
3            ...
4            //如果前面校验ok,这里执行apk拷贝
5            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
6                mRet = mArgs.copyApk();
7            }
8            //执行安装
9            processPendingInstall(mArgs, mRet);
10        }
11    }
12}

判断mRet,执行apk拷贝,然后执行安装。先看 mArgs.copyApk()如何拷贝:

1//FileInstallArgs.java
2int copyApk() {
3    return doCopyApk();
4}
5
6private int doCopyApk() {
7    //获取拷贝文件路径:/data/app
8    try {
9        final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
10        final File tempDir =
11                mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
12        codeFile = tempDir;
13        resourceFile = tempDir;
14    } catch (IOException e) {
15        Slog.w(TAG, "Failed to create copy file: " + e);
16        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
17    }
18
19    //拷贝apk
20    int ret = PackageManagerServiceUtils.copyPackage(
21            origin.file.getAbsolutePath(), codeFile);
22    if (ret != PackageManager.INSTALL_SUCCEEDED) {
23        Slog.e(TAG, "Failed to copy package");
24        return ret;
25    }
26
27    final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
28    final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
29    NativeLibraryHelper.Handle handle = null;
30    try {
31        handle = NativeLibraryHelper.Handle.create(codeFile);
32        //拷贝Native代码 即 .so文件
33        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
34                abiOverride, isIncremental);
35    } catch (IOException e) {
36        Slog.e(TAG, "Copying native libraries failed", e);
37        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
38    }...
39
40    return ret;
41}

在FileInstallArgs中的copyApk()走到doCopyApk(),先获取了拷贝文件路径:/data/app,使用PackageManagerServiceUtils.copyPackage()进行APK拷贝,接着是 .so文件的拷贝。也就是说,把发送到 Session暂存目录 data/app-staging 的APK 拷贝到了 /data/app。

接着看执行安装processPendingInstall(mArgs, mRet):

1//PMS.java
2private void processPendingInstall(final InstallArgs args, final int currentStatus) {
3    ...
4    //res,用于存 安装结果
5    PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
6    processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED,Collections.singletonList(new InstallRequest(args, res)));
7    ...
8}
9
10//安装操作 使用handler入队
11private void processInstallRequestsAsync(boolean success, List<InstallRequest>installRequests) {
12    mHandler.post(() -> {
13        if (success) {
14            for (InstallRequest request : installRequests) {
15                //安装前检验:returnCode不为INSTALL_SUCCEEDED就移除拷贝的apk等
16                request.args.doPreInstall(request.installResult.returnCode);
17            }
18            synchronized (mInstallLock) {
19                //安装:解析apk
20                installPackagesTracedLI(installRequests);
21            }
22            for (InstallRequest request : installRequests) {
23                //同安装前检验
24                request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);
25            }
26        }
27        for (InstallRequest request : installRequests) {
28            //安装后续:备份、可能的回滚、发送安装完成先关广播
29            restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, new PostInstallData(request.args, request.installResult, null));
30        }
31    });
32}

这里我们先重点看安装过程,installPackagesTracedLI() 又走到 installPackagesLI():

1private void installPackagesLI(List<InstallRequest> requests) {
2    ...
3    try {
4        for (InstallRequest request : requests) {
5            final PrepareResult prepareResult;
6            //1.准备:分析当前安装状态,解析包并初始验证
7            prepareResult = preparePackageLI(request.args, request.installResult);
8            ...
9            try {
10                //2.扫描:根据准备阶段解析的包信息上下文 进一步解析
11                final ScanResult result = scanPackageTracedLI(prepareResult.packageToScan, prepareResult.parseFlags,prepareResult.scanFlags, System.currentTimeMillis(),request.args.user, request.args.abiOverride);
12                ...
13                //注册appId
14                createdAppId.put(packageName, optimisticallyRegisterAppId(result));
15               //保存version信息
16                versionInfos.put(result.pkgSetting.pkg.getPackageName(),getSettingsVersionForPackage(result.pkgSetting.pkg));
17            }
18            ...
19        }
20        ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,installResults,prepareResults,mSharedLibraries,Collections.unmodifiableMap(mPackages), versionInfos,lastStaticSharedLibSettings);
21        CommitRequest commitRequest = null;
22        synchronized (mLock) {
23            Map<String, ReconciledPackage> reconciledPackages;
24            try {
25                //3.核对:验证扫描后的包信息和系统状态,确保安装成功
26                reconciledPackages = reconcilePackagesLocked(
27                        reconcileRequest, mSettings.mKeySetManagerService);
28            } catch (ReconcileFailure e) {
29                for (InstallRequest request : requests) {
30                    request.installResult.setError("Reconciliation failed...", e);
31                }
32                return;
33            } 
34            try {
35                //4.提交:提交扫描的包、更新系统状态。这是唯一可以修改系统状态的地方,并且要对所有可预测的错误进行检测。
36                commitRequest = new CommitRequest(reconciledPackages,
37                        mUserManager.getUserIds());
38                commitPackagesLocked(commitRequest);
39                success = true;
40            }
41        }
42        //安装成功的后续才做:准备app数据、编译布局资源、执行dex优化
43        executePostCommitSteps(commitRequest);
44    } ...
45}

安装过程分四个阶段:

  1. 准备,分析当前安装状态,解析包 并初始校验:在 preparePackageLI() 内使用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息;使用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包最终路径 等。

  2. 扫描,根据准备阶段解析的包信息上下文 进一步解析:确认包名真实;根据解析出的信息校验包有效性(是否有签名信息等);搜集apk信息——PackageSetting、apk的静态库/动态库信息等。

  3. 核对,验证扫描后的包信息,确保安装成功:主要就是覆盖安装的签名匹配验证。

  4. 提交,提交扫描的包、更新系统状态:添加 PackageSetting 到 PMS 的 mSettings、添加 AndroidPackage 到 PMS 的 mPackages 、添加 秘钥集 到系统、应用的权限添加到 mPermissionManager、四大组件信息添加到 mComponentResolver 。这是唯一可以修改系统状态的地方,并且要对所有可预测的错误进行检测。

前三步主要是 解析和校验,第四部是把 包信息 提交到 PMS 内存数据结构中。其中解析和提交在上面的PMS初始化中 扫描apk目录后也是同样的过程。这里就不再展开跟踪了。

安装完成后,调用了 executePostCommitSteps() 准备app数据、执行dex优化:

1private void executePostCommitSteps(CommitRequest commitRequest) {
2    ...
3    for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
4        ...
5        //提供目录结构/data/user/用户ID/包名/cache(/data/user/用户ID/包名/code_cache)
6        prepareAppDataAfterInstallLIF(pkg);
7        ...
8        //是否要dex opt
9        final boolean performDexopt =
10                (!instantApp || Global.getInt(mContext.getContentResolver(),
11                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
12                && !pkg.isDebuggable()
13                && (!onIncremental);
14
15        if (performDexopt) {
16            ...
17            DexoptOptions dexoptOptions = new DexoptOptions(packageName,REASON_INSTALL,flags);
18            ...
19            //执行dex优化:dexopt 是对 dex 文件 进行 verification 和 optimization 的操作,其对 dex 文件的优化结果变成了 odex 文件,这个文件和 dex 文件很像,只是使用了一些优化操作码(譬如优化调用虚拟指令等)。
20            mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
21                    null /* instructionSets */,
22                    getOrCreateCompilerPackageStats(pkg),
23                    mDexManager.getPackageUseInfoOrDefault(packageName),
24                    dexoptOptions);
25        }...
26    }
27}
  • prepareAppDataAfterInstallLIF():提供目录结构/data/user/用户ID/包名/cache(/data/user/用户ID/包名/code_cache)

  • mPackageDexOptimizer.performDexOpt():dexopt 是对 dex 文件 进行 verification 和 optimization 的操作,其对 dex 文件的优化结果变成了 odex 文件,这个文件和 dex 文件很像,只是使用了一些优化操作码(譬如优化调用虚拟指令等)。

这两个操作最终都是使用 Installer 对应的方法来操作。前面介绍 PMS 创建时传入了 Installer 的实例,而 Installer 继承自 SystemService 也是一个系统服务。这里来看下:

1public class Installer extends SystemService {
2
3public void onStart() {
4    if (mIsolated) {
5        mInstalld = null;
6    } else {
7        connect();
8    }
9}
10
11private void connect() {
12    IBinder binder = ServiceManager.getService("installd");
13    if (binder != null) {
14        try {
15            binder.linkToDeath(new DeathRecipient() {
16                @Override
17                public void binderDied() {
18                    Slog.w(TAG, "installd died; reconnecting");
19                    connect();
20                }
21            }, 0);
22        } catch (RemoteException e) {
23            binder = null;
24        }
25    }
26
27    if (binder != null) {
28        mInstalld = IInstalld.Stub.asInterface(binder);
29        try {
30            invalidateMounts();
31        } catch (InstallerException ignored) {
32        }
33    } else {
34        Slog.w(TAG, "installd not found; trying again");
35        BackgroundThread.getHandler().postDelayed(() -> {
36            connect();
37        }, DateUtils.SECOND_IN_MILLIS);
38    }
39}
40
41public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
42        String seInfo, int targetSdkVersion) throws InstallerException 
{
43    if (!checkBeforeRemote()) return -1;
44    try {
45        return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
46                targetSdkVersion);
47    } catch (Exception e) {
48        throw InstallerException.from(e);
49    }
50}
51
52public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet, int dexoptNeeded, @Nullable String outputPath ...) throws InstallerException {
53    ...
54    if (!checkBeforeRemote()) return;
55    try {
56        mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
57                dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
58                targetSdkVersion, profileName, dexMetadataPath, compilationReason);
59    } catch (Exception e) {
60        throw InstallerException.from(e);
61    }
62}
  • 在 Installer onStart()方法中 通过 installd 的Binder对象获取了 mInstalld 实例。可见这里是IPC操作,即 System_Server 进程中的 Installer IPC 到 具有root权限的守护进程。像目录 /data/user 的创建 必须要有root权限。

  • PMS中使用了Installer的很多方法,Installer是Java层提供的Java API接口,Installd 则是在init进程启动的具有root权限的Daemon进程。

到这里安装完成,再回到 PMS 的 processInstallRequestsAsync(),最后调用restoreAndPostInstall()进行 备份、可能的回滚、发送安装完成先关广播:

1private void restoreAndPostInstall(
2        int userId, PackageInstalledInfo res, @Nullable PostInstallData data) 
{
3    ...
4    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
5    mHandler.sendMessage(msg);
6    ...
7}

mHandler 使用 PMS的 handlePackagePostInstall()方法处理 POST_INSTALL:

  • 根据安装结果 发送 Intent.ACTION_PACKAGE_ADDED 等广播,桌面Launcher 收到广播后就会在桌上增加App的Icon

  • 调用 PackageInstallSession 中保存的IPackageInstallObserver2实例的onPackageInstalled()方法,最后发送安装成功的通知显示在通知栏,通过 IntentSender 发送 在 InstallInstalling 中就定义好的广播,最后 InstallInstalling页面 根据结果展示 安装成功/安装失败 。

到这里,APK的安装过程 就梳理完毕了,来回顾下:

  • APK用写入Session且包信息和APK安装操作 都提交到了PMS;

  • PMS中先把APK拷贝到 /data/app,然后使用PackageParser2解析APK 获取 四大组件、搜集签名、PackageSetting等信息,并进行校验确保安装成功;

  • 接着提交信息包更新系统状态及PMS的内存数据;

  • 然后使用 Installer 准备用户目录/data/user、进行 dexOpt;

  • 最后发送安装结果通知UI层。

未命名文件.png

四、总结

本篇从用户感受的APK安装讲起,分别详细梳理了 包管理器服务——PackageManagerService 的服务开启、初始化扫描过程;然后分析了对应原生系统的安装过程及页面;接着就是详细分析了安装流程,包括创建Session 发送APK、PMS中的APK拷贝操作、包括APK解析校验和dexOpt等具体安装流程;最后是安装结果的发送。

内容确实很多,但是我们只需要重点了解 PMS的初始化、APK安装 的整体流程即可,无需关注繁杂的细节。

虽然耗费时不少时间阅读源码、也阅读了一些文章,但不免有错误和遗漏之处。欢迎讨论~

END


“终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解!


“终于懂了” 系列:Android组件化,全面掌握!


欢迎关注我的微信:bcce5360,群人数已超200,无法扫码入群,加我微信拉你进群。


点击下方卡片关注JsonChao,为你构建一套

未来Android开发必备的知识体系

▲ 点击上方卡片关注 JsonChao,构建一套

未来 Android 开发必备的知识体系


你若喜欢,为JsonChao点个在看哦 

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存