今天我们一起来看一下 , 系统是怎么把应用安装到你的手机上的 。
入口
当我们点击一个 APK 进行安装时 , 会弹出以下界面
文章插图
这是 Android Framework 提供的软件包安装程序 , 页面为:PackageInstallerActivity
packagecom.android.packageinstaller; publicclassPackageInstallerActivityextendsActivity{ publicvoidonClick(View v){ if(v == mOk) { if(mOk.isEnabled()) { //...省略一些细节startInstall(); // 开始安装} } elseif(v == mCancel) { // Cancel and finish} } privatevoidstartInstall(){ // Start subactivity to actually install the applicationIntent newIntent = newIntent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this, InstallInstalling.class); ...// newIntent.putExtra 其它参数 startActivity(newIntent); finish(); } } 在这个页面点击安装时 , 会把安装包的信息通过 Intent 传递到 InstallInstalling 这个 Activity. InstallInstalling 的作用主要是向 PMS 发送包信息以及处理回调 。
InstallInstalling.onCreate
进来新页面 , 当然是先从 onCreate 开始了
protectedvoidonCreate(@Nullable Bundle savedInstanceState){ ApplicationInfo appInfo = getIntent() .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); ... // 根据 mPackageURI 创建一个对应的 FilefinalFile sourceFile = newFile(mPackageURI.getPath()); // 显示应用信息 icon, 应用名或包名PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, sourceFile), R.id.app_snippet); // 创建、组装 SessionParams , 它用来携带会话的参数PackageInstaller.SessionParams params = newPackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.installFlags = PackageManager.INSTALL_FULL_APP; ... params.installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME); File file = newFile(mPackageURI.getPath()); // 对 APK 进行轻量级的解析 , 并将解析的结果赋值给 SessionParams 相关字段 PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0); params.setAppPackageName(pkg.packageName); params.setInstallLocation(pkg.installLocation); params.setSize( PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride)); // 向 InstallEventReceiver 注册一个观察者返回一个新的 mInstallId// InstallEventReceiver 是一个 BroadcastReceiver , 可以通过 EventResultPersister 接收到所有的安装事件// 这里事件会回调给 this::launchFinishBasedOnResultmInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult); try{ // PackageInstaller 的 createSession // 方法内部会通过 IPackageInstaller 与 PackageInstallerService进行进程间通信 , // 最终调用的是 PackageInstallerService 的 createSession 方法来创建并返回 mSessionIdmSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch(IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } InstallInstalling.onResume
接下来是 onResume, 通过 InstallingAsyncTask 做一些异步工作
protectedvoidonResume(){ super.onResume(); // This is the first onResume in a single life of the activityif(mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId); if(sessionInfo != null&& !sessionInfo.isActive()) { mInstallingTask = newInstallingAsyncTask(); mInstallingTask.execute(); } else{ // we will receive a broadcast when the install is finishedmCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } } InstallingAsyncTask
【手把手教你apk的安装流程 ios如何安装apk软件步骤】privatefinalclassInstallingAsyncTaskextendsAsyncTask<Void, Void, PackageInstaller.Session> { @OverrideprotectedPackageInstaller.Session doInBackground(Void... params){ PackageInstaller.Session session; session = getPackageManager().getPackageInstaller().openSession(mSessionId); session.setStagingProgress(0); File file = newFile(mPackageURI.getPath()); OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes) InputStream in = newFileInputStream(file) longsizeBytes = file.length(); byte[] buffer = newbyte[1024* 1024]; while(true) { intnumRead = in.read(buffer); if(numRead == -1) { session.fsync(out); break; } // 将 APK 文件通过 IO 流的形式写入到 PackageInstaller.Session 中out.write(buffer, 0, numRead); if(sizeBytes > 0) { floatfraction = ((float) numRead / (float) sizeBytes); session.addProgress(fraction); } } returnsession; } @OverrideprotectedvoidonPostExecute(PackageInstaller.Session session){ Intent broadcastIntent = newIntent(BROADCAST_ACTION); broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntent.setPackage( getPackageManager().getPermissionControllerPackageName()); broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallInstalling.this, mInstallId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); // 调用 PackageInstaller.Session 的 commit 方法 , 进行安装session.commit(pendingIntent.getIntentSender()); } } 来看下 PackageInstaller.Session 里的实现
publicstaticclassSessionimplementsCloseable{ privateIPackageInstallerSession mSession; publicvoidcommit(@NonNull IntentSender statusReceiver){ try{ mSession.commit(statusReceiver, false); } catch(RemoteException e) { throwe.rethrowFromSystemServer(); } } } mSession 的类型为 IPackageInstallerSession , 这说明要通过 IPackageInstallerSession 来进行进程间的通信 , 最终会调用PackageInstallerSession 的 commit 方法 , 剧透一下在这个类执行完后 , 就会进入鼎鼎大名的 PMS 去真正的执行安装了 :
publicclassPackageInstallerSessionextendsIPackageInstallerSession.Stub{ publicvoidcommit(@NonNull IntentSender statusReceiver, booleanforTransfer){ // 将包的信息封装为 PackageInstallObserverAdapterfinalPackageInstallObserverAdapter adapter = newPackageInstallObserverAdapter( mContext, statusReceiver, sessionId, isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId); mRemoteObserver = adapter.getBinder(); // 通过 Handler 处理消息事件mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } privatefinalHandler.Callback mHandlerCallback = newHandler.Callback() { @OverridepublicbooleanhandleMessage(Message msg){ switch(msg.what) { caseMSG_COMMIT: commitLocked(); break; } } }; privatefinalPackageManagerService mPm; privatevoidcommitLocked()throwsPackageManagerException { mPm.installStage(mPackageName, stageDir, ...); } } mPm 就是系统服务 PackageManagerService 。installStage 方法就是正式开始 apk 的安装过程 。这个过程包括两大步:拷贝安装包、装载代码
拷贝安装包
继续看 installStage 的代码
// PackageManagerService.javavoidinstallStage(String packageName, File stagedDir,...){ finalMessage msg = mHandler.obtainMessage(INIT_COPY); // 把之前传入的 sessionParams 安装信息 , 及其它信息封装成 InstallParamsfinalInstallParams params = newInstallParams(origin, null, observer, sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid, verificationInfo, user, sessionParams.abiOverride, sessionParams.grantedRuntimePermissions, signingDetails, installReason); mHandler.sendMessage(msg); } 发送的消息 INIT_COPY 从名字上就知道是去初始化复制
classPackageHandlerextendsHandler{ voiddoHandleMessage(Message msg){ switch(msg.what) { caseINIT_COPY: { HandlerParams params= (HandlerParams) msg.obj; // 调用 connectToService 方法连接安装 apk 的 Service 服务 。if(!connectToService()) { return; } else{ // Once we bind to the service, the first// pending request will be processed.mPendingInstalls.add(idx, params); } } } } privateboolean connectToService(){ // 通过隐式 Intent 绑定 Service , 实际绑定的 Service 是 DefaultContainerService Intent service = newIntent().setComponent(DEFAULT_CONTAINER_COMPONENT); if(mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) { mBound = true; returntrue; } returnfalse; } } 当绑定 Service 成功之后 , 会在 mDefContainerConn 的 onServiceConnection 方法中发送一个绑定操作的 Message , 如下所示:
classDefaultContainerConnectionimplementsServiceConnection{ publicvoidonServiceConnected(ComponentName name, IBinder service){ final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } } // MCS_BOUND 还是在前面的 PackageHandler 处理 , 直接截取相关代码{ HandlerParams params= mPendingInstalls.get(0); if(params.startCopy()) { if(mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } } } mPendingInstalls 是一个等待队列 , 里面保存所有需要安装的 apk 解析出来的 HandlerParams 参数(前面在 INIT_COPY 处理时 add) , 从 mPendingInstalls 中取出第一个需要安装的 HandlerParams 对象 , 并调用其 startCopy 方法 , 在 startCopy 方法中会继续调用一个抽象方法 handleStartCopy 处理安装请求 。通过之前的分析 , 我们知道 HandlerParams 实际类型是 InstallParams 类型 , 因此最终调用的是 InstallParams 的 handlerStartCopy 方法 , 这是整个安装包拷贝的核心 。
classInstallParamsextendsHandlerParams{ publicvoidhandleStartCopy()throwsRemoteException { if(origin.staged) { // 设置安装标志位 , 决定是安装在手机内部存储空间还是 sdcard 中if(origin.file != null) { installFlags |= PackageManager.INSTALL_INTERNAL; installFlags &= ~PackageManager.INSTALL_EXTERNAL; } } // 判断安装位置finalbooleanonSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; finalbooleanonInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; finalbooleanephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; finalInstallArgs args = createInstallArgs(this); // ... ret = args.copyApk(mContainerService, true); } privateInstallArgs createInstallArgs(InstallParams params){ if(params.move != null) { returnnewMoveInstallArgs(params); } else{ returnnewFileInstallArgs(params); } } } 正常的流程下 , createInstallArgs 返回的是 FileInstallArgs 对象
FileInstallArgs 的 copyApk 方法
intcopyApk(IMediaContainerService imcs, booleantemp)throwsRemoteException { returndoCopyApk(imcs, temp); } privateintdoCopyApk(IMediaContainerService imcs, booleantemp)throwsRemoteException { // 创建存储安装包的目标路径 , 实际上是 /data/app/ 应用包名目录finalFile tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); finalIParcelFileDescriptorFactory target = newIParcelFileDescriptorFactory.Stub() { @OverridepublicParcelFileDescriptor open(String name, intmode)throwsRemoteException { finalFile file = newFile(codeFile, name); finalFileDescriptor fd = Os.open(file.getAbsolutePath(), O_RDWR | O_CREAT, 0644); Os.chmod(file.getAbsolutePath(), 0644); returnnewParcelFileDescriptor(fd); } }; // 调用服务的 copyPackage 方法将安装包 apk 拷贝到目标路径中;ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 将 apk 中的动态库 .so 文件也拷贝到目标路径中 。ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, abiOverride); } 这里的 IMediaContainerService imcs 就是之前连接上的 DefaultContainerService
DefaultContainerService
copyPackage 方法本质上就是执行 IO 流操作 , 具体如下:
// new IMediaContainerService.Stub()publicintcopyPackage(String packagePath, IParcelFileDescriptorFactory target){ PackageLite pkg = null; final File packageFile = newFile(packagePath); pkg = PackageParser.parsePackageLite(packageFile, 0); returncopyPackageInner(pkg, target); } // DefaultContainerServiceprivateintcopyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target){ copyFile(pkg.baseCodePath, target, "base.apk"); if(!ArrayUtils.isEmpty(pkg.splitNames)) { for(inti = 0; i < pkg.splitNames.length; i++) { copyFile(pkg.splitCodePaths[i], target, "split_"+ pkg.splitNames[i] + ".apk"); } } returnPackageManager.INSTALL_SUCCEEDED; } privatevoidcopyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName){ InputStream in= null; OutputStream out= null; try{ in= newFileInputStream(sourcePath); out= newParcelFileDescriptor.AutoCloseOutputStream( target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE)); FileUtils.copy(in, out); } finally{ IoUtils.closeQuietly(out); IoUtils.closeQuietly(in); } } 最终安装包在 data/app 目录下以 base.apk 的方式保存 , 至此安装包拷贝工作就已经完成 。
装载代码
安装包拷贝完成 , 就要开始真正安装了 。代码回到上述的 HandlerParams 中的 startCopy 方法:
privateabstractclassHandlerParams{ finalbooleanstartCopy(){ ... handleStartCopy(); handleReturnCode(); } } classInstallParamsextendsHandlerParams{ @OverridevoidhandleReturnCode(){ // If mArgs is null, then MCS couldn't be reached. When it// reconnects, it will try again to install. At that point, this// will succeed.if(mArgs != null) { processPendingInstall(mArgs, mRet); } } privatevoidprocessPendingInstall(finalInstallArgs args, finalintcurrentStatus){ mHandler.post(newRunnable() { publicvoidrun(){ PackageInstalledInfo res = newPackageInstalledInfo(); if(res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // 预安装操作 , 主要是检查安装包的状态 , 确保安装环境正常 , 如果安装环境有问题会清理拷贝文件args.doPreInstall(res.returnCode); synchronized(mInstallLock) { // 安装阶段installPackageTracedLI(args, res); } args.doPostInstall(res.returnCode, res.uid); } ... } } } } installPackageLI
installPackageTracedLI 方法中添加跟踪 Trace , 然后调用 installPackageLI 方法进行安装 。这个方法有 600 行 , 取部分关键代码:
privatevoidinstallPackageLI(InstallArgs args, PackageInstalledInfo res){ ... PackageParser pp = newPackageParser(); finalPackageParser.Package pkg; // 1. parsePackagepkg = pp.parsePackage(tmpPackageFile, parseFlags); // 2. 校验安装包签名finalKeySetManagerService ksms = mSettings.mKeySetManagerService; if(ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { if(!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "+ pkg.packageName + " upgrade keys do not match the "+ "previously installed version"); return; } } // 3. 设置相关权限 , 生成、移植权限 intN = pkg.permissions.size(); for(inti = N-1; i >= 0; i--) { finalPackageParser.Permission perm = pkg.permissions.get(i); ... } // 4. 生成安装包Abi(Application binary interface , 应用二进制接口)try{ String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); finalbooleanextractNativeLibs = !pkg.isLibrary(); derivePackageAbi(pkg, abiOverride, extractNativeLibs); } catch(PackageManagerException pme) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); return; } // 5. 冻结 APK , 执行替换安装 或 新安装 , try(PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI")) { if(replace) { replacePackageLIF(pkg, parseFlags, scanFlags, args.user, installerPackageName, res, args.installReason); } else{ privatevoidinstallNewPackageLIF((pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res, args.installReason); } } // 5. 优化dex文件(实际为 dex2oat 操作 , 用来将 apk 中的 dex 文件转换为 oat 文件)if(performDexopt) { mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, null/* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(pkg.packageName), dexoptOptions); } ... } 最后我们来看一下 installNewPackageLIF
privatevoidinstallNewPackageLIF(PackageParser.Package pkg, final@ParseFlags intparseFlags, final@ScanFlags intscanFlags,...){ // 继续扫描解析 apk 安装包文件 , 保存 apk 相关信息到 PMS 中 , 并创建 apk 的 data 目录 , 具体路径为 /data/data/应用包名PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, System.currentTimeMillis(), user); // 更新系统设置中的应用信息 , 比如应用的权限信息updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason); if(res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // 安装然后准备 APP 数据 prepareAppDataAfterInstallLIF(newPackage); } else{ // 如果安装失败 , 则将安装包以及各种缓存文件删除deletePackageLIF(pkgName, UserHandle.ALL, false, null, PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null); } } prepareAppDataAfterInstallLIF 还会有一系列的调用
prepareAppDataAfterInstallLIF() -> prepareAppDataLIF() -> prepareAppDataLeafLIF() -> mInstaller.createAppData(...) finalInstaller mInstaller; privatevoid prepareAppDataLeafLIF(...) { // 最终调用 系统服务 Installer 安装ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo, app.targetSdkVersion); } publicclassInstallerextendsSystemService{ ... } 至此整个 apk 的安装过程结束 , 实际上安装成功之后 , 还会发送一个 App 安装成功的广播 ACTION_PACKAGE_ADDED 。手机桌面应用注册了这个广播 , 当接收到应用安装成功之后 , 就将 apk 的启动 icon 显示在桌面上 。
总结
在手机上仅仅是点一下安装按钮而已 , 背后却有着这么繁琐的流程 , 相信通过今天的学习大家应该能对系统的应用安装流程有一个完整的认知 。回顾一下安装的流程如下:
- 点击 APK 安装 , 会启动 PackageInstallerActivity , 再进入 InstallInstalling 这两个 Activity 显示应用信息
- 点击页面上的安装 , 将 APK 信息存入 PackageInstaller.Session 传到 PMS
- PMS会做两件事 , 拷贝安装包和装载代码
- 在拷贝安装包过程中会开启 Service 来 copyAPK 、检查apk安装路径 , 包的状态
- 拷贝完成以 base.apk 形式存在/data/app包名下
- 装载代码过程中 , 会继续解析 APK , 把清单文件内容存放于 PMS
- 对 apk 进行签名校验
- 安装成功后 , 更新应用设置权限 , 发送广播通知桌面显示APP图标 , 安装失败则删除安装包和各种缓存文件
- 执行 dex2oat 优化
推荐阅读
- 手把手教你12306抢票全攻略 12306抢票技巧攻略
- 10年老HR教你3招快速招人 怎么招人最快最有效
- 10分钟教你做蛋清甜点 蛋清能做什么简单甜点
- 1分钟教你解决耳机滋滋声 耳机里有滋滋的声音怎么办
- 教你旧手机资料导入到新手机 换了新手机怎么把电话号码转过去
- 一文教你挑选硬盘最全指南 希捷和西数哪个好
- 教你查看mac存储空间的方法 mac怎么看存储空间大小
- 1分钟教你取消自动续费 小米电视会员怎么取消自动续费
- 手把手教你下载使命召唤 使命召唤去哪里下载正版
- 手把手教你录屏最全指南 苹果录制屏幕在哪设置