androiod 学习--PMS应用安装过程

来源:互联网 发布:类似blued的软件 编辑:程序博客网 时间:2024/06/05 19:37

         前面,我们已经分析了,PMS我们已经分析了,PackageManagerService整个的启动过程,对android应用管理大概有一个大概的了解,实际开发中可以有一个更深入的理解,前面讲的只是一个管理大概的流程,以及数据如何存储,存储大部分都是用hashmap来存储的,这样存储也是为了查找方便,不过唯一不好的就是浪费空间。所以android的速度提升了,不过rom运行大小也增大了。

       费话少说了,我们还是继续分析应用的安装过程。一般应用安装,我们都是获取PackageManager 然后调用installPackage方法,不过PackageManager真正的实现类是ApplicationPackageManager,我们看一下具体的实现:

 public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,                               String installerPackageName) {        try {            mPM.installPackage(packageURI, observer, flags, installerPackageName);        } catch (RemoteException e) {            // Should never happen!        }    }
  这里也只是调用PMS去实现安装,所以要分析安装的过程,我们也还是要分析PMS里面的installPackage方法。不过经过各种跳转,最终还是会走到installPackageWithVerificationAndEncryption方法里面去,主要是生成一个InstallParams对象发送一个copy的消息。
final Message msg = mHandler.obtainMessage(INIT_COPY);        msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,                verificationParams, encryptionParams, user);        mHandler.sendMessage(msg);
 InstallParams是继承HandlerParams对象,主要是实现apk复现等其他功能。看Handler关于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;                }
 1.如果没有已经绑定过DefaultContainerService 直接添加到mPendingInstalls队列里面去 再发一个 MCS_BOUND 消息。

 2.没有绑定service,则要进行绑定再添加到mPendingInstalls 队列里面。

 再看一下:

  HandlerParams params = mPendingInstalls.get(0);                        if (params != null) {                            if (params.startCopy()) {                                // We are done...  look for more work or to                                // go idle.                                if (DEBUG_SD_INSTALL) Log.i(TAG,                                        "Checking for more work or unbind...");                                // 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 woek");                                    mHandler.sendEmptyMessage(MCS_BOUND);                                }
取出mPendingInstalls里面的HandlerParams 同时调用params.startCopy()进行复制,主要是有一个重试的机制在里面。总共有4次失败的机会。真正执行复现的方法是handleStartCopy 先执行一个权限等相关的检测,最后调用DeviceStorageMonitorService 方法进行暂时文件复制:

  if (mTempPackage != null) {                            ParcelFileDescriptor out;                            try {                                out = ParcelFileDescriptor.open(mTempPackage,                                        ParcelFileDescriptor.MODE_READ_WRITE);                            } catch (FileNotFoundException e) {                                out = null;                                Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI);                            }                            // Make a temporary file for decryption.                            ret = mContainerService                                    .copyResource(mPackageURI, encryptionParams, out);                            IoUtils.closeQuietly(out);                            packageFile = mTempPackage;                            FileUtils.setPermissions(packageFile.getAbsolutePath(),                                    FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP                                            | FileUtils.S_IROTH,                                    -1, -1);                        } else {                            packageFile = null;                        }                    } else {                        packageFile = new File(mPackageURI.getPath());                    }
 复制完成之后还是设置权限。不要忘记后面还是有一个handleReturnCode方法。再看往下看:

 

  if (packageFile != null) {                        // Remote call to find out default install location                        final String packageFilePath = packageFile.getAbsolutePath();                        pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,                                lowThreshold);                        /*                         * If we have too little free space, try to free cache                         * before giving up.                         */                        if (pkgLite.recommendedInstallLocation                                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {                            final long size = mContainerService.calculateInstalledSize(                                    packageFilePath, isForwardLocked());                            if (mInstaller.freeCache(size + lowThreshold) >= 0) {                                pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,                                        flags, lowThreshold);                            }                            /*                             * The cache free must have deleted the file we                             * downloaded to install.                             *                             * TODO: fix the "freeCache" call to not delete                             *       the file we care about.                             */                            if (pkgLite.recommendedInstallLocation                                    == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {                                pkgLite.recommendedInstallLocation                                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;                            }                        }                    }                } finally {                    mContext.revokeUriPermission(mPackageURI,                            Intent.FLAG_GRANT_READ_URI_PERMISSION);                }            }
暂时文件复制成功之后会调用getMinimalPackageInfo 扫描获取包的信息:

 public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,                long threshold) {            PackageInfoLite ret = new PackageInfoLite();            if (packagePath == null) {                Slog.i(TAG, "Invalid package file " + packagePath);                ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;                return ret;            }            DisplayMetrics metrics = new DisplayMetrics();            metrics.setToDefaults();            PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packagePath, 0);            if (pkg == null) {                Slog.w(TAG, "Failed to parse package");                final File apkFile = new File(packagePath);                if (!apkFile.exists()) {                    ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;                } else {                    ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;                }                return ret;            }            ret.packageName = pkg.packageName;            ret.versionCode = pkg.versionCode;            ret.installLocation = pkg.installLocation;            ret.verifiers = pkg.verifiers;            ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,                    packagePath, flags, threshold);            return ret;        }
parsePackageLite解析包的信息,这个方法很重要,不过前面已经讲过了,这里就不再分析,解析包之后就可以拿到包的主要信息,还有另外一个方法比较重要:

recommendAppInstallLocation 读取设置里面的信息和apk 本身设置的安装路径,根据剩下空间等因素,确定最终路径.

接下来创建InstallArgs ,createInstallArgs方法会根据不同的安装路径选择生成对象.再调用copyApk方法复制 apk包,lib库等。


最后,再看一下handleReturnCode方法:主要是做扫尾工作,删除临时文件,不过最重要的还是processPendingInstall方法:

 第一.扫描apk,解析然后把数据充分(把应用的相关信息添加到 Settings里面);

 第二.发送POST_INSTALL删除安装过程中生成的临时文件。

  




0 0
原创粉丝点击