待办事项
Handler原理
- Looper.prepare()方法中,先获取当前线程Thread.currentThread(),记为t;
- 从t的threadLocals变量,获取到ThreadLocalMap对象;
- ThreadLocalMap对象内部是一个数组;
- ThreadLocal
.set()方法,会把Looper实例放到当前线程的threadLocals的数组中,下标由ThreadLocal确定; - Looper的构造方法中,会新建一个MessageQueue实例;
- Looper.loop()开始进入无限循环,获取不到消息会进入阻塞状态;
- Handler实例的sendMessage方法,把消息插入到MessageQueue;
- 获取到消息之后,调用msg.target.dispatchMessage进行分发处理;
- 如果msg的callback(Runnable类型)不为空,则调用callback的run方法;
- 否则,调用handler自身的handleMessage方法。
App启动过程
- Intent包名+类名确定目标Class的全限定名,调用AMS启动Activity;
- AMS查询PMS,校验目标Activity是否在Manifest注册;
- AMS通过socket向zygote申请fork新进程,指定入口函数ActivityThread.main();
- 新进程创建完成之后,执行入口main函数,调用Looper.prepareMainLooper(),创建ActivityThread实例;
- 接着新进程attachApplication通知AMS新进程就绪,然后执行Looper.loop()准备接收AMS的接下来的消息;
- AMS调用handleBindApplication通知该新进程进行进一步初始化;
- 通过Class.newInstance()创建Application实例;
- 调用Application的attachBaseContext()方法;
- 调用Application的onCreate()方法;
- AMS接着调用handleLaunchActivity通知App进程需要启动Activity;
- 同样的,通过Class.newInstance()创建Activity实例;
- activity的attach方法创建PhoneWindow;
- activity调用onCreate方法,把setContentView()指定的视图加载进父布局;
- activity调用onStart();
- activity调用onResume();
- 创建ViewRootImpl实例root,
- 以顶级布局DecorView作为参数,调用root.setView(view, wparams, panelParentView);
- root.performTraversals();
- root.performMeasure();
- root.performLayout();
- root.performDraw();
- activity调用onWindowFocusChanged(),真正的界面对用户可见。
java的4种引用
引用类型 | 回收时机 | get() | isEnqueued() |
---|---|---|---|
强引用 | 不回收 | - | - |
软引用 | OOM之前 | 强引用被回收之后为null | 强引用被回收之后为true |
弱引用 | 每次GC | 强引用被回收之后为null | 强引用被回收之后为true |
虚引用 | 不影响 | 永远为null | 强引用被回收之后为true |
垃圾回收的方法
GC Roots |
---|
栈帧中的本地变量表引用的对象 |
类静态属性引用的对象 |
类常量引用的对象 |
JNI方法栈引用的对象 |
垃圾收集算法 | 使用场景 | 特点 |
---|---|---|
标记-清除 | 老年代 | 内存碎片 |
标记-整理 | 老年代 | 额外的整理工作 |
复制 | 新生代 | 牺牲10%的Survivor空间 |
leakcanary原理
- 初始化时在Application注册监听Activity生命周期;
- 对onDestroy的Activity进行watch:创建此Activity的弱引用;分配一个特有的key,把key加入监控Set中;
- 5秒之后,从弱引用的queue中poll元素,如果获取到弱引用,说明强引用已被回收,获取其key,从Set中移除;
- 否则,调用Runtime.getRuntime().gc()触发GC;
- 如果仍然存在Activity的强引用,说明Activity可能存在泄漏,调用Debug.dumpHprofData()获取dump信息;
- 3-5阶段的操作是通过backgroundHandler提交给HandlerThread执行。
- 在新进程的前台Service中,解析dump数据,找到泄漏Activity的最短引用链,如子线程->Runnable->Activity。
卡顿优化
因素
- CPU竞争:线程数、线程优先级;
- 界面层级,measure/layout/draw耗时;
- gc;
排查
- Systrace
- CPU Profile
- 过度绘制
- Layout Inspector
- GPU条形图
解决方法
- 耗时方法放到子线程;
- 布局扁平化;
- 使用线程池;
- 规划好线程优先级;
- 内存优化。
内存抖动
- 避免循环体内创建对象;
- 避免在onDraw创建对象;
- Bitmap内存缓存;
- 对象池。
内存优化点
- 内部类引用导致Activity的泄漏
- static引用了Activity
- unregisterReceiver
- Cursor对象注意关闭
- 流注意关闭
组件化/模块化
架构 | 目的 | 定位 | 依赖 |
---|---|---|---|
模块化 | 隔离 | 横向 | 路由跳转 |
组件化 | 复用 | 纵向 | 上下级依赖 |
http/https
协议 | 安全性 | 端口 | 成本 |
---|---|---|---|
http | 明文 | 80 | 低 |
https | 加密 | 443 | 每个消息加密/解密,SSL握手 |
tcp/udp
协议 | TCP | UDP |
---|---|---|
面向连接 | 是 | 否 |
可靠性 | 是 | 否 |
顺序控制 | 是 | 否 |
重发控制 | 是 | 否 |
流量控制 | 是 | 否 |
拥塞控制 | 慢启动 | 否 |
实时性 | 低 | 高 |
广播 | 1对1 | 1对多 |
进程间通信
方式 | 优点 | 缺点 | 例子 |
---|---|---|---|
Binder | 架构清晰 | 拷贝1次 | Messenger、AIDL、四大组件进程通信 |
Socket | 传输字节流 | 拷贝2次 | 跨网络通信 |
共享内存 | 无需拷贝 | 控制复杂 |
WebView优化点
图片后加载
- onPageStarted: WebSettings.setBlockNetworkImage(true);
- onPageFinished: WebSettings.setBlockNetworkImage(false);
独立进程
独立进程可以防止WebView内存泄漏导致整体App闪退。
定义独立进程
<activity
android:name=".WebViewActivity"
android:process=":webview"></activity>
销毁进程
@Override
protected void onDestroy() {
super.onDestroy();
Process.killProcess(Process.myPid());
System.exit(0);
}