开发者社区> mogqp3unktbnq> 正文

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

简介: 目录 App安装过程 PackageInstallerActivity onCreat() onResume() bindUi() startInstall() InstallInstalling onCreat() onStart() onResume() InstallingAsyncTask PackageInstaller.commit() PackageInstallerSession commit() 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。


   

   


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
29203 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
16480 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20715 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
14904 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
22364 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23588 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
36470 0
201
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载