Android 应用安装流程初探

启动安装

在app代码中,如果想安装另一个应用,一般通过下列代码实现,调用后会显示系统安装界面

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType("the apkPath of app...", 
    "application/vnd.android.package-archive");
startActivity(intent);

系统安装界面是com.android.packageinstaller.PackageInstallerActivity,它在manifest里的声明是

<activity android:name=".PackageInstallerActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        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: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" />
    </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>

可以看到这个activity支持三种action,VIEW、INSTALL_PACKAGE、CONFIRM_PERMISSIONS,其中VIEW和INSTALL_PACKAGE都可以启动应用的安装。

PackageInstallerActivity根据用户设置(比如是否允许安装未知来源的应用等)进行一些检查,同时查询应用需要的权限进行显示,在用户点击“安装”以后,启动界面InstallAppProgress进行安装。这里整个流程比较简单,不再贴代码进行分析了,不过其中获取应用信息、获取权限信息的调用可以学习一下。

在流程的最后,调用了PMS.installPackageWithVerificationAndEncryption()正式进入安装流程

PMS安装流程

通过context.getPackageManager得到的PackageManager实际上是ApplicationPackageManager的实例,所以调用到的就是

/**
* packageURI -- 要安装的apk的uri
* obesrver -- 安装过程的观察者
* flags -- 这个流程里,表示是否覆盖安装,当然其他流程中会有其他值
* installerPackageName -- 安装者的包名
* verificationParams -- 一些校验参数,后续流程中看它的具体使用
* encryptionParams -- 一些加密参数,这个流程里是null
**/
@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
        IPackageInstallObserver observer, int flags, String installerPackageName,
        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
    installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
            installerPackageName, verificationParams, encryptionParams);
}

installCommon在校验参数之后调用了PMS.installPackage,然后调用到PMS.installPackageAsUser

@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride, int userId) {
    // 主要做几个事情,1、检查安装者权限;2、更新flag;3、创建UserHandle实例;4、创建OriginInfo
    // ...
    // 这里的Handler是内部类PackageHandler
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
            null, verificationParams, user, packageAbiOverride, null);
    mHandler.sendMessage(msg);
}

PackageHandler的message处理方法如下,这里先仅列出两个事件的处理

void doHandleMessage(Message msg) {
   switch (msg.what) {
       case INIT_COPY: {
           HandlerParams params = (HandlerParams) msg.obj;
           int idx = mPendingInstalls.size();
           if (!mBound) {
               if (!connectToService()) {
                   params.serviceError();
                   return;
               } else {
                   mPendingInstalls.add(idx, params);
               }
           } else {
               mPendingInstalls.add(idx, params);
               if (idx == 0) {
                   mHandler.sendEmptyMessage(MCS_BOUND);
               }
           }
           break;
       }
       case MCS_BOUND: {
           if (msg.obj != null) {
               mContainerService = (IMediaContainerService) msg.obj;
           }
           if (mContainerService == null) {
               if (!mBound) {
                    // 异常情况处理
               }
           } 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) {

                               removeMessages(MCS_UNBIND);
                               Message ubmsg = obtainMessage(MCS_UNBIND);
                               sendMessageDelayed(ubmsg, 10000);
                           }
                       } else {
                           mHandler.sendEmptyMessage(MCS_BOUND);
                       }
                   }
               }
           } else {
                // 异常情况,打印log
           }
           break;
       }
   }
}

仔细看一下,安装过程是需要调用IMediaContainerService mContainerService的,在第一次调用到INIT_COPY的时候还没有bindService,所以mBound是false,mContainerService的是空,所以要先调用connectToService,然后把安装参数add到pending队列,在bindService成功后,会发送MCS_BOUND事件,mContainerService会被赋值为DefaultContainerService.IMediaContainerService.Stub实例,然后开始处理pending队列。

真正的处理从InstallParams.startCopy()开始,依次调用handleStartCopy()和handleReturnCode()

final boolean startCopy() {
    boolean res;
    try {
        if (++mRetries > MAX_RETRIES) {
            // 超过重试次数
            return false;
        } else {
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        // ...
    }
    handleReturnCode();
    return res;
}
public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;

    // If we're already staged, we've firmly committed to an install location
    i
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值