抖音字体大小设置方法安卓 抖音字体大小设置方法(14)


但在实际执行过程中,这种完全进行按需注入会比较困难,因为我们无法精确掌握插件加载时机,比如我们的可能通过是通过 compileonly 的方式隐式的依赖、加载插件的类,也可能在 xml 中使用某个插件的 view 的方式触发插件的加载,如果要进行适配会对业务开发带来比较大的侵入 。
这里尝试换一个思路进行优化——我们虽然没法精确地知道插件加载时机,但却可以知道哪里没有插件加载 。比如 Application 阶段是没有插件加载的,那么完全可以等 Applicaiton 阶段执行完成再进行 DelegateClassloader 的注入 。事实上在启动过程中,类的加载主要集中在 Application 阶段,通过在 Applicaiton 执行完成再去进行 DelegateClassloader 进行注入,可以极大地减少插件化方案对启动速度的影响,同时也可以避免对业务的侵入 。
侵入式优化方案:改造 ClassLoader 模型
上面的方案无需侵入业务改造成本很小,但是它只是优化了 Application 阶段的类加载,后续阶段 ART 对类加载的优化仍然无法享受到,从极致性能的角度我们做了进一步的优化 。我们优化的核心思想就是把 DelegateClassloader 从 PathClassLoader 和 BootClassLoader 之间彻底去除掉,通过其他方式来解决宿主加载插件类的问题 。通过分析我们可以知道宿主加载插件的类主要有几种方式:
通过 Class.forName 的方式去反射加载插件的类;通过 compileOnly 隐式依赖插件的类,运行时直接加载插件的类;启动插件的四大组件时加载插件的组件类;在 xml 中使用插件的类;
因此我们的问题就变成了在不注入 ClassLoader 的情况下,如何实现宿主加载插件的这四大类 。
首先是Class.forName 的方式,解决这种方式下插件类加载的问题最直接的解决办法就是调用 Class.forName 时显示的去指定 ClassLoader 为 DelegateClassloader,不过这样的方式对业务开发不够友好,且存在一些三方 sdk 中代码我们无法修改的问题 。我们最终的解决办法就是对 Class.forName 调用进行字节码插桩,在类加载失败时再尝试使用 DelegateClassloader 去进行加载 。
接下来是compileOnly 的隐式依赖,这种方式比较难进行通用处理,因为我们无法找到一个合适的时机去对类加载失败进行兜底 。针对这个问题我们的解决办法就是进行业务的改造,将 compileOnly 的隐式依赖调用的方式改成通过 Class.forName 的方式,之所以进行这样的改造主要是基于几下几点考虑:
首先抖音中 compileOnly 隐式依赖调用的方式非常少,改造成本相对可控;其次 compileOnly 的方式在插件的使用上虽然便捷,但是它在入口上不够收敛,在插件加载管控、问题排查、插件宿主版本间兼容上都存在一定的问题,通过 Class.forName + 接口化的方式可以较好的解决这些问题 。
插件四大组件类的加载和 xml 中使用插件类的问题都可以通过同一个方案来解决——将 LoadedApk 中的 ClassLoader 替换为DelegateClassLoader,这样无论是四大组件 class 的加载还是 LayoutInflate 加载 xml 时的 class 加载都会使用 DelegateClassLoader 加载,关于这部分的原理大家可以参考 DroidPlugin、Replugin 等相关插件化原理解析,这里就不展开介绍了 。

抖音字体大小设置方法安卓 抖音字体大小设置方法


3.1.2 Class verify 优化
对于 ClassLoader 的优化,优化的是类加载过程中的 load 阶段,对于类加载的其他阶段也可以进行一定的优化,比较典型的一个案例就是classverify的优化,classverify 过程主要是校验 class 是否符合 java 规范,如果不符合规范则会在 verify 阶段抛出 verify 相关的异常 。
一般情况下 Android 中的 class 在应用安装或插件加载时就会进行 verify,但是存在一些特定 case,比如 Android10 之后的插件、插件编译采用 extract filter 类型、宿主与插件相互依赖导致静态 verify 失败等情况,则需要在运行时进行 verify 。运行 verify 的过程除了会校验 class,还会触发它所依赖 class 的 load,从而造成耗时 。

推荐阅读