从最上层到驱动层看Android Conetext.getSystemService究竟做了什么

曾经好奇为什么同样是binder接口,Conetext.getService为什么可以直接获取,而不需要像bindeService一样异步回调,今天终于从能够从上层源码到binder驱动源码,彻底了解这一过程,同时分析binder接口在调用的时候,底层究竟是如何实现的。

1.从getSystemService 到native的getContextObject

ContextImpl.java

SystemServiceRegistry.java

SYSTEM_SERVICE_FETCHERS是一个hasmap,键值是一个抽象类,ServiceFetcher,它在初始化的时候实现,见如下代码

registerService 的作用就是把CachedServiceFetcher 放到 SYSTEM_SERVICE_FETCHERS 中,这样每次取服务时都可以从这个缓存获取,因此我们第二次getSystemService时性能是非常高的,时间复杂度位O(1),然后所有系统服务端最终都会调用到这一步 查看更多

Https、数字证书、SSL连接、中间人攻击

证书的签发原理

数字证书信息可以由申请者提供,包含域名等信息,CA机构签名后,这个证书就有效了,这个签名是数字签名,有效性来自RSA非对称加密

数字证书 = 域名数据(域名自己的公钥+域名等)+ 数据摘要(对域名的原始数据进行hash )+数字签名(对数据摘要使用CA机构自己的私钥加密的结果)

根证书:无条件信任的由系统内置的各大CA机构的证书,包含对应CA机构的公钥等

证书的验证原理

1.使用受信任的颁发者的公钥校验证书

使用本地的受信任的证书中的公钥(待验证的证书里指定了颁发者Issuer,因此可以快速找到它的公钥)来解密ssl链接中下发的的数字证书的数字签名,得到数据摘要S1,然后按相同的摘要算法对证书内容进行计算得到数据摘要S2,读取证书中的数据摘要S3,当S1=S2=S3说明证书确定是由正规机构颁发,并且内容完全正确 查看更多

总结一下各路绕过反射限制的方法

为了防止app调用系统的hide方法,android 9.0对反射的调用做了一个限制,每个方法或者变量在编译后在flag里表示了该方法是hide还是公开,通过回溯调用栈判断该调用者的类是否是BootstrapClassLoader 如果是则信任

应对方法

1,定义和系统相同类,骗过编译器,直接调用隐藏方法

2. jni修改 javaruntime里的 隐藏api策略, hidden_api_policy_

3.既然只检查调用类是否是是系统类,那么使用反射去调用反射方法,两次反射,第二次调用系统方法,在检查的时候会发现调用者反射工具类,而反射工具类是系统方法

4。 修改 javaruntime .里的豁免条件GetHiddenApiExemptions ,可以使用反射+反射 查看更多

MMKV代替 SharedPreferences

SharedPreferences 的缺点

1.SharedPreferences 写入性能低,在线上项目经常出现anr的情况,虽然 SharedPreferences 已经提供了异步写入apply方法,但由于为了保证数据的可靠性, SharedPreferences 会在一些特定的生命周期(如onDestory方法)直接执行还在pending过程的任务,导致app anr

2.对于多进程的使用, SharedPreferences 并不是很好,因为 SharedPreferences 有内存缓存,不能跨进程立即刷新,而使用contentprovider的话代码量更重

MMKV的优势

1.MMKV由于使用内存映射,使得每次写入只需写入到内存快即可,刷入操作由内核控制,不需要频繁的主动写入硬盘,即使进程意外挂掉,内核也会保存数据,写入速度极快 查看更多

TCP的连接和释放过程

TCP的报文格式

1) 源端口和目的端口       各占2个字节,分别写入源端口和目的端口。

2) 序号              占4字节。序号范围是【0,2^32 – 1】,共2^32(即4294967296)个序号。序号增加到2^32-1后,下一个序号就又回到0。也就是说,序号使用mod 2^32运算。TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号是301,而接待的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的序号也叫“报文段序号”。 查看更多

Android MessageQueue中的同步屏障

Message中有一个方法叫Message.setAsynchronous,表示发送异步消息,然而他需要插入同步屏障才能生效,那什么是同步屏障呢?

一般来说,Message如果按顺序进行发送的,并不是设置delay的话,运行都是同步的,然而在某些特殊时期,为了提高某个消息的优先级,防止中途插入的Message导致任务调度延迟,会使用异步Message,当调用postSyncBarrier之后,之后的任意同步消息都不再执行,期间只能执行异步消息,直到移除了同步屏障。

插入屏障:MessageQueue.postSyncBarrier

往链表里插入了一个奇怪的msg,他的target为null

消息循环:MessageQueue.next

遍历消息,也就是忽略所有非异步消息,只取出异步消息 查看更多

android 内存泄漏分析

一、使用android studio profile分析

android studio 相比mat来说优势在于可以分析native内存的大小,例如Bitmap等,而mat不能分析native内存。

这里以android studio 3.42为例 ,方法如下

第一步、选择右上角profile app


第二步、选择 运行设备

第三步、点击中间的memory区域,让左上角出现dump 按钮

第四部、点击左上角区域的gc按钮,然后点击dump按钮

第五步、找出其中内存较大的实例或者对象较多的实例(这里选用bitamp)

简单说明一下这里的参数

Allocations: 表示这个类实例有多少个,实例越多占用的内存越大

Shallowsize:表示这个类仅仅自身占用的内存大小,不包含引用的对象和native的大小

NativeSize :表示这个对象native的内存大小

retainedsize:表示这个对象+引用对象+native对象的大小,分析内存泄漏时,可以从这里入手

另外:点击以上各个参数可以按照参数来进行排序

第六步,选择需要查看的对象

下面显示的是reference,上面显示的是对象的详细信息

depth表示引用链的深度,当对象比较多的时候,尤其是发现对象是被一个链表引用,可以通过,选择最浅的depth来找到节点 查看更多

Android获得当前时刻每个线程的调用栈

有时候app出现异常,但是没有发生anr,比如某个重要工作线程hang住了此时怎么快速定位呢

大家都知道anr的时候会生成trace文件,如何在不发生anr的时候也生成trace文件呢

答案很简单可以通过adb shell kill -3 +pid

执行后可以看到打印了

I/art: Wrote stack traces to ‘/data/anr/traces.txt’

值得注意的是 虽然执行的是kill 命令

但是kill -3并不会导致 进程挂掉,而是仅仅打印调用栈

在有事故现场的情况下 非常好用!!

更多内容请查看大佬博客

http://gityuan.com/2017/07/09/android_debug/

Android APP启动速度优化

使用traceview跟踪启动速度

1.在application的onCreate中添加

2.在首个展示的页面中添加

3.从sdk从获取trace文件并使用android studio打开

call chart图,按调用栈分开

call chart图 , 按调用栈和时序来绘制,适合分析一个流程中的耗时

flame chart 图,调用栈从底部到顶部,一般遇到平顶的情况表示可能有性能问题

flame chart 图 相同调用栈合并,适合看频繁callback导致耗时问题

4.分析call chart图或者flame chart

从call chart图可以看到,每个第一列都是一次主线程的消息循环,

第一次循环回调了Application.oncreate,鼠标移动到目标位置可以看到 Application .onCreate耗时500毫秒

第二次消息循环做了hanldeLunchActivity,耗时1.4秒

第三次消息循环是做了performtraversals ,耗时130毫秒

整个启动时间耗时两秒左右,从这里可以看出 hanldeLunchActivity 和 Application.oncrate是非常耗时的

再分析这两次回调, 可以从下一格图,占用上一格的百分比 看出执行耗时比例。 查看更多