深入学习Android:虚拟机&运行时
经常在知乎上看到一些“如何深入学习Android”之类的问题,我也曾经写过一些答案:
也有很多别的优秀的回答,但是我总觉得少了点什么。相比于调像素改颜色做动画,了解一些系统原理性的知识确实已经深入一步了,但是这还远远不够:我们写的Apk运行在虚拟机上,而虚拟机又运行在Linux内核上,如果就在虚拟机给我们提供的温室里面转悠,怎么能算是深入理解呢?
打个比方,谈到Android View系统的原理,很多人或许会想到,布局解析流程,View绘制流程,事件传递机制,动画实现原理,等等。但是,从根本上来讲,我们App的Activity界面,是如何呈现在用户面前的?那些个View,又是如何渲染到屏幕上的呢?我们事件的源头是Window,那Window的事件又是怎么来的?还有,我们经常讲Binder通信机制,我也写过一些文章(Binder学习指南)这些讲解很多都只说明了通信的过程和大致原理,至于数据是如何从一个进程传输到另外一个进程的,这些细节我们都不清楚。
说到这里,已经很清楚了:如果仅仅学习了Android Framework的Java层,并不能算作是“深入Android”,必须深入native层,才能把握整个系统的来龙去脉。众所周知,Android开发已经趋于饱和,而Android系统上技术的应用也已经进入深水区,如要想提升自己的核心竞争力,必须花心思、花时间,去学习Android的虚拟机 & 运行时。
我相信,有很多小伙伴跟踪Android Framwork源码的时候跟着跟着就翻车了——一个native方法挂在那,并不知道里面干了些什么。其实,你离系统真正的原理之间只有一步之遥,就如native和naive一样;native方法有什么可怕的呢?照跟不误。下面,我给出一些我的学习路径和学习经验吧,希望能有帮助。
- Dex文件格式以及dalvik虚拟机指令。那些知名的反编译工具smali/baksmali, apktool, dex2jar, jdgui, jadx, jeb...它们是如何工作的,原理是什么?学完Dex文件格式以及dalvik虚拟机指令,再结合开源项目 jadx ,你会发现自己写一个反编译工具也并不是什么难事。
- 类加载。也许你知道ClassLoader机制,了解双亲委派模型,但那不过是皮毛而已。你写的代码被打包进Apk之后,系统是如何把这个类加载到内存中并使用的?一个Apk中数万个方法,查找会有效率问题吗,如何提高查找以及加载速度?ClassNotFoundException,NoClassDefFoundError是怎么发生的,他们有啥区别?QQ空间的插件机制preverify又是什么,redex为什么可以优化启动速度?
- 进程管理 & 进程通信。当你按下电源键开机的时候,发生了什么?当你点击桌面App图标的时候又发生了什么?各式各样的系统服务是如何工作的?系统杀进程的时候发生了什么?从一个App切换到另外一个App又发生了什么?进程优先级又是如何确定的?也许你通过学习Java层的framework可以对这些问题说出个一二,但这些最终实现是在虚拟机里面,在这里你可以揭开它最神秘的面纱。
- 线程调度 你在Java层new Thread的时候发生了什么?`Process.setThreadPriority` 与 `Thread.setPriority`有什么区别?虚拟机线程与Linux内核线程有什么关系,如何协调?如何让一个线程获取更多的调度时间?
- 内存管理 包括内存分配和垃圾回收(GC);创建一个对象的时候,背后发生了什么?Java层可以操作内存嘛?Unsafe的原理又是啥?OOM是如何发生的,如何避免内存泄漏?当我们说对象已死的时候我们在说什么?作为一个自动内存管理的平台,谈性能优化怎么能不谈GC?GC会对应用程序的启动有多大影响,如何减少甚至避免GC?
- 反射机制 一个对象是如何知道自己是谁,拥有哪些成员和方法的?为什么大家都说避免使用反射,反射的开销真的很大吗?
- 本地接口(JNI) 我们使用native函数,那么Java层到底是如何与C/C++通信的?我们在framework里面见到的Java类里面大量的`long mObject`有什么作用(比如AssetManager/DisplayEventReceiver) ?native方法和别的方法调用有何不同,开销如何呢?加密放so里就真的安全了?
- 虚拟机执行流程 这里面的知识就相当有意思了,我们写的代码归根结底是如何执行的?在Android上调用一个方法的开销有多大?为什么感觉TraceView不准?ART虚拟机上的那个AOT又是什么,为什么要这么做?即时编译又是个啥,为什么dexposed对ART支持不好?热更新在Android7上又会遇到什么困难?如何实现native层的Hook?等等等等。
上面列举了一些关键词,你可以把这些关键词拿去google,然后自己对着源码寻找答案;也许你对C++很陌生,那么就先了解一下C++的知识,特别智能指针,模版(不然根本看不懂)。如果你依然感觉陌生和恐惧,建议阅读一下《汇编语言》以及《CSAPP》这两本书,消除一下神秘感和陌生感;当然,我个人不太赞同未雨绸缪,既然决定了,天亮就出发吧,一边学一边打基础,然后真正滴”深入”Android ^_^,如果依然有问题,欢迎与我交流 & 讨论。
附参考资料:
- 《汇编语言(第3版)》 王爽【摘要 书评 试读】图书 这本书非常不错,作者写书的理念我非常认同,真正的循循善诱,循序渐进,学习起来非常舒服。看这本书并不意味着你真的要去写汇编,读汇编,而是培养一种底层的思维习惯——计算机的原理实际上非常简单和朴素,这本书帮你消除对2进制,指令,数据,寄存器等的陌生感和恐惧感。
- 《C++ Primer(中文版)(第5版)》 斯坦利·李普曼 (Stanley B. Lippman), 约瑟·拉乔伊 (Josee Lajoie), 芭芭拉·默 (Barbara E. Moo), 王刚, 杨巨峰【摘要 书评 试读】图书 嗯,要学底层,怎么能不懂C++呢?我觉得这是每一个程序员必须学习的一门语言,不懂C/C++,永远无法深入底层。
- Dex文件格式
- Dalvik虚拟机指令
- JNI Tips
- 《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》 周志明【摘要 书评 试读】图书
- Dalvik源码
- ART源码
- 《Android Dalvik虚拟机结构及机制剖析(第2卷):Dalvik虚拟机各模块机制分析》 吴艳霞, 张国印【摘要 书评 试读】图书 这本书怎么说呢?dalvik已经成为历史,而且书中措辞和用语也有很多不恰当的地方甚至有谬误,但还是一本不错的入门书。虽然dalvik已经废弃,但是dalvik指令集长存,并且dalvik的实现相对简单,可以先从dalvik入手来学习Android的运行时。
- 读源码mac可以选择understand,当然AndroidXRef 也很不错,但是它的回退和历史功能堪忧,还是习惯understand/sourceinsight.