研究应用的安装过程,老样子,我们还是先从使用入手。
在AndroID
中,通过发送Intent
就可以启动应用的安装过程,比如:
Uri uri = Uri.fromfile(new file(apkfilePath)); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uri,"application/vnd.androID.package-archive"); startActivity(intent);
而在AndroID
的系统应用PackageInstaller
中有一个InstallStart
会响应这个Intent
,
<activity androID:name=".InstallStart" androID:theme="@style/Installer" 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> ...... </activity>
在InstallStart
中会进行各种Uri
的判断,最终会跳转到一个叫做PackageInstallerActivity
的界面。
国内的厂商基本上会在
InstallStart
这里进行修改,替换为自己的安装界面
对于PackageInstallerActivity
来说,它的主要作用是
Uri
协议进行解析,包括file
和package
两种 如果是file
协议会解析APK
文件得到包信息PackageInfo
对未知来源进行处理 如果允许安装未知来源或者根据Intent
判断得出该APK
不是未知来源,就会初始化安装确认界面如果管理员限制来自未知源的安装, 就d出提示Dialog
或者跳转到设置界面 提取并在界面展示应用对应的权限当点击确认安装后,其实会执行到startInstall()
方法,相关内容如下:
private voID startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); ...... newIntent.setData(mPackageURI); newIntent.setClass(this, Installinstalling.class); ...... startActivity(newIntent); finish(); }
上面的逻辑是跳转到Installinstalling
界面进行安装,我们看看这个Activity
:
@OverrIDe protected voID onCreate(@Nullable Bundle savedInstanceState) { ...... if ("package".equals(mPackageURI.getScheme())) { // 直接安装 package try { getPackageManager().installExistingPackage(appInfo.packagename); launchSuccess(); } catch (PackageManager.nameNotFoundException e) { launchFailure(PackageManager.INSTALL_Failed_INTERNAL_ERROR, null); } } else { // 对于 file 安装,按照session的逻辑去走 // 这部分在onResume中开始 final file sourcefile = new file(mPackageURI.getPath()); ...... if (savedInstanceState != null) { mSessionID = savedInstanceState.getInt(SESSION_ID); mInstallID = savedInstanceState.getInt(INSTALL_ID); ...... } else { ...... mSessionID = getPackageManager().getPackageInstaller().createSession(params); ...... // 创建应用安装状态的Observer // 真正注册是在后面 Session 的 commitLocked 中 // InstallEventReceiver 这里是做了二次封装,方便进行持久化 *** 作 InstallEventReceiver.addobserver(this, mInstallID, this::launchFinishBasedOnResult); } ...... } } @OverrIDe protected voID onResume() { super.onResume(); // This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionID); // 启动异步安装任务 InstallingAsyncTask 进行一系列的session *** 作 if (sessionInfo != null && !sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } ...... } }
从上面的方法我们可以看到安装 *** 作主要分为两个接口:
getPackageManager().installExistingPackage()
:主要是对已经并扫描到Settings
的mPackages
集合中的应用进行 *** 作 根据当前的userID
从集合中查询应用状态,如果存在并且应用没有安装,就进行一些简单的配置工作最后通过prepareAppDataAfterInstallliF
方法,创建特定userID
下的应用数据 InstallingAsyncTask
任务中的Session
系列 *** 作:一个应用完整的安装过程从这里开始private final class InstallingAsyncTask extends AsyncTask<VoID, VoID, PackageInstaller.Session> { volatile boolean isDone; @OverrIDe protected PackageInstaller.Session doInBackground(VoID... params) { PackageInstaller.Session session; ...... session = getPackageManager().getPackageInstaller().openSession(mSessionID); try { file file = new file(mPackageURI.getPath()); try (inputStream in = new fileinputStream(file)) { long sizeBytes = file.length(); try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; while (true) { ...... out.write(buffer, 0, numRead); ...... } } } return session; } ...... } @OverrIDe protected voID onPostExecute(PackageInstaller.Session session) { if (session != null) { ...... session.commit(pendingIntent.getIntentSender()); ...... } else { getPackageManager().getPackageInstaller().abandonSession(mSessionID); ...... }}
任务中做的第一个事情是:根据APK
的Uri
,将APK
的信息通过IO
流的形式写入到PackageInstaller.Session
中第二个事情就是PackageInstallerSession.commit()
函数 session
这个东西看来很重要,那么我们先来看看PackageInstallerSession
吧
PackageInstallerSession
AndroID
通过PackageInstallerSession
来表示一次安装过程,一个PackageInstallerSession
包含一个系统中唯一的一个SessionID
,如果一个应用的安装必须分几个阶段来完成,即使设备重启了,也可以通过这个ID
来继续安装过程
PackageInstallerSession
中保存了应用安装的相关数据,例如,安装包的路径、安装的进度、中间数据保存的目录等。
PackageInstallerService
提供了接口createSession
来创建一个Session
对象:
@OverrIDe public int createSession(SessionParams params, String installerPackagename, int userID) { try { return createSessionInternal(params, installerPackagename, userID); } catch (IOException e) { throw ExceptionUtils.wrap(e); } }
createSession()
方法将返回一个系统唯一值作为Session ID
。如果希望再次使用这个Session
,可以通过接口openSession()
打开它。openSession()
方法的代码如下所示:
@OverrIDe public IPackageInstallerSession openSession(int sessionID) { try { return openSessionInternal(sessionID); } catch (IOException e) { throw ExceptionUtils.wrap(e); } }
openSession()
方法将返回一个IPackageInstallerSession
对象,它是Binder
服务PackageInstallerSession
的IBinder
对象。在PackageInstallerService
中mSessions
数组保存了所有PackageInstallerSession
对象,定义如下:
@GuardedBy("mSessions") private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
我们知道,当PackageManagerService
初始化时会创建PackageInstallerService
服务,在这个服务的初始化函数中会读取/data/system
目录下的install_sessions.xml
文件,这个文件中保存了系统中未完成的Install Session
。然后PackageInstallerService
会根据文件的内容创建PackageInstallerSession
对象并插入到mSessions
中。
而对于上面提到的commit()
函数,是真正触发PMS
安装的函数,定义如下:
@OverrIDe public voID commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { ...... mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); ...... } private final Handler.Callback mHandlerCallback = new Handler.Callback() { @OverrIDe public boolean handleMessage(Message msg) { switch (msg.what) { ...... case MSG_COMMIT: synchronized (mlock) { ...... commitLocked(); ...... } break; ...... } return true; } }; private voID commitLocked() throws PackageManagerException { ...... mRemoteObserver.onUserActionrequired(intent); ...... mPm.installStage(mPackagename, stageDir, localObserver, params, mInstallerPackagename, mInstallerUID, user, mSigningDetails); }
commit()
函数最后会调用到PMS
的installStage()
方法,到这里就触发了安装的第一阶段:文件复制
我们已经知道PackageInstallerSession
通过调用PMS
的installStage()
方法开启了安装第一阶段,不过整个安装过程比较复杂,我们先看看这个过程的序列图:
installStage()
我们继续从installStage()
来分析,代码如下:
voID installStage(String packagename, file stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackagename, int installerUID, UserHandle user, PackageParser.SigningDetails signingDetails) { ...... final Message msg = mHandler.obtainMessage(INIT_copY); ...... final InstallParams params = new InstallParams(origin, null, observer, sessionParams.installFlags, installerPackagename, sessionParams.volumeUuID, verificationInfo, user, sessionParams.abiOverrIDe, sessionParams.grantedRuntimePermissions, signingDetails, installreason); ...... msg.obj = params; ...... mHandler.sendMessage(msg); }
installStage()
方法主要进行了:
InstallParams
对象 InstallParams
是安装过程中的主要数据结构一般情况,安装应用的时间通常比较长,因此AndroID
把安装过程拆分,把调用过程的参数数据保存到InstallParams
中 创建并发送了一个Message
消息 msg.what
为INIT_copY
msg.obj
为InstallParams
对象 先来简单了解下InstallParams
的类:
上图中的类都是PMS的内部类
InstallParams
继承自HandlerParams
,用来记录安装应用的参数。InstallParams
中有一个成员变量mArgs
,是一个抽象类型InstallArgs
,主要是用来执行APK
的复制,真正的实现类包括: fileInstallArgs
:用来完成非ASEC
应用的安装 asec
全称是AndroID Secure External Cache
MoveInstallArgs
:用来完成已安装应用的移动安装 简单了解完InstallParams
,我们继续看INIT_copY
消息的处理过程:
case INIT_copY: { HandlerParams params = (HandlerParams) msg.obj; int IDx = mPendingInstalls.size(); ...... if (!mBound) { // 绑定 DefaultContainerService if (!connectToService()) { params.serviceError(); ...... return; } else { // 连接成功,将安装信息保存到 mPendingInstalls 的尾部 mPendingInstalls.add(IDx, params); } } else { // 如果已经绑定服务,直接插入到集合尾部 mPendingInstalls.add(IDx, params); if (IDx == 0) { // 如果插入前的集合为空 // 直接发送 MCS_BOUND 消息 mHandler.sendEmptyMessage(MCS_BOUND); } } break;}
整个过程比较简洁:
首先是connectToService()
去绑定和启动DefaultContainerService
服务。 服务绑定是一个异步的过程,需要等待绑定结果通过onServiceConnected()
返回从DefaultContainerConnection
中可以看到,当服务连接成功后,会发送MSC_BOUNF
消息 class DefaultContainerConnection implements ServiceConnection { public voID onServiceConnected(Componentname name, IBinder service) { final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); }}
然后将安装的参数信息放到了mPendingInstalls
列表中。 当有多个安装请求到达时,可以通过mPendingInstalls
进行排队 如果添加前的列表为空,说明没有其他的安装请求,此时直接发送MSC_BOUND
消息那么,重点又转移到了MCS_BOUND
的消息处理,代码如下:
case MCS_BOUND: { if (msg.obj != null) { mContainerService = (IMediaContainerService) msg.obj; } if (mContainerService == null) { if (!mBound) { // DefaultContainerService bind失败的情况 for (HandlerParams params : mPendingInstalls) { params.serviceError(); ...... } mPendingInstalls.clear(); } else { Slog.w(TAG, "Waiting to connect to media container service"); } } else if (mPendingInstalls.size() > 0) { // 取出头部安装参数 HandlerParams params = mPendingInstalls.get(0); if (params != null) { ...... if (params.startcopy()) {// 执行安装 // 已执行成功,移除头部元素 if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { ...... // 如果集合中没有需要安装的信息了,发送MCS_UNBIND延时消息进行解绑 removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000); } } else { // 如果集合中还存在安装信息,发送 MCS_BOUND 消息,继续处理 mHandler.sendEmptyMessage(MCS_BOUND); } } } } break;}
MCS_BOUND
消息的处理过程就是:
InstallParams
类的startcopy()
方法来执行安装 处理逻辑肯定在这个方法里面,下面细讲 只要mPendingInstalls
中还有安装信息,就会重复发送MCS_BOUND
消息直到所有应用都处理完毕,然后发送一个延时10s
的MCS_UNBIND
消息 MCS_UNBIND
消息的处理很简单,收到消息后如果发现mPendingInstalls
中有数据了,则发送MCS_BOUND
消息继续安装。否则断开DefaultContainerService
连接,安装过程到此结束 case MCS_UNBIND: { if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) { if (mBound) { // 断开 DefaultContainerService 连接 disconnectService(); } } else if (mPendingInstalls.size() > 0) { // 继续发送执行安装 *** 作的消息 mHandler.sendEmptyMessage(MCS_BOUND); } break;}
针对startcopy()
方法的处理过程,代码如下:
private static final int MAX_RETRIES = 4;final boolean startcopy() { boolean res; try { // 重试超过4次,则推出 if (++mRetrIEs > MAX_RETRIES) { // 发送 MCS_GIVE_UP 消息取消安装 mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false; } else { // 核心方法,执行 copy handleStartcopy(); res = true; } } catch (remoteexception e) { // 出现异常,发送 MCS_RECONNECT 消息,进行重连 mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } // 开始进入第二阶段 handleReturnCode(); return res;}
startcopy()
方法通过调用handleStartcopy()
来完成安装过程。考虑到安装过程的不确定性,startcopy()
主要是进行错误处理:
MCS_RECONNECT
消息处理中,会重新绑定DefaultContainerService
如果绑定成功,安装过程就会重新开始,startcopy()
会被再次调用。重试的次数记录在变量mRetrIEs
中,超过4次,安装失败 handleStartcopy()
执行成功,startcopy()
会调用handleReturnCode()
来继续处理handleStartcopy()
方法比较长,简要代码如下:
public voID handleStartcopy() throws remoteexception { int ret = PackageManager.INSTALL_SUCCEEDED; ...... if (onInt && onSd) { // 设置安装路径异常标志 ret = PackageManager.INSTALL_Failed_INVALID_INSTALL_LOCATION; } else if (onSd && ephemeral) { // 设置安装路径异常标志 ret = PackageManager.INSTALL_Failed_INVALID_INSTALL_LOCATION; } else { // 获取安装包中的 PackageInfolite 对象 pkglite = mContainerService.getMinimalPackageInfo(origin.resolvedpath, installFlags, packageAbiOverrIDe); ...... if (!origin.staged && pkglite.recommendedInstallLocation == PackageHelper.RECOMMEND_Failed_INSUFFICIENT_STORAGE) { // Todo: focus freeing disk space on the target device // 如果安装空间不够,尝试释放cache空间 ...... try { mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); ...... } catch (InstallerException e) { Slog.w(TAG, "Failed to free cache", e); } ...... if (pkglite.recommendedInstallLocation == PackageHelper.RECOMMEND_Failed_INVALID_URI) { pkglite.recommendedInstallLocation = PackageHelper.RECOMMEND_Failed_INSUFFICIENT_STORAGE; } } } ...... final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { ...... if (!origin.existing && requiredUID != -1 && isverificationEnabled(verifIErUser.getIDentifIEr(), installFlags, installerUID)) { // 此部分是在执行应用的校验 // 主要通过向待校验功能的组件发送Intent来完成 ...... /* * We don't want the copy to proceed until verification * succeeds, so null out this fIEld. */ mArgs = null; } else { // 无需校验,调用 InstallArgs 的 copyAPK 方法继续处理 ret = args.copyApk(mContainerService, true); } } mRet = ret;}
上面handleStartcopy()
方法的主要逻辑是:
getMinimalPackageInfo()
方法确认是否还有足够的安装空间,如果安装空间不够,会通过mInstaller.freeCache()
来释放一部分接下来通过createInstallArgs()
创建InstallArgs
对象,createInstallArgs()
方法中会对InstallParams
的move
成员变量进行判断 move
不为空使用MoveInstallArgs
move
为空使用fileInstallArgs
,我们重点来看这个 再接下来对apk
进行校验,这个校验过程是通过发送Intent.ACTION_PACKAGE_NEEDS_VERIFICATION)
广播给系统中所有可以接收该Intent
的应用来完成的无需校验,执行InstallArgs
的copyApk()
方法InstallArgs
的copyApk()
方法如下:
int copyApk(IMediaContainerService imcs, boolean temp) throws remoteexception { ...... return docopyApk(imcs, temp);}private int docopyApk(IMediaContainerService imcs, boolean temp) throws remoteexception { if (origin.staged) { codefile = origin.file; resourcefile = origin.file; return PackageManager.INSTALL_SUCCEEDED; } try { final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; // 在/data/app路径下生成临时文件 final file tempDir = mInstallerService.allocateStageDirLegacy(volumeUuID, isEphemeral); codefile = tempDir; resourcefile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_Failed_INSUFFICIENT_STORAGE; } // 为临时文件创建文件描述符 ParcelfileDescriptor // 主要是为了将文件序列化,方便通过binder传输 final IParcelfileDescriptorFactory target = new IParcelfileDescriptorFactory.Stub() { @OverrIDe public ParcelfileDescriptor open(String name, int mode) throws remoteexception { if (!fileUtils.isValIDExtfilename(name)) { throw new IllegalArgumentException("InvalID filename: " + name); } try { final file file = new file(codefile, name); final fileDescriptor fd = Os.open(file.getabsolutePath(), O_RDWR | O_CREAT, 0644); Os.chmod(file.getabsolutePath(), 0644); return new ParcelfileDescriptor(fd); } catch (ErrnoException e) { throw new remoteexception("Failed to open: " + e.getMessage()); } } }; int ret = PackageManager.INSTALL_SUCCEEDED; // 使用服务 DefaultContainerService 的 copyPackage 方法复制文件 ret = imcs.copyPackage(origin.file.getabsolutePath(), target); if (ret != PackageManager.INSTALL_SUCCEEDED) { Slog.e(TAG, "Failed to copy package"); return ret; } // 安装应用中自带的 native 动态库 final file libraryRoot = new file(codefile, liB_DIR_name); NativelibraryHelper.Handle handle = null; try { handle = NativelibraryHelper.Handle.create(codefile); ret = NativelibraryHelper.copyNativeBinarIEsWithOverrIDe(handle, libraryRoot, abiOverrIDe); } catch (IOException e) { Slog.e(TAG, "copying native librarIEs Failed", e); ret = PackageManager.INSTALL_Failed_INTERNAL_ERROR; } finally { IoUtils.closeQuIEtly(handle); } return ret;}
copyApk()
最后调用了DefaultContainerService
的copyPackage()
方法将应用的文件复制到了/data/app
目录下。如果应用中还有native
的动态库,也会把apk中的库文件提取出来。
执行完copyApk()
方法后,安装第一阶段的工作就完成了,应用安装到了/data/app
目录下。我们接下来看看handleReturnCode()
的内容,也就是第二阶段。
第二阶段的工作主要是
把应用的格式转换成oat
格式为应用创建数据目录把应用的信息装载进PMS
的数据结构中handleReturnCode()
方法代码如下:
voID handleReturnCode() { if (mArgs != null) { processpendingInstall(mArgs, mRet); }}
handleReturnCode()
只是调用了processpendingInstall()
方法继续处理,这个方法的代码如下:
private voID processpendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public voID run() { mHandler.removeCallbacks(this);// 防止重复调用 PackageInstalledInfo res = new PackageInstalledInfo(); res.setReturnCode(currentStatus); res.uID = -1; res.pkg = null; res.removedInfo = null; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageTracedli(args, res); } args.doPostInstall(res.returnCode, res.uID); } ...... // 将相关数据记录到 mRunningInstalls 集合中 PostInstallData data = new PostInstallData(args, res); mRunningInstalls.put(token, data); ...... // 省略备份部分 if (!doRestore) { Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); mHandler.sendMessage(msg); } } });}
processpendingInstall()
方法中post
了一个任务:
installPackageTracedli()
来装载应用接下来的部分是通过Ibackupmanager
服务执行备份相关的 *** 作,这部分就不先深入了,篇幅已经过大。。。备份完成后发送POST_INSTALL
消息继续处理我们还是先看下核心方法installPackageTracedli()
的内容,代码如下:
private voID installPackageTracedli(InstallArgs args, PackageInstalledInfo res) { installPackageli(args, res);}private voID installPackageli(InstallArgs args, PackageInstalledInfo res) { ...... // Result object to be returned res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); res.installerPackagename = installerPackagename; ...... // 创建 APK 解析对象 PackageParser PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); ...... final PackageParser.Package pkg; try { // 解析 APK pkg = pp.parsePackage(tmpPackagefile, parseFlags); } catch (PackageParserException e) { res.setError("Failed parse during installPackageli", e); return; } ...... // 收集应用的签名信息 if (args.signingDetails != PackageParser.SigningDetails.UNKNowN) { pkg.setSigningDetails(args.signingDetails); } else { PackageParser.collectCertificates(pkg, false /* skipVerify */); } ...... pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { // 如果是应用升级的情况,继续使用旧的应用包名 String oldname = mSettings.getRenamedPackageLPr(pkgname); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldname) && mPackages.containsKey(oldname)) { pkg.setPackagename(oldname); pkgname = pkg.packagename; replace = true; } ......// 省略部分新旧应用的属性比对 } // 检查已安装的应用集合中是否存在该应用的记录 PackageSetting ps = mSettings.mPackages.get(pkgname); if (ps != null) { // 存在同名应用 ......// 省略一些本地库的签名判断 // 检查是否为系统应用 oldCodePath = mSettings.mPackages.get(pkgname).codePathString; if (ps.pkg != null && ps.pkg.applicationInfo != null) { systemApp = (ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYstem) != 0; } res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIDs(), true); } // 检查应用中定义的Permission是否被重复定义 int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { final PackageParser.Permission perm = pkg.permissions.get(i); final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name); ...... // 省略部分只要是对系统定义权限和非系统定义权限的区分 // 如果是系统应用定义的权限,则忽略本应用中的定义,然后继续 // 如果是非系统应用定义的权限,则本次安装失败 ...... } } ...... try (PackageFreezer freezer = freezePackageForInstall(pkgname, installFlags, "installPackageli")) { if (replace) { ...... replacePackageliF(pkg, parseFlags, scanFlags, args.user, installerPackagename, res, args.installreason); } else { installNewPackageliF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackagename, volumeUuID, res, args.installreason); } } ...... // 省略 dexopt 相关 *** 作 }}
这部分的代码逻辑和前面介绍的scanDirli()
很像,不详细介绍了,整体流程是:
PackageParser
解析了安装的应用文件 解析过程和scanDirli()
中的parsePackage()
一样 得到解析结果后,主要是判断新安装的应用是否和已安装的应用构成升级关系 如果是,则调用replacePackageliF()
继续处理否则调用installNewPackageliF()
处理 这里不深入进行分析了哈,跟踪完调用过程后其实都会走到addForInitli()
中
再次吐槽一下
PMS
,package
解析部分的调用是真滴复杂啊,各种条件判断
我们继续看下POST_INSTALL
消息的处理过程:
case POST_INSTALL: { PostInstallData data = mRunningInstalls.get(msg.arg1); final boolean dIDRestore = (msg.arg2 != 0); mRunningInstalls.delete(msg.arg1); if (data != null) { InstallArgs args = data.args; PackageInstalledInfo parentRes = data.res; ...... // Handle the parent package handlePackagePostInstall(parentRes, grantPermissions, killApp, virtualPreload, grantedPermissions, dIDRestore, args.installerPackagename, args.observer); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) ? parentRes.addedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i); handlePackagePostInstall(childRes, grantPermissions, killApp, virtualPreload, grantedPermissions, false /*dIDRestore*/, args.installerPackagename, args.observer); } ...... } ......} break;
POST_INSTALL
的消息处理基本上都是在执行handlePackagePostInstall()
方法,handlePackagePostInstall()
方法主要工作包括两个:
Intent.ACTION_PACKAGE_ADDED
:新应用安装成功Intent.ACTION_PACKAGE_REPLACED
:应用更新成功发广播的目的是通知系统中的其他应用APK
安装完成。例如,Launcher
中需要增加应用的图标等 调用IPackageInstallObserver2
的onPackageInstalled()
方法通知观察者 我们最初介绍的PackageInstaller
在安装应用时就是通过注册观察者来实现结果监听的 总结关于
PMS
的软件卸载流程,大家同样可以从PackageInstaller
入手,关键类是UninstallUninstalling
。这里就先跳过了,PMS
篇幅占用时间太久了。。。。。
以上是内存溢出为你收集整理的深入Android系统(十)PMS-3-应用安装过程全部内容,希望文章能够帮你解决深入Android系统(十)PMS-3-应用安装过程所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)