IPackageManager.Stub
继承了Binder
,而这个方法是Binder
中的,调用逻辑如下:
// Binder.javaprotected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,int flags) throws RemoteException {if (code == INTERFACE_TRANSACTION) {reply.writeString(getInterfaceDescriptor());return true;} else if (code == DUMP_TRANSACTION) {// 省略部分代码...return true;} else if (code == SHELL_COMMAND_TRANSACTION) {ParcelFileDescriptor in = data.readFileDescriptor();ParcelFileDescriptor out = data.readFileDescriptor();ParcelFileDescriptor err = data.readFileDescriptor();String[] args = data.readStringArray();ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);try {if (out != null) {// 重点?。。〉饔昧?shellCommand 方法shellCommand(in != null ? in.getFileDescriptor() : null,out.getFileDescriptor(),err != null ? err.getFileDescriptor() : out.getFileDescriptor(),args, shellCallback, resultReceiver);}} finally {// 省略部分代码...}return true;}return false;}public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,@Nullable FileDescriptor err,@NonNull String[] args, @Nullable ShellCallback callback,@NonNull ResultReceiver resultReceiver) throws RemoteException {// 这里调用的?。。?onShellCommand(in, out, err, args, callback, resultReceiver);}
所以这里逻辑清晰了,再次整理下逻辑:
- Binder.onTransact收到 SHELL_COMMAND_TRANSACTION 命令会执行 shellCommand方法
- shellCommand方法又调用了onShellCommand方法
- IPackageManager.Stub继承了Binder
- IPackageManagerImpl继承了IPackageManager.Stub并重写了onShellCommand方法
- IPackageManagerImpl的onShellCommand执行了PackageManagerShellCommand相关逻辑
IPackageManager.aidl
,并向其发送 SHELL_COMMAND_TRANSACTION 命令 。得益于Android Binder机制,我们可以在应用进程拿到IPackageManger
的Binder , 并通过它来发送命令 。代码实现如下:
// 执行dex2oatprivate fun performDexOpt() {val args = arrayOf("compile", "-f", "--secondary-dex", "-m",if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "verify" else "speed-profile",context.packageName)executeShellCommand(args)}// IPackageManager.aidl 发送 SHELL_COMMAND_TRANSACTION 命令private fun executeShellCommand(args: Array<String>) {val lastIdentity = Binder.clearCallingIdentity()var data: Parcel? = nullvar reply: Parcel? = nulltry {data = https://www.huyubaike.com/biancheng/Parcel.obtain()reply = Parcel.obtain()data.writeFileDescriptor(FileDescriptor.`in`)data.writeFileDescriptor(FileDescriptor.out)data.writeFileDescriptor(FileDescriptor.err)data.writeStringArray(args)data.writeStrongBinder(null)resultReceiver.writeToParcel(data, 0)getPMBinder().transact(SHELL_COMMAND_TRANSACTION, data, reply, 0)reply.readException()} catch (t: Throwable) {Log.e(TAG,"executeShellCommand error.", t)} finally {data?.recycle()reply?.recycle()}Binder.restoreCallingIdentity(lastIdentity)}
4.反注册Secondary Apk反注册也是执行PackageManagerShellCommand
相关方法,只不过给的参数不一样 。所以大部分逻辑跟第三步是一样的 。代码实现如下:private fun reconcileSecondaryDexFiles() {val args = arrayOf("reconcile-secondary-dex-files", context.packageName)executeShellCommand(args)}
最后,本项目的代码组织情况如下:- DexOpt:外部调用接口,执行DexOpt.dexOpt即可开启dex2oat 。
- ApkOptimizerN:负责Android7-Android9的dex2oat逻辑 。
- ApkOptimizerQ:负责Android10的dex2oat逻辑 。也是本文的讲解重点 。
1.优点
- 插件的加载速度大大增加(实测可以达到90%以上),对插件化框架的冷启动有很大的意义 。
- 代码运行的速度有微小的提升 。测试了跳转Activity、Service这些场景,能够提升20-80ms左右,跟机型有很大的关系 。
- dex2oat产物也会占用一定的存储空间 。所以如果插件更新记得及时删除老的oat文件 。
- dex2oat 执行时间较长,首次还是建议直接加载插件 , 在后台执行dex2oat优化 。
- 部分手机执行后没有成功生成oat文件,还是存在机型兼容问题 。
推荐阅读
- Azure DevOps Server 入门实践与安装部署
- 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践
- 重新整理 .net core 实践篇 ———— linux上排查问题 [外篇]
- .NET API 接口数据传输加密最佳实践
- 重新整理 .net core 实践篇 ———— linux 上线篇 [外篇]
- 鹅长微服务发现与治理巨作PolarisMesh实践-上
- Arctic 基于 Hive 的流批一体实践
- 前端监控系列4 | SDK 体积与性能优化实践
- 2 HTML躬行记——WebRTC基础实践
- AI 【第1篇】人工智能语音测试原理和实践---宣传