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


在线下情况下我们可以通过对 logcat 中“Start proc”等关键字进行过滤,去发现是否存在启动阶段启动子进程的情况,以及获得触发子进程启动的组件信息 。对于一些复杂的工程或者是三方 sdk,我们即使知道了启动进程的组件,也比较难定位到具体的启动逻辑,我们可以通过对 startService、bindService 等启动Service、Recevier、ContentProvider组件调用进行插桩,输入调用堆栈的方式,结合“Start proc”中组件的去精准定位我们的触发点 。除了在 manifest 中生命的进程可能还存在一些 fork 出 native 进程的情况,这种进程我们可以通过adb shell ps的方式去进行发现 。

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


2.2 GC 抑制
后台任务影响启动速度中还有还有另一个比较典型的 case 就是 GC,触发 GC 后可能会抢占我们的 cpu 资源甚至导致我们的线程被挂起,如果启动过程中存在大量的 GC,那么我们的启动速度将会受到比较大的影响 。
解决这个问题的一个方法就是减少我们启动阶段代码的执行,减少内存资源的申请与占用,这个方案需要我们去改造我们的代码实现,是解决 gc 影响启动速度的最根本办法 。同时我们也可以通过 GC 抑制的通用办法去减少 GC 对启动速度的影响,具体来说就是在启动阶段去抑制部分类型的 GC,以达到减少 GC 的目的 。
近期公司的 Client Infrastructure-App Health 团队调研出了 ART 虚拟机上的 GC 抑制方案,在公司的部分产品上尝试对应用的启动速度有不错的优化效果,详细的技术细节在后续打磨完成后将会在“字节跳动终端技术”公众号分享出来 。
3. 全局优化
前面介绍的案例基本都是针对某个阶段一些比较耗时点的优化,实际上我们还存在一些单次耗时不那么明显,但是频率很高可能会影响到全局的点,比如我们业务中的高频函数、比如我们的类加载、方法执行效率等,这里我们将对抖音在这些方面的优化尝试做一些介绍 。
3.1 类加载优化3.1.1 ClassLoader 优化
首先我们来看一下抖音在类加载方面的一个优化案例 。谈到类加载我们就离不开类加载的双亲委派机制,我们简单回顾一下这种机制下的类加载过程:
首先从已加载类中查找,如果能够找到则直接返回,找不到则调用 parent classloader 的 loadClass 进行查找;如果 parent clasloader 能找到相关类则直接返回,否则调用 findClass 去进行类加载;protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
c = findClass(name);
}
}
return c;
}
Android 中的 ClassLoader
双亲委派机制中很重要的一个点就是 ClassLoader 的父子关系,我们再来看一下 Android 中 ClassLoader 情况 。一般情况下 Android 中有两个 ClassLoader,分别是 BootClassLoader 和 PathClassLoader,BootClassLoaderart 负责加载 android sdk 的类,像我们的 Activity、TextView 等都由 BootClassLoader 加载 。PathClassLoader 则负责加载 App 中的类,比如我们的自定义的 Activity、support 包中的 FragmentActivity 这些会被打进 app 中的类则由 PathClassLoader 进行加载 。BootClassLoader 是 PathClassLoader 的 parent 。
ART 虚拟机对类加载的优化
ART 虚拟机在类加载方面仍然遵循双亲委派的原则,不过在实现上做了一定的优化 。一般情况下它的大致流程如下:
首先调用 PathClassLoader 的 findLoadedClass 方法去查找已加载的类中查找,这个方法将会通过 jni 调用到 ClassLinker 的 LookupClass 方法,如果能够找到则直接返回;在已加载类中找不到的情况下,不会立刻返回到 java 层,其会在 native 层去调用 ClassLinker 的 FindClassInBaseDexClasLoader 进行类查找;在 FindClassInBaseDexClasLoader 中,首先会去判断当前 ClassLoader 是否为 BootClassLoader,如果为 BootClasLoader 则尝试从当前 ClassLoader 的已加载类中查找,如果能够找到则直接返回,如果找不到则尝试使用当前 ClassLodaer 进行加载,无论能否加载到都返回;如果当前 ClassLoader 不是 BootClassLoader,则会判断是否为 PathClasLoader,如果不是 PathClassLoader 则直接返回;如果当前 ClassLoader 为 PathClassLoader,则会去判断当前 PathClassLoader 是否存在 parent,如果存在 parent 则将 parent 传入递归调用 FindClassInBaseDexClasLoader 方法,如果能够找到则直接返回;如果找不到或者当前 PathClassLoader 没有 parent 则直接在 native 层通过 DexFile 直接进行类加载 。

推荐阅读