android PKMS-2 InstallInstalling文件详解

1 概述

InstallInstalling是普通安装流程中,系统启动的第二个Activity
InstallStart封装的数据有
包信息,应用信息 用户id,包括启动进程传来的原始数据信息

2 InstallInstalling都做了什么

作为系统应用中第二个Activity,他同样要遵循生命周期来做一些事情

2.1 onCreate
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //通过INTENT_ATTR_APPLICATION_INFO,获取应用进程(发起安装进程)相关信息
        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        //获取应用进程URI数据
        mPackageURI = getIntent().getData();

        if ("package".equals(mPackageURI.getScheme())) {
            /**
             * case 当前URI的Scheme为package
             * 
             * 过程 跨进程根据应用进程提供的包信息来安装,跳转安装成功activity
             */
            try {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageInstaller.STATUS_FAILURE,
                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
            /**
             * case 当前URI的Scheme不为package
             * 过程
             * 1 创建file对象指定安装包路径
             * 2 封装应用程序相关信息
             * 3 设置提示框相关UI和按钮行为 (用户可以点击取消按钮取消当次安装)
             * 4 检查当前Activity是否保存上次安装操作中的session_id install_id
             *   存在的话添加监听器
             * 5 首先创建会话属性PackageInstaller.SessionParams
             *   检查intent是否携带referrerUri
             *   根据refererUrl是否携带可以判断当次安装资源是本地还是网络下载
             *   当次应用是否为及时应用
             *   设置referrerUri
             *   设置安装包来源URI
             *   设置安装包所属user
             *   解析安装包部分数据
             *   设置安装包名称
             *   设置安装原因
             *   解析安装包获取包名,安装位置,安装大小
             * 
             * 上述提到的这些信息都会保存到我们新创建的会话属性中 PackageInstaller.SessionParams params
             * 
             * 6 根据会话属性创建会话拿到会话id
             */
            final File sourceFile = new File(mPackageURI.getPath());
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);

            mAlert.setIcon(as.icon);
            mAlert.setTitle(as.label);
            mAlert.setView(R.layout.install_content_view);
            mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
                    (ignored, ignored2) -> {
                        if (mInstallingTask != null) {
                            mInstallingTask.cancel(true);
                        }

                        if (mSessionId > 0) {
                            getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                            mSessionId = 0;
                        }

                        setResult(RESULT_CANCELED);
                        finish();
                    }, null);
            setupAlert();
            requireViewById(R.id.installing).setVisibility(View.VISIBLE);

            if (savedInstanceState != null) {
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                mInstallId = savedInstanceState.getInt(INSTALL_ID);

                // Reregister for result; might instantly call back if result was delivered while
                // activity was destroyed
                try {
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
                params.setPackageSource(
                        referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
                                : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);      
                params.setInstallAsInstantApp(false);
                params.setReferrerUri(referrerUri);
                params.setOriginatingUri(getIntent()
                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
                params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                        UID_UNKNOWN));
                params.setInstallerPackageName(getIntent().getStringExtra(
                        Intent.EXTRA_INSTALLER_PACKAGE_NAME));
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);
                File file = new File(mPackageURI.getPath());
                try {
                    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
                    final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                            input.reset(), file, /* flags */ 0);
                    if (result.isError()) {
                        Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
                        Log.e(LOG_TAG,
                                "Cannot calculate installed size " + file + ". Try only apk size.");
                        params.setSize(file.length());
                    } else {
                        final PackageLite pkg = result.getResult();
                        params.setAppPackageName(pkg.getPackageName());
                        params.setInstallLocation(pkg.getInstallLocation());
                        params.setSize(InstallLocationUtils.calculateInstalledSize(pkg,
                                params.abiOverride));
                    }
                } catch (IOException e) {
                    Log.e(LOG_TAG,
                            "Cannot calculate installed size " + file + ". Try only apk size.");
                    params.setSize(file.length());
                }

                try {
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }

                try {
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }

            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
        }
    }

总结:onCreate 生命周期,主要就是为新的安装包创建安装会话

2.2 onResume
    protected void onResume() {
        super.onResume();

        // This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            /**
             * case 安装任务为空
             * 
             * 过程 创建安装异步任务,并且执行异步任务
             */
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

            if (sessionInfo != null && !sessionInfo.isActive()) {
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // we will receive a broadcast when the install is finished
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            }
        }
    }

总结onResume主要就是创建异步任务,执行安装相关操作

3 附加

既然这里提到了异步任务,我们也刚好可以了解一下AyncTask

3.1 概述

AsyncTask 是 Android 提供的一个用于在后台执行异步任务的类。它提供了一种简单的方法来执行后台操作,并在主线程中更新 UI。AsyncTask 是在 Android 中比较常用的工具之一,特别适合于需要在后台执行任务并更新 UI 的场景。

AsyncTask 类包含四个步骤方法:

onPreExecute():在后台任务执行前被调用,通常用于在执行后台任务前做一些初始化操作,例如显示进度条等。
doInBackground(Params...):在后台执行耗时操作,该方法运行在后台线程中,用于执行耗时任务,但不能操作 UI。
onProgressUpdate(Progress...):在调用 publishProgress(Progress...) 后被调用,用于更新任务的执行进度,可以在这个方法中更新 UI。
onPostExecute(Result):在后台任务执行完毕并通过 return 语句返回结果时被调用,用于处理执行结果并更新 UI。

3.2 异步下载任务
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                //首先获取当次安装会话
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
                return null;
            }
            //设置初始进度
            session.setStagingProgress(0);

            try {
                //根据mPackageURI创建file对象
                File file = new File(mPackageURI.getPath());
                //获取输入流读取文件数据写入到内存
                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    //通过session.openWrite方法创建了输出流
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        //设置数据缓存数组
                        byte[] buffer = new byte[1024 * 1024];
                        while (true) {
                            //输入流读取数据
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }
                            //输出流将数据写到session中
                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                //同步每次写入进度
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);
                //出现异常关闭会话
                session.close();

                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                //case 1 安装会话不为空 创建intent对象
                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
                //对intent进行数据封装
                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                //封装包名
                broadcastIntent.setPackage(getPackageName());
                //封装安装id
                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
                //获取待处理的广播intent
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallInstalling.this,
                        mInstallId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
                //这里执行commit操作将IntentSender提交
                /**
                 * IntentSender 用于允许一个应用程序的组件请求另一个应用程序的组件执行操作,
                 * 即使此时两者可能不在运行中。
                 */
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                //失败的话丢弃会话
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);

                if (!isCancelled()) {
                    //执行安装失败通知
                    launchFailure(PackageInstaller.STATUS_FAILURE,
                            PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }
    }
}

总结:当次异步任务主要就是,读取文件数据写到session中,然后构建了广播意图调用session.commit方法

接下来就是普通安装的session处理流程

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容