3. 其它能够黑科技优化的方向
3.1 中心线程绑定大核
3.1.1 界说
中心线程绑定大核的思路也很简单理解,现在的 CPU 都是多核的,大核的频率比小核要高不少,假如咱们的中心线程固定运转在大核上,那么运用功能自然会有所进步。
中心线程指的是 UI 线程、RenderThread 线程,由于它们直观影响用户的感触,或许在具体项目中的其它特定线程,比方语音处理,为了有更快的处理结果,语音线程也是能够列为中心线程的。
3.1.2 检查设备是否有巨细核
-
能够经过/sys/devices/system/cpu/目录下的文件获取各个核的频率
-
尝试了下正在开发的设备,它没有巨细核之分,一切核的频率全都相同,如下:
- 当然,咱们能够将此判别写到代码中,由咱们的 App 智能判别是否需求绑定巨细核,并找出来大核线程是哪个,具体代码这儿就不贴了,原理同上,需求注意下读取权限问题
3.1.3 绑定 CPU 核完成
- 绑定大核是经过函数 sched_setaffinity 完成的。
extern "C" JNIEXPORT void JNICALL Java_com_zj_android_startup_optimize_StartupNativeLib_bindCore(JNIEnv env, *jobject /* this */, jint thread_id, jint core) {
cpu_set_t mask; // CPU 核的集合
CPU_ZERO(&mask); // 将mask置空
CPU_SET(core, &mask); // 将需求绑定的 cpu 核设置给mask,核为序列0,1,2,3……
if (sched_setaffinity(thread_id, sizeof(mask), &mask) == -1) { // 将线程绑核
LOG ("bind thread %d to core %d fail", thread_id, core);
} else {
LOG ("bind thread %d to core %d success", thread_id, core);
}
}
-
如上所示,sched_setaffinity 共有 3 个参数。
- 参数 1 是线程的 id,假如为 0 则表明主线程。
- 参数 2 表明 cpu 序列掩码的长度。
- 参数 3 则表明需求绑定的 cpu 序列的掩码。
-
以上是线程绑定大核的中心代码,能够看到咱们还需求获取 RenderThread 的 id ,以及 cpu 大核的序列。
-
运用中线程的信息记录在 /proc/pid/task 的文件中,经过解析 task 文件就能够获取当前进程的一切线程,而 cpu 大核序列也能够经过解析 /sys/devices/system/cpu 目录完成。
3.2 GC 按捺
3.2.1 什么是 GC 按捺
- 首先 GC,便是 Java 的废物收回,GC 按捺指的是在 App 发动阶段,不让体系做 GC 或许是将 GC 的频频下降,以进步发动速度
- 此技能在 Android10 以上的体系已参加,所以这儿评论的是 在 Android10 以下的体系中添加此功能
3.2.2 Android10 中的 GC 按捺怎么完成的
-
Java 的废物收回机制,在 Android 5.0 之后,ART 替代了 Dalvik,ART 虚拟机在废物收回的时分虽然没有像 Dalvik 相同 stop the world,但在发动阶段假如产生废物收回,GC 线程相同抢占了不少体系资源。
-
Google 也注意到发动阶段 GC 对发动速度的影响,并在 Android 10 之后做了必定的优化,概况可见如下提交:cs.android.com/android/_/a…
- 能够看出,基本思路是在 2s 内进步后台 GC 的阈值,削减发动阶段的 GC 次数,根据 Google 的测验,按捺 GC 后效果如下:
- 能够看出,GC 次数明显削减,发动速度也有必定的进步。
3.2.3 咱们的程序是否有必要进行 GC 按捺
- 能够经过以下代码获取 gc 的次数与耗时,便利统计 gc 对发动耗时的影响,以评估是否有必要做 GC 按捺
Debug.getRuntimeStat("art.gc.gc-count") // gc 次数
Debug.getRuntimeStat("art.gc.gc-time") // gc 耗时
Debug.getRuntimeStat("art.gc.blocking-gc-count") // 堵塞 gc 次数
Debug.getRuntimeStat("art.gc.blocking-gc-time") // 堵塞 gc 耗时
在电视项意图主页检查 GC 的情况,结果如下,发现从发动到主页显示出来,GC 次数和时刻都是比较高的值:
- 别的,我在 profiler 工具中观察到咱们的 GC 线程能够更直观的看到,不只是在发动的时分,后续它也会频频很多的运转,如下:
3.2.4 GC 按捺完成
GC 作业的原理
GC 首要是经过 HeapTaskDaemon 线程完成的,这是一个看护线程,在 Zygote 线程发动后这个线程也就发动了,发动后首要做了以下作业:
- 从 HeapTaskDaemon.runInternal()办法开端一步步骤用到 native 层的 task_processor.RunAllTasks() 办法。
- 当 TaskProcessor 中的 tasks 为空时,会休眠等待,否则会取出第一个 HeapTask 并履行其 Run 办法。
而 HeapTask 的 Run 办法是一个虚函数,需求子类来完成。
class HeapTask : public SelfDeletingTask {
};
class SelfDeletingTask : public Task {
};
class Task : public Closure {
};
class Closure {
public:
virtual ~Closure() { }
// 界说 Run 虚函数
virtual void Run(Thread* self) = 0;
};
HeapTask 便是废物收回的任务,有多个子类,比方最常见的 ConcurrentGCTask 便是其子类,在 Java 内存到达阈值时就会履行这个 Task,用于履行并发 GC。
GC 按捺计划:Native 层的 Hook
在了解了 HeapTaskDaemon 的履行流程之后,咱们想到,假如发动时在 ConcurrentGCTask 的 Run 办法履行前休眠一段时刻,不就能够完成 GC 按捺了吗?
而 Run 办法正好是虚函数,虚函数与 Java 中的抽象函数类似,留给子类去扩展完成多态。
虚函数和外部库函数相同都没法直接履行,需求在表中去查找函数的真实地址,那么咱们是不是能够运用类似 PLT Hook 的思路,运用自界说函数的地址替换原有函数地址,完成 Hook 呢?
答案是必定的,如上图所示,一个类中假如存在虚函数,那么编译器就会为这个类生成一张虚函数表,而且将虚函数表的地址放在目标实例的首地址的内存中。同一个类的不同实例,共用一张虚函数表的。
因此咱们的首要思路如下:
- 发动时将虚函数表中的 Run 函数地址替换为自界说函数地址。
- 在自界说函数内部休眠一段时刻,按捺 GC。
休眠完成后将虚函数表中的函数地址替换回来,避免影响后续履行。
3.3 字节码插桩与功能监控
3.3.1 功能监控的流程
根据功能问题,咱们能够进行一个功能方面的监控,以到达随时了解情况,随时进行优化的意图。商场上有很多商业化的 APM 渠道,比方闻名的 NewRelic,还有国内的 听云、OneAPM 等等,还有咱们自己也有功能监控渠道。这些渠道的作业流程如下:
- 首先在客户端(Android、iOS、Web 等)收集数据;
- 接着将收集到的数据整理上签到服务器;
- 服务器接收到数据后建模、存储、发掘剖析,让后将数据可视化,供用户运用。
其间客户端数据收集时运用字节码插桩比较便利快捷,而且具有较大的通用性
3.3.2 字节码插桩原理
字节码插桩的原理便是在 Android 打包的时分,经过 ASM 等结构将 Java 字节码,插入到特定方位上,到达自动参加某些重复代码的意图,也即是 AOP 编程,如下是 Android 打包的流程:
插桩进口
在打包进程中,会将一切 class 文件,包含第三方的 class 文件打包成一个或许多个 dex 文件。这其间涉及到两个很关键的环节:
javac:将 。java 格局的源代码文件编译成 class 文件;
dex: 将 class 格局的文件打包汇总,组成一个或许多个 dex 文件。
咱们想要对字节码进行修正,只需求在 javac 之后 dex 之前遍历一切的字节码文件,并按照必定的规矩过滤修正就好了,这儿便是字节码插桩的进口。
那么咱们到底怎么介入打包进程,在 class 转换为 dex 文件的时分完成对字节码的修正呢?
答案是 transform api。Android Gradle Plugin 1.5.0 及以上版别,Google 官方提供了 transform api 作为字节码插桩的进口。咱们只需求完成一个自界说的 Gradle Plugin,然后在编译阶段去修正字节码文件即可。
修正字节码
找到了插桩进口,接下来就要对字节码进行修正。对于字节码的修正,比较常用的结构有 Javassist 和 ASM。具体的运用就不进行介绍了,有结构运用的话,写字节码还是比较便利的。
4. 总结
本篇首要介绍了一些 Android 中有用的黑科技,包含 Hook 技能,线程自界说调整,GC 按捺,字节码插桩等,在电视版智家 App9.0 项目中现已验证了部分技能,还有一些技能正在规划中,后续将会逐渐的进步咱们的 App 功能。
最后,评论一个问题,这些黑科技是”奇淫巧技”吗,还是合理合法的运用呢?
这儿引用一篇文章中的原话:
国产定制安卓体系一直都在安卓版别号更新之前,抢先不只一个身位。
以至于每次的安卓大版别更新像是在追授国产定制 Android 在 N 年前魔改的功勋,甚至像是在若干个发行版别选一个最好的计划作为整个 Android 生态的标准。
招安,才是最形象的解释。
国内的 Android 黑科技一直是首先发展的,遍数国内 Android 技能圈走过的旅程,从之前的插件化,到双开等,哪一个在当时不算是”奇淫巧技”呢,最后不都成了 Android 官方的标配了么,所以,斗胆的探索去吧,能处理咱们问题的技能便是好技能。
5. 参阅
- 盘点 Android 常用 Hook 技能
- 怎么高雅封闭 Android 日志输出
- Android 中怎么 Hook 住 JNI 办法
- JNI 函数 Hook 实战
- 发动优化中的一些黑科技,了解一下~
- Android 功能监控系列一(原理篇)
- 怎么点评谷歌刚发布的 AOSP14,在 iOS 和鸿蒙的竞赛下,安卓还有哪些第三方开发的体系亮点值得关注?
6. 团队介绍
「三翼鸟数字化技能渠道-场景规划交互渠道」首要担任规划工具的研制,包含营销规划工具、家电VR规划和展示、水电暖通前置规划能力,研制并沉积素材库,构建家居家装素材库,集成户型库、全品类产品库、规划计划库、生产工艺模型,打造根据户型和风格的AI规划能力,快速生成算量和报价;一起研制了门店规划师中心和项目中心,包含规划师办理能力和项目经理办理能力。完成了场景全生命周期办理,一起为水,空气,厨房等产业提供商机办理工具,从而完成了以场景贯穿的B端C端全流程体系。