❤️Android 从源码解读 Apk 的安装过程 ❤️(上)

简介: 目录App安装过程PackageInstallerActivityonCreat()onResume()bindUi()startInstall()InstallInstallingonCreat()onStart()onResume()InstallingAsyncTaskPackageInstaller.commit()PackageInstallerSessioncommit()dispatchStreamValidateAndCommit()mHandler()handleStreamValidateAndCommit()


App安装过程


源码:Android SDK 30


    当我们点击某一个 App 安装包进行安装时,首先会弹出一个系统界面指示我们进行安装操作。这个界面是 Android Framework 中预置的一个 Activity>PackageInstallerActivity.java。


PackageInstallerActivity

       frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java


/**
 * This activity is launched when a new application is installed via side loading
 * The package is first parsed and the user is notified of parse errors via a dialog.
 * If the package is successfully parsed, the user is notified to turn on the install unknown
 * applications setting. A memory check is made at this point and the user is notified of out
 * of memory conditions if any. If the package is already existing on the device,
 * a confirmation dialog (to replace the existing package) is presented to the user.
 * Based on the user response the package is then installed by launching InstallAppConfirm
 * sub activity. All state transitions are handled in this activity
 */
public class PackageInstallerActivity extends AlertActivity {
      ...
}


     看到 Activity 当然是先从 onCreate() 开始了


onCreat()


public class PackageInstallerActivity extends AlertActivity {
    @Override
    protected void onCreate(Bundle icicle) {
        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
        super.onCreate(null);
        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }
        //mPm = PackageManagerService
        mPm = getPackageManager();
        //mIpm = IPackageManager
        //将原始接口返回给包管理器。
        mIpm = AppGlobals.getPackageManager();
        //AppOpsManager 涵盖了从帮助运行时权限访问控制和跟踪到电池消耗跟踪的各种功能。
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        //提供在设备上安装、升级和删除应用程序的能力。
        mInstaller = mPm.getPackageInstaller();
        //在多用户系统上管理用户和用户详细信息
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
        final Intent intent = getIntent();
        ...
        //安装包Uri
        final Uri packageUri;
        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
            //获取android.content.pm.extra.SESSION_ID
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            //获取安装Session的详细信息
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                //获取SessionInfo失败退出安装
                finish();
                return;
            }
            mSessionId = sessionId;
            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            packageUri = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }
        if (packageUri == null) {
            //获取packageUri失败退出安装
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            finish();
            return;
        }
        //解析 Uri 并为此包设置安装程序。
        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }
    }
}


 在 onCreate() 方法中,首先执行一些初始化操作,获取PackageManager和PackageInstaller、UserManager等对象,根据当前Intent的信息最一些逻辑判断并弹出消息弹窗,然后获取一些安装包信息。接下来咱们安装 Activity 的生命周期往下看 onStart() 没有重写,咱继续往下看


onResume()


    @Override
    protected void onResume() {
        super.onResume();
        //processPackageUri已拿到mAppSnippet
        if (mAppSnippet != null) {
            // 在禁用确定按钮的情况下加载虚拟布局,直到我们覆盖此布局
            // 开始安装确认
            bindUi();
            // 检查是否允许安装包,如果允许则启动安装。 
            //如果不允许显示适当的对话框。
            checkIfAllowedAndInitiateInstall();
        }
        if (mOk != null) {
            mOk.setEnabled(mEnableOk);
        }
    }


bindUi()


    private void bindUi() {
        ...
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        ...
                            startInstall();
                        ...
                    }
                }, null);
        ...
    }


这里点击安装按钮进去 startInstall() 方法,失败的情况咱就不看了,继续往下走着。


startInstall()


    private void startInstall() {
        // 启动子Activity,安装应用程序
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
        newIntent.setClass(this, InstallInstalling.class);
        ...
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        startActivity(newIntent);
        finish();
    }


startInstall 调用即使启用了一个新的Activity–>InstallInstalling。会把安装包的信息通过 Intent 传递到 InstallInstalling 这个 Activity。


InstallInstalling

       frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java


/**
 * Send package to the package manager and handle results from package manager. Once the
 * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
 * <p>This has two phases: First send the data to the package manager, then wait until the package
 * manager processed the result.</p>
 */
public class InstallInstalling extends AlertActivity {
      ...
}


在这个页面点击安装时,InstallInstalling 的作用主要是向 PMS 发送包信息以及处理回调。 这有两个阶段:


  • 首先将数据发送给 PackageManager ;


  • 等待 PackageManager 处理结果(InstallSuccess 或 InstallFailed)。


       看到 Activity 当然是先从 onCreate() 开始了


onCreat()


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        //要安装的包的 URI
        mPackageURI = getIntent().getData();
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                //注释1:安装现有包
                getPackageManager().installExistingPackage(appInfo.packageName);
                //安装成功页面
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                //安装失败页面
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
             //注释2:没有现有包
             //根据 mPackageURI 创建一个对应的 File
            final File sourceFile = new File(mPackageURI.getPath());
            //显示应用信息 icon,应用名
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
            mAlert.setIcon(as.icon);
            ...
            if (savedInstanceState != null) {
                //咱们第一次仅savedInstanceState==null
                //安装包的会话 ID
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                //我们等待的安装事件的 ID
                mInstallId = savedInstanceState.getInt(INSTALL_ID);
                //重新登记结果;如果结果是在 Activtiy 被销毁时交付的,则可能会立即回调
                try {
                    //添加一个观察者。如果此id已存在事件,请在此调用内回调。
                    // 向 InstallEventReceiver 注册一个观察者
                    // InstallEventReceiver 是一个 BroadcastReceiver,可以通过 EventResultPersister 接收到所有的安装事件
                    // 这里事件会回调给 this::launchFinishBasedOnResult
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                //咱们第一次仅savedInstanceState==null
                //创建 SessionParams,它用来携带会话的参数
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.setInstallAsInstantApp(false);
                ...
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);
                File file = new File(mPackageURI.getPath());
                try {
                    // 对 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));
                } catch (PackageParser.PackageParserException e) {
                    ...
                } catch (IOException e) {
                    ...
                }
                try {
                    // 向 InstallEventReceiver 注册一个观察者返回一个新的 mInstallId
                    // InstallEventReceiver 是一个 BroadcastReceiver,可以通过 EventResultPersister 接收到所有的安装事件
                    // 这里事件会回调给 this::launchFinishBasedOnResult
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
                try {
                    //PackageInstaller 的 createSession 
                    //方法内部会通过 IPackageInstaller 与 PackageInstallerService进行进程间通信,
                    //最终调用的是 PackageInstallerService 的 createSession 方法来创建并返回 mSessionId
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }
            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
            //监听会话的更改并更新进度条
            mSessionCallback = new InstallSessionCallback();
        }
    }


NameNotFoundException:当找不到给定的包、应用程序或组件名称时,将引发此异常。


       在 onCreate() 方法中,首先执行一些初始化操作,比如获取 mInstallId、mSessionId以及监听回调 mSessionCallback 。接下来看看 onStart() 。


onStart()


    @Override
    protected void onStart() {
        super.onStart();
        //设置回话监听回调
        getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
    }


onResume()


    @Override
    protected void onResume() {
        super.onResume();
        //This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            //PackageInstaller:提供在设备上安装、升级和删除应用程序的能力。
            //这包括支持打包为单个“整体”APK 的应用程序或打包为多个“拆分”APK 的应用程序。
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            //安装Session的详细信息
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
            if (sessionInfo != null && !sessionInfo.isActive()) {
                //创建mInstallingTask并启动
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // 安装完成后我们会收到广播
                //mCancelButton不允许点击
                mCancelButton.setEnabled(false);
                //Activity.setFinishOnTouchOutside():设置此活动在其窗口边界外触摸
                setFinishOnTouchOutside(false);
            }
        }
    }


InstallingAsyncTask


    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String)}
     */
    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
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
                return null;
            }
            //设置暂存此会话的当前进度(0-1z之间)。
            session.setStagingProgress(0);
            try {
                File file = new File(mPackageURI.getPath());
                //输入流in
                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    //打开一个流以将 APK 文件写入会话。
                    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;
                            }
                            //将 APK 文件通过 IO 流的形式写入到 PackageInstaller.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) {
                //包已下载好,设置安装提交广播
                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                broadcastIntent.setPackage(getPackageName());
                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());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }
    }


其他没啥好看的,咱么看看具体的session.commit


PackageInstaller.commit()


        public void commit(@NonNull IntentSender statusReceiver) {
            try {
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }


mSession 就是 IPackageInstallerSession 。这说明要通过 IPackageInstallerSession 来进行进程间的通信,最终会调用PackageInstallerSession 的 commit 方法


PackageInstallerSession

       frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java


public class PackageInstallerSession extends IPackageInstallerSession.Stub {
      ...
}


commit()


    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + mParentSessionId +  " and may not be committed directly.");
        }
        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        //return true 多个包
        if (isMultiPackage()) {
            ...
        }
        dispatchStreamValidateAndCommit();
    }


dispatchStreamValidateAndCommit()


    private void dispatchStreamValidateAndCommit() {
        // 通过 Handler 处理消息事件
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }


mHandler()


    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
                ...
            }
            return true;
        }
    };


handleStreamValidateAndCommit()


    private void handleStreamValidateAndCommit() {
        PackageManagerException unrecoverableFailure = null;
        // 这将跟踪会话和任何子项是否已通过验证并准备好进入下一安装阶段
        boolean allSessionsReady = false;
        try {
            allSessionsReady = streamValidateAndCommit();
        } catch (PackageManagerException e) {
            unrecoverableFailure = e;
        }
        //多包,不是多包直接过
        if (isMultiPackage()) {
            ....
        }
        if (!allSessionsReady) {
            return;
        }
        // 通过 Handler 处理MSG_INSTALL消息事件
        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }


mHandler()


    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                ...
                case MSG_INSTALL:
                    handleInstall();
                    break;
                ...
            }
            return true;
        }
    };


handleInstall()


    private void handleInstall() {
        ...
        //return true 会话被再次启用。
        if (params.isStaged) {
            mStagingManager.commitSession(this);
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //安装 APEX 包,则返回 true。
        if (isApexInstallation()) {
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                    "APEX packages can only be installed using staged sessions.", null);
            return;
        }
        // 对于 multiPackage 会话,读取锁外的子会话,
        // 因为读取持有锁的子会话可能会导致死锁 (b/123391593)。
        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
        try {
            synchronized (mLock) {
                //接下来咱们就看看这个方法。
                installNonStagedLocked(childSessions);
            }
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
            destroyInternal();
            dispatchSessionFinished(e.error, completeMsg, null);
        }
    }


installNonStagedLocked()


    @GuardedBy("mLock")
    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
        final PackageManagerService.ActiveInstallSession installingSession =
                makeSessionActiveLocked();
        if (installingSession == null) {
            return;
        }
        //return true 多个包
        if (isMultiPackage()) {
            List<PackageManagerService.ActiveInstallSession> installingChildSessions =
                    new ArrayList<>(childSessions.size());
            ...
            mPm.installStage(installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }


这里不管不管怎么样都会调用 mPm.installStage(installingChildSessions);


     mPm 就是系统服务 PackageManagerService。


 

 


相关文章
|
3天前
|
JSON 编译器 开发工具
VS Code阅读Android源码
VS Code阅读Android源码
9 1
|
22天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
51 1
|
23天前
|
Java Android开发
Android反编译查看源码
Android反编译查看源码
25 0
|
2天前
|
Linux 开发工具 Android开发
Docker系列(1)安装Linux系统编译Android源码
Docker系列(1)安装Linux系统编译Android源码
4 0
|
1月前
|
Shell 开发工具 Android开发
ADB 下载、安装及使用教程:让你更好地管理 Android 设备
ADB 下载、安装及使用教程:让你更好地管理 Android 设备
508 2
|
1月前
|
定位技术 API 数据库
基于Android的在线移动电子导航系统的研究与实现(论文+源码)_kaic
基于Android的在线移动电子导航系统的研究与实现(论文+源码)_kaic
|
1月前
|
搜索推荐 测试技术 定位技术
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
|
1月前
|
Java 关系型数据库 应用服务中间件
基于Android的人事管理系统设计与实现(论文+源码)_kaic
基于Android的人事管理系统设计与实现(论文+源码)_kaic
|
1月前
|
设计模式 测试技术 数据库
基于Android的食堂点餐APP的设计与实现(论文+源码)_kaic
基于Android的食堂点餐APP的设计与实现(论文+源码)_kaic
|
1月前
|
网络协议 关系型数据库 MySQL
安卓手机termux上安装MariaDB数据库并实现公网环境下的远程连接
安卓手机termux上安装MariaDB数据库并实现公网环境下的远程连接