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删除安装过程中生成的临时文件。
- androiod 学习--PMS应用安装过程
- androiod 学习--应用管理PackageManagerService
- 我要学习Androiod应用开发
- androiod studio小米真机调试不能安装应用问题解决
- Androiod ListView原理学习
- Androiod源码学习之RecyclerView
- Pms 简单学习
- pms
- Android6.0 PackageManagerService(PMS)-安装
- Android-6.0之PMS安装APK前奏
- Android-6.0之PMS安装APK上篇
- Android-6.0之PMS安装APK下篇
- Android 应用的安装与启动过程(学习笔记)
- Android 应用安装过程
- Android应用安装过程
- PMS管理APP安装到data和禁止卸载列表
- Android应用的安装过程
- Findbug的安装应用过程
- 测试
- 笔试题-6-灯的开关问题
- QT知识点总结
- 不容错过的iOS 8的导航交互
- Svm算法
- androiod 学习--PMS应用安装过程
- iOS—UIImageView绘制圆形图片
- 设置微信分享的标题 缩略图 连接 描述
- android shape的使用(总结二)
- Codeforces 269 div2
- 二. 200多万元得到的创业教训--令人又爱又恨的外包
- .9 图片讲解
- iPhone 6/Plus/5s/三星S5谁的屏幕更好?
- Android自定义控件——开源组件SlidingMenu的项目集成