当前位置:首页 > 安卓源码 > 技术博客 >

Android开发中应用的安装流程是怎样的呢?

时间:2017-02-13 17:11 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

这段时间在研究插件化相关的技术,追根溯源,所以干脆把Apk的安装流程梳理了一遍,与大家共享,望指正! 本文基于Android 5.1的源码,分析Apk安装流程。 Apk是Android Pakage的缩写,即Android安装包,Apk文件其实是zip格式,一般包含一个或多个dex文件、res

这段时间在研究插件化相关的技术,追根溯源,所以干脆把Apk的安装流程梳理了一遍,与大家共享,望指正!

本文基于Android 5.1的源码,分析Apk安装流程。

Apk是Android Pakage的缩写,即Android安装包,Apk文件其实是zip格式,一般包含一个或多个dex文件、resources.arsc、AndroidManifest.xml、res目录、META-INF目录及包含so库的lib目录,这里就不在啰嗦。

Android开发中应用的安装流程是怎样的呢?

1、安装流程图

先来看一张整体的流程图:

Android开发中应用的安装流程是怎样的呢?

安装过程:复制apk安装包到/data/app目录下,解压并扫描安装包,向资源管理器注入apk资源,解析AndroidManifest文件,并在/data/data目录下创建对应的应用数据目录,然后针对dalvik/art环境优化dex文件,保存到dalvik-cache目录,将AndroidManifest文件解析出的组件、权限注册到PackageManagerService,完成后发送广播。

2、安装时序图

上图太过笼统,不利于了解细节,这里整理出一张时序图,以便于分析。

Android开发中应用的安装流程是怎样的呢?

点击放大查看高清无码大图

说明:时序图中划分为三个部分:PackageInstaller进程、System进程、DefaultContainerService进程,重点关注System进程中的PackageManagerService。

PackageManagerService:PMS是Android中最核心的服务之一,主要负责对系统的apk进行管理,以及对四大组件的管理。

3、流程分析

用户安装Apk时,如从厂商官方应用市场下载,一般无安装页面,这里以用户安装第三方安装包为例进行分析,PackageInstaller应用负责安装及卸载过程与用户交互(见时序图PackageInstaller进程)。这里着重介绍System进程PMS安装流程。

3.1 将apk文件复制至/data/app目录

此流程在时序图中过程还是挺繁复的,中间涉及到比较多的跳转,我们挑重点看:

public class PackageManagerService extends IPackageManager.Stub {
    @Override
    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, VerificationParams verificationParams,
            String packageAbiOverride, int userId) {
        ...
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, observer, installFlags,
                installerPackageName, verificationParams, user, packageAbiOverride);
        mHandler.sendMessage(msg);
    }
}

此处,通过PackageHandler发送INIT_COPY消息准备复制apk,我们再来接着看,

class PackageHandler extends Handler {
    ...
    void doHandleMessage(Message msg) {
        switch (msg.what) {
             case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                    // If a bind was already initiated we dont really
                    // need to do anything. The pending install
                    // will be processed later on.
                    if (!mBound) {
                        // If this is the only one pending we might
                        // have to bind to the service again.
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                            return;
                        } else {
                            // Once we bind to the service, the first
                            // pending request will be processed.
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        mPendingInstalls.add(idx, params);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
             }
             case MCS_BOUND: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
                    if (msg.obj != null) {
                        mContainerService = (IMediaContainerService) msg.obj;
                    }
                    if (mContainerService == null) {
                        ...
                    } else if (mPendingInstalls.size() > 0) {
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            if (params.startCopy()) {
                                // We are done...  look for more work or to
                                // go idle.
                                
                                // Delete pending install
                                if (mPendingInstalls.size() > 0) {
                                    mPendingInstalls.remove(0);
                                }
                                if (mPendingInstalls.size() == 0) {
                                    if (mBound) {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting delayed MCS_UNBIND");
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        // Unbind after a little delay, to avoid
                                        // continual thrashing.
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    // There are more pending requests in queue.
                                    // Just post MCS_BOUND message to trigger processing
                                    // of next pending install.
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Posting MCS_BOUND for next work");
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                        }
                    } else {
                        // Should never happen ideally.
                        Slog.w(TAG, "Empty queue");
                    }
                    break;
             }
        }
        ...
    }
    ...
}

INIT_COPY这条case主要是确保DefaultContainerService已经绑定,DefaultContainerService是一个单独的apk进程,主要提供检查和复制设备上的文件的服务。MCS_BOUND这条case中最关键的是执行params.startCopy()开始拷贝工作。

class InstallParams extends HandlerParams {
    ...
    public void handleStartCopy() throws RemoteException {
        ...
        if (onInt && onSd) {
            ...
        } else {
            pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);
        ...
        }
      
        final InstallArgs args = createInstallArgs(this);
        mArgs = args;

        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            ...
            if (!origin.existing && requiredUid != -1
                        && isVerificationEnabled(userIdentifier, installFlags)) {
                ...
                if (ret == PackageManager.INSTALL_SUCCEEDED
                            && mRequiredVerifierPackage != null) {
                        /*
                         * Send the intent to the required verification agent,
                         * but only start the verification timeout after the
                         * target BroadcastReceivers have run.
                         */
                        verification.setComponent(requiredVerifierComponent);
                        mContext.sendOrderedBroadcastAsUser(verification, getUser(),
                                android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                                new BroadcastReceiver() {
                                    @Override
                                    public void onReceive(Context context, Intent intent) {
                                        final Message msg = mHandler
                                                .obtainMessage(CHECK_PENDING_VERIFICATION);
                                        msg.arg1 = verificationId;
                                        mHandler.sendMessageDelayed(msg, getVerificationTimeout());
                                    }
                                }, null, 0, null, null);

                        /*
                         * We don't want the copy to proceed until verification
                         * succeeds, so null out this field.
                         */
                        mArgs = null;
                    }
            } else {
                /*
                 * No package verification is enabled, so immediately start
                 * the remote call to initiate copy using temporary file.
                 */
                ret = args.copyApk(mContainerService, true);
            }
        }
    }
    ...
}

这部分主要是检查存储空间,权限等,若已有软件包验证程序,则需要等待验证程序检验安装包,否则可直接安装。这里我们直接来看args.copyApk。这里需要提到的是createInstallArgs(this)会根据InstallParams来判断安装位置,这里以内部存储安装为例。

class FileInstallArgs extends InstallArgs {
    ...
    int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            ...
            try {
                final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }

            final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
                @Override
                public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
                    ...
                }
            };

            int ret = PackageManager.INSTALL_SUCCEEDED;
            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                return ret;
            }

            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;
        }
    ...
}

这里完成了apk拷贝及so库的拷贝。

3.2 解析安装包

public class PackageManagerService extends IPackageManager.Stub {
    ...
    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() {
                ...
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        installPackageLI(args, res);
                    }
                    args.doPostInstall(res.returnCode, res.uid);
                }
                ...
            }
        }
    }
    
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        PackageParser pp = new PackageParser();
        ...
        
        final PackageParser.Package pkg;
        try {
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        }
        ...
        if (replace) {
            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, res);
        } else {
            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, res);
        }
        ...
    }
    ...
}

此处对于安装包进行解析,包括解析AndroidManifest版本、权限、组件等,详见PackageParser::parsePackage(tmpPackageFile, parseFlags),这部分代码量较大但流程清晰,这里简单看一下

public class PackageParser {
    ...
    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        if (packageFile.isDirectory()) {
            return parseClusterPackage(packageFile, flags);
        } else {
            return parseMonolithicPackage(packageFile, flags);
        }
    }
    ...
    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
        ...

        final AssetManager assets = new AssetManager();
        try {
            final Package pkg = parseBaseApk(apkFile, assets, flags);
            pkg.codePath = apkFile.getAbsolutePath();
            return pkg;
        } finally {
            IoUtils.closeQuietly(assets);
        }
    }
    ...
    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        ...
        //将资源添加进资源管理
        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
        ...
        try {
            res = new Resources(assets, mMetrics, null);
            ...
            final String[] outError = new String[1];
            //这里解析manifest文件,具体就不在展开,详看请移步源码
            final Package pkg = parseBaseApk(res, parser, flags, outError);
            ...

            return pkg;

        } catch (PackageParserException e) {
            throw e;
        } catch (Exception e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
        }
    }
    ...
}

3.3 检测权限、注册组件

接下来看执行installNewPackageLI函数,这部分代码核心为scanPackageLIupdateSettingsLI

public class PackageManagerService extends IPackageManager.Stub {
    ...
    private void installNewPackageLI(PackageParser.Package pkg,
            int parseFlags, int scanFlags, UserHandle user,
            String installerPackageName, PackageInstalledInfo res) {
        ...

        try {
            PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
                    System.currentTimeMillis(), user);

            updateSettingsLI(newPackage, installerPackageName, null, null, res);
            // delete the partially installed application. the data directory will have to be
            // restored if it was already existing
            if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                // remove package from internal structures.  Note that we want deletePackageX to
                // delete the package data and cache directories that it created in
                // scanPackageLocked, unless those directories existed before we even tried to
                // install.
                deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
                                res.removedInfo, true);
            }

        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
    }
    ...
}

scanPackageLI负责安装,而updateSettingLI则是完成安装后的设置信息更新。如果安装失败则会删除安装包。

我们来看scanPackageLI这部分代码

public class PackageManagerService extends IPackageManager.Stub {
    ...
    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        boolean success = false;
        try {
            final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
                    currentTime, user);
            success = true;
            return res;
        } finally {
            if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
                removeDataDirsLI(pkg.packageName);
            }
        }
    }
    ...
    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        ...
        // writer
        synchronized (mPackages) {
            // 验证已注册的ContentProvider是否有其他同名
            if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
                final int N = pkg.providers.size();
                int i;
                for (i=0; i<N; i++) {
                    PackageParser.Provider p = pkg.providers.get(i);
                    if (p.info.authority != null) {
                        String names[] = p.info.authority.split(";");
                        for (int j = 0; j < names.length; j++) {
                            if (mProvidersByAuthority.containsKey(names[j])) {
                                PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
                                final String otherPackageName =
                                        ((other != null && other.getComponentName() != null) ?
                                                other.getComponentName().getPackageName() : "?");
                                throw new PackageManagerException(
                                        INSTALL_FAILED_CONFLICTING_PROVIDER,
                                                "Can't install because provider name " + names[j]
                                                + " (in package " + pkg.applicationInfo.packageName
                                                + ") is already used by " + otherPackageName);
                            }
                        }
                    }
                }
            }
        }
      
        if (mPlatformPackage == pkg) {
            ...
        } else {
            // This is a normal package, need to make its data directory.
            dataPath = getDataPathForPackage(pkg.packageName, 0);
            if (dataPath.exists()) {
                ...
            } else {
                //invoke installer to do the actual installation
                //这里创建了应用数据目录,用于存放用户数据
                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
                                           pkg.applicationInfo.seinfo);
                ...
            }
          
        }
      
        // We also need to dexopt any apps that are dependent on this library.  Note that
        // if these fail, we should abort the install since installing the library will
        // result in some apps being broken.
        if (clientLibPkgs != null) {
            if ((scanFlags & SCAN_NO_DEX) == 0) {
                for (int i = 0; i < clientLibPkgs.size(); i++) {
                    PackageParser.Package clientPkg = clientLibPkgs.get(i);
                    if (performDexOptLI(clientPkg, null /* instruction sets */, forceDex,
                            (scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {
                        throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
                                "scanPackageLI failed to dexopt clientLibPkgs");
                    }
                }
            }
        }
      
        // writer
        synchronized (mPackages) {
            ...
            // 以下对四大组件进行注册
            int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            for (i=0; i<N; i++) {
                PackageParser.Provider p = pkg.providers.get(i);
                p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        p.info.processName, pkg.applicationInfo.uid);
                mProviders.addProvider(p);
                ...
            }
            ...
        }
    }
    ...
}

scanPackageLI()方法主要逻辑是由scanPackageDirtyLI()实现。这里主要对provider冲突检测,创建应用数据目录,dexopt操作,四大组件注册,权限注册等。

3.4 安装完成

继续看processPendingInstall,安装成功后如需要备份则会通过BackupManagerService进行备份:

public class PackageManagerService extends IPackageManager.Stub {
    ...
    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() {
                ...
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                    IBackupManager bm = IBackupManager.Stub.asInterface(
                            ServiceManager.getService(Context.BACKUP_SERVICE));
                    if (bm != null) {
                        if (DEBUG_INSTALL) Log.v(TAG, "token " + token
                                + " to BM for possible restore");
                        try {
                            if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
                                bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                            } else {
                                doRestore = false;
                            }
                        } catch (RemoteException e) {
                            // can't happen; the backup manager is local
                        } catch (Exception e) {
                            Slog.e(TAG, "Exception trying to enqueue restore", e);
                            doRestore = false;
                        }
                    } else {
                        Slog.e(TAG, "Backup Manager not found!");
                        doRestore = false;
                    }
                }
                if (!doRestore) {
                    // No restore possible, or the Backup Manager was mysteriously not
                    // available -- just fire the post-install work request directly.
                    if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                    mHandler.sendMessage(msg);
                }
                ...
            }
        }
    }
    ...
}

无论备份与否,最终则会通过PackageHandler发送POST_INSTALL消息,最终通过发送Intent.ACTION_PACKAGE_ADDED广播,apk的安装流程就到此结束了。

Android开发中应用的安装流程是怎样的呢? 转载https://www.codesocang.com/appboke/34769.html

技术博客阅读排行

最新文章