CpuArch

image-20210615211220037

‘armeabi-v7a’表示32位cpu架构, ‘arm64-v8a’表示64位, ‘x86’是只模拟器或特定rom。

Read more

matrix

Matrix Tencent

一:TraceCanary

插桩:通过插桩,在除了get/set、默认或匿名构造函数等简单函数外的所有方法,入口/出口插入MethodBeat.i()/MethodBeat.o()。

image-20210729175300794

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//AppMethodBeat.java
private static void mergeData(int methodId, int index, boolean isIn) {
if (methodId == AppMethodBeat.METHOD_ID_DISPATCH) {
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
}
long trueId = 0L;
if (isIn) {
trueId |= 1L << 63;
}
trueId |= (long) methodId << 43;
trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;
sBuffer[index] = trueId;
checkPileup(index);
sLastIndex = index;
}

用一个64位的long型来存储方法的:方法开始/方法结束(最高位63位)、方法id(递增,43到62位)、当前与MethodBeat模块初始化时差(0到42位)

Read more

ThreadLocal

ThreadLocal:

ThreadLocal: 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

ThreadLocal类不过是为了执行set/get,确定泛型的工具人而已。真正数据是靠的每个Thread内部维护。

实际上就是 每个Thread对象中 维护了一个

叫ThreadLocal.ThreadLocalMap的

**key为 ThreadLocal对象, value为 存储的值 **

的key-vale映射结构,(实际上是一个键值对组成entry的唯一环形数组,线性探测,初始值3/4容量扩容)

当调用threadLocalInstance.set(value)时,其实时调用的CurrentThread.threadLocalMap.set(threadLocalInstance, value)

Read more

Gc

现代VM:”引用计数法,不行。可达性分析法,行!”
JVM:”可达分析法,很行”

Read more

EventDispatch

image-20220127160157749

Core

事件分发中有一个重要的规则:一个触控点的一个事件序列只能给一个view处理

分析:以DOWN事件为序列分发判定,ViewGroup为消费DOWN事件的View生成一个TouchTarget(这个TouchTarget就包含了该view的实例与触控id,id可以是多个以应对多指触控),后续MOVE、UP都会交给这个TouchTarget。如果TouchTarget为空则ViewGroup自己处理。如果viewGroup消费了down事件,那么子view将无法收到任何事件。

Read more

插件化/热修复

image-20210410164426213

插件化和热修复不是同一个概念,虽然站在技术实现的角度来说,他们都是从系统加载器的角度出发,无论是采用hook方式,亦或是代理方式或者是其他底层实现,都是通过“欺骗”Android 系统的方式来让宿主正常的加载和运行插件(补丁)中的内容;但是二者的出发点是不同的。插件化顾名思义,更多是想把需要实现的模块或功能当做一个独立的提取出来,减少宿主的规模,当需要使用到相应的功能时再去加载相应的模块。热修复则往往是从修复bug的角度出发,强调的是在不需要二次安装应用的前提下修复已知的bug。

  • PathClassLoader:只能加载已经安装到Android系统中的apk文件(/data/app目录),是Android默认使用的类加载器。
  • DexClassLoader:可以加载任意目录下的dex/jar/apk/zip文件,也就是我们一开始提到的补丁。
  • BaseDexClassLoader: 是 PathClassLoader 和 DexClassLoader 的父类,其内有一个 DexPathList 属性,实现了 findClass 方法逻辑,PathClassLoader 和 DexClassLoader 都只是在构造函数上对其做了简单封装而已。
Read more

AndroidVm

热知识:java常见的虚拟机如Hotspot虚拟机是基于栈结构的,而Dalvik是基于寄存器结构的。

常见的java虚拟机跑的是.class文件,而Dalvik跑的是.dex(.odex)文件。

image-20210602170532688

BoostMultiDex优化Dalvik虚拟机多Dex启动速度

Android 4.4 及以下采用的是 Dalvik 虚拟机,在通常情况下,Dalvik 虚拟机只能执行做过 OPT 优化的 DEX 文件,也就是我们常说的 ODEX 文件。

一个 APK 在安装的时候,其中的classes.dex会自动做 ODEX 优化,并在启动的时候由系统默认直接加载到 APP 的PathClassLoader里面,因此classes.dex中的类肯定能直接访问,不需要我们操心。

除它之外的 DEX 文件,也就是classes2.dex、classes3.dex、classes4.dex等 DEX 文件(这里我们统称为 Secondary DEX 文件),这些文件都需要靠我们自己进行 ODEX 优化,并加载到 ClassLoader 里,才能正常使用其中的类。否则在访问这些类的时候,就会抛出ClassNotFound异常从而引起崩溃。

因此,Android 官方推出了 MultiDex 方案。只需要在 APP 程序执行最早的入口,也就是Application.attachBaseContext里面直接调MultiDex.install,它会解开 APK 包,对第二个以后的 DEX 文件做 ODEX 优化并加载。这样,带有多个 DEX 文件的 APK 就可以顺利执行下去了。

这个操作会在 APP 安装或者更新后首次冷启动的时候发生,正是由于这个过程耗时漫长,才导致了我们最开始提到的耗时黑屏问题。

1
2
3
4
5
if (Build.VERSION.SDK_INT <= 19) { 
BoostMultiDex.install(this);
} else {
MultiDex.install(this);
}
Read more

JsBridage

Js与原生交互的方式:

通过注入对象或拦截 URL SCHEME实现JSBridge

Read more

fresco

Fresco(2.5.0)

以MVC的 Fresco架构入手,层层递进分析fresco的整体思路。

img

Read more

retrofit

原理-动态代理

*Retrofit通过 反射构建一个 接口的 实现类(动态代理本质就是反射),其中每个重写方法被调用时,都会回调到InvocationHandler.invoke中,invoke回调时(只要是不是object类中的方法)都会通过 获取到的方法的注解、方法的名称、方法的返回值、方法参数的注解、方法参数类型等等所需信息, 解析成一个ServiceMethod对象(放入缓存池),ServiceMethod根据获取到的方法信息,构建OkHttp请求,并将结果通过converter转换后回调给最初传入的Callback*

Read more

okhttp

链接:https://juejin.cn/post/6887896333685161992

简述:

通过对外提供的OkHttpClient和Request的builder实现基础信息和必要信息的配置,直到封装构建成了RealCall对象(RealCall implement Call)并新建CallBack实例传入realCall.equeue(callback),才真正完成了请求实体的实例化。

之后realCall.enqueue(call)方法的调用才是实际上开始进行请求:先判断是否call已经执行过了(executed = AtomicBoolean()),若未执行则继续

之后由Dispatcher调用enqueue进行判断当前正在执行的请求数及当前网络请求的主机数是否超过了最大值。要是超过了最大值,就将请求放到等待队列中,要是没超过,就放到正在执行的队列中,然后调用线程池(默认单例初始化了一个缓存线程池(即无核心线程、无限线程池数量、SynchronousQueue))执行它调度,执行的过程也就是asyncCall的run()方法通过责任链五大拦截器进行层层处理的过程。

Read more

leakcanary

  1. 线程栈中的局部变量表引用的所有变量,即运行线程中引用到的所有变量,包括线程中方法参数和局部变量

  2. 存活的线程对象

  3. native 的 jni引用

  4. class 对象 (classLoader 不会卸载class)

  5. 引用类型的静态变量

// 1跟2其实说的是一个东西

Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.

在检测到适当的可达性改变后,垃圾收集器将注册的引用对象(WeakReference)追加到引用队列(ReferenceQueue)。

​ 核心思路是:
​ leakCanary做法是ondestory后手动出发GC,GC过后对象WeakReference一直不被加入 ReferenceQueue,它可能存在内存泄漏。

​ 利用 双参初始化的弱引用 WeakReference(T referent, ReferenceQueue<? super T> q) 在object对象变成弱可达的时候(可视为已被回收/(其他非强引用也一样)),会将该WeakReference对象入队q中 的特性(也就是queque中最后会存在已经被回收了的weakreference对象),通过在Activity和Fragment的onDestroy()中,将该Activity或Fragment实例的弱引用初始化(双参object,queue)后放入map中(key随机固定uuid),GC后 把map中的 queue包含的对象 移除,map中剩余的即为可能泄露的对象

Read more

Image

内存所在位置

Android 2.3 之前: 像素数据存在于native heap

Android 3.0 ~ 7.1 之间: 存在于 java heap

Android 8.0及之后: 存在于 native

Read more

Android Build

Apk总构建流程

简述

Aapt 会将主工程、依赖库中的资源(res、assets)和androidManifest都合并,产出R.java、资源及资源索引resources.arsc;

之后javac编译包括R.java文件、主工程的java文件、aidl产生的java文件,产出class文件;如果需要插桩的话就插桩

之后使用Proguard/R8混淆工具对.class文件脱糖、压缩、混淆等,产出新的class文件;

之后使用Dx/D8编译工具将新的class文件再转换成dex文件,

之后打包成apk,然后签名、zipalign优化。

工具:aapt/aapt2、javac、Proguard/R8、Dx/D8、ApkBuilder、zipalign

img

Read more

ScreenDraw

从onCreate到页面绘制

当手指点击了桌面的App图标时发生了什么 - ProcessOn

Android 屏幕刷新机制

主要参考 https://juejin.cn/post/6863756420380196877#heading-12

省流版:

双缓存:为了解决画面撕裂;画面撕裂来自于只有一个buffer时,正在display的那一帧数据被后一帧的数据覆盖了

Vsync:系统在收到VSync pulse(Vsync脉冲)后,将马上开始下一帧的渲染,(CPU开始计算数据)。

三缓冲:当显示器正在写入FrameBuffer同时GPU也正在写入BackBuffer时,下一次渲染开始了,此时CPU可以使用新增的GraphicBuffer进行计算。减少了Jank。(更多缓冲需要耗费更大的内存)

ChoreoGrapher机制:规定了数据计算开始(measure、layout、draw)的时机(vsync信号),使计算到渲染图像数据能有一个完整的16.6ms:更新ui(request()/invalidate())后编舞者注册vsync信号回调,在下一个vsync信号到时候立刻进行view的测量布局绘制

Read more