Android
Dalvik 和 ART
发展史
Android 2.2
- Android 首次在 Dalvik 上使用 JIT (Just In Time Compiler),在运行时对 dex/odex 进行编译与优化,将 Dalvik Code 编译为精简的 Native Code,JIT 的引入,将 Dalvik 的运行性能提升了 3~6倍
- 缺点:每次运行时都需要进行编译,会造成额外的电量消耗
Android 4.4
- Android 带来了全新的运行环境 ART(Android Run Time) 和全新的编译策略 AOT(Ahead Of Time)
- AOT 是静态编译,在安装时,会启动 dex2oat 过程,将 dex 文件预编译为 ELF 文件,每次运行时不需要重新编译
Android 5.0
- 全面替换为 ART 运行环境,编译策略也改为 AOT
- 缺点:首次安装的时间会变长,会额外增加文件大小,一个 100M 的 App,预编译后可能达到 200M
Android 7.0
- 运行环境仍是 ART,编译策略改为 AOT 和 JIT 混合编译
- 特点:在首次安装时,不会进行预编译,直接运行,使用运行时编译,在手机处于空闲或者充电状态的时候,在进行预编译
- 优点:集合了 AOT 和 JIT 的优点,使得安装速度加快,运行速度、存储空间和耗电量等指标都得到了优化
ART 相对于 Dalvik 的提升
- ART 在标记所有对象的时候不需要挂起所有的线程,ART 的 GC 要求对象在创建的时候,标记自身的堆栈,由于时间非常的短,不需要挂起线程
- 提供了 LOS (Large Object Space),专门为 Bitmap 的分配提供了空间,提高了内存的管理效率和整体性能
- ART 中有 moving collector 来压缩对象,使内存变得更加紧凑
Bitmap
占用内存
Bitmap 的发小氛围 Bitmap 对象大小和像素大小
版本区别
- Android 3.0 之前 Bitmap 对象放在 Java 堆,而像素数据放在 Native 内存中,如果不手动调用 recycle() 方法,Bitmap Native 内存回收完全依赖 finilize() 函数回调
- Android 3.0 ~ Android 7.0 Bitmap 对象和像素数据都放在了Java 堆中,就算不手动调用 recycle() 方法,像素数据也会被 gc 回收,不过 Bitmap 是内存占用大户,Bitmap 频繁的销毁创建,会导致大量的 GC
- Android 8.0 将像素数据放入了 Native 的内存中,并且使用了 NativeAllocationRegistry 来确保,Bitmap 对象和数据一起被回收
BitmapFactory.Optins
- inBitmap & inMutable,告诉编译器,尝试使用已经存在的内存区域,新解码器的 Bitmap 尝试使用之前 Bitmap 在 head 中分配的 pixel data 内存区域,在 Android 3.0 ~ 4.4,只能是相同大小的 Bitmap 才能使用,Android 4.4 以后,只要比现在 bitmap 大,就可以用
- injustDecodeBounds,true 时只返回图片的宽高,不返回 Bitmap
- inSampleSize 压缩比
图片加载库对比
Fresco
- 加载图片的流程
- 缓存中是否有数据,有则返回
- 如果缓存中没有,则开启后台线程进行后续工作
- 检查未解码的缓存,如果有,则进行解码,放入内存缓存中
- 未解码的缓存中没有,则查找为解码的文件是否有,有的话,则读取文件,解码,放入各个缓存中
- 为解码的文件缓存中没有,则直接从网上下载或者文件中加载
- 关键概念
- DraweeView,继承自 View,用于展示 UI,一般使用 SimpleDraweeView 即可
- DraweeHierarchy,用于组织和维护最终绘制和展现的 Drawable 对象,相当于 MVC 中的 M
- DraweeController,负责和 Image Loader 交互
- DraweeControllerBuilder,通过 Builder 模式创建
- 三级缓存
- 内存缓存,直接是 Bitmap 缓存,使用 LRUCache
- 内存未解码缓存,没有解码的图片缓存
- 文件未解码缓存
- 三个线程池
- IO 线程池,默认两个线程
- 网络下载线程池,与手机的 CPU 核数一样
- 用于解码的线程池,与手机的 CPU 核数一样
- 优点
- Android 5.0 以下,Bitmap 的像素数据放在 Native 内存中,5.0 ~ 7.0 放在 Java 堆中,7.0 以后 Android 系统将 Bitmap 的像素数据放入到 Native 内存中
- 支持渐进式展现
- 支持 Gif 和 WebP 动画
- 缺点
- 框架体积比较大,大概 2M~3M
Glide
- 使用二级缓存,Java 的像素内存跟随这 Android
线程并发
AsyncTask
AsyncTask 内部由两个线程和一个 Handler 实现的,Handler 用于将信息切换到主线程,两个线程池一个用于排队,一个用于执行。AsyncTask.execute(),传入一个 FutureTask 的对象。Android 3.0 之前是并行,3.0以后是串行
HandlerThread
继承于 Thread,在 run 函数中,创建了 Looper 对象
IntentService
内部实现了 HandlerThread 和 Handler,可以执行性耗时操作,因为它是一个 Service,所以它的优先级比其他 Thread 要高很多
四大组件
Activity
- 启动模式
- Standard 模式,不论栈中是否已经有该 Activity,都会创建一个新的 Activity
- SingleTop 模式,如果该 Activity 在栈顶,则不会创建新的 Activity
- SingleTask 模式,一个栈中只有一个 Activity 的实例,如果该 Activity 不在栈顶,则将其上面的 Activity 推出栈
- SingleInstance 模式,一个栈中有且只有该 Activity 一个实例对象
- onRestart 的调用场景
- 点击 home 后,然后切换回来
- 切换到其他的 Activity,然后点击 back 键,返回去
- 从本应用切换到其他应用,然后再切换回来
- Activity 跳转生命周期
- 启动 A:onCreate、onStart、onResume
- A 启动 B:(A)onPause、(B)onCrete、(B)onStart、(B)onResume、(A)onStop
- B 返回 A:(B)onPause、(A)onRestart、(A)onStart、(A)onResume、(B)onStop、(B)onDestroy
- Activity 启动流程
- Launcher 点击 App,Launcher 也是一个桌面应用,通过 ActivityManager 向 ActivityManagerService 发送启动应用的请求
- AMS 通过 ServiceManager 查找相应的进程,如果没有进程,则通过 Binder 向 Zygote 进程发送创建进程的请求
- Zygote fork 出子进程,也就是 App 进程,执行 ActivityThread 中的 main 函数
- App 进程通过 IActivityManager 向 AMS 发送 attachApplication 的请求
- AMS 接到请求后,通过 IApplicationThread 向用户进程发送 bindApplication 的请求
- 用户进程接到 bindApplication 的消息后,通过 Instrumentation 创建 Application,onAttachApplication() -> contentProvider -> onCreate()
- 创建完 Application 后,AMS 向 App 进程发送启动 Activity 的请求
Service
- 启动服务的两种方式
- startService:onCreate、onStartCommand、onDestroy
- bindService:onCreate、onBind、unBind、onDestroy
- 如何保证 Service 不被杀死
- onStartCommand 中返回 START_STICK
- 提升 service 优先级,在 service 中创建一个 Notification,这样 service 就是前台的进程了
- onDestroy 中发送广播,重新开启
- IntentService,内部使用 HandlerThread 和 Handler 来异步处理消息,只需要重写 onHandlerIntent 函数即可
BroadCast
ContentProvider
View
View 的绘制流程
JetPack
LifeCycle
LiveData
ViewModel
paging
进程保活
防止被杀死
- 使用 service 或者 foreground service 提高进程的优先级
- 监听锁屏的广播,启动一个透明或者 1 像素的 activity
被杀死后重启
- 监听静态广播
- 多进程互相拉起
- 链式调用 应用间相互启动
- service 的 onStartCommand 函数中,返回START_STICK,在杀死后,适当的时候会重新启动 service
- 文件死锁,一般在 Native 中进行
网络
TCP/IP 四层模型和 OSI 七层模型
HTTP 协议
HTTP 请求和响应格式
建立连接和断开连接
HTTPS/SSL(Secure Socket Layer)
HTTP 与 HTTPS 区别
Java
JVM
Java 内存结构以及分区
Java 判断对象是否存活,以及常见的垃圾回收算法
Java 常见的垃圾回收器
Java 类加载的步骤
Java 类加载器
设计模式
六大原则
- Single Responsibility 单一职责
- Open Close 开闭原则,对增加开发,对修改关闭
- Liscov Substitution 里氏替换原则,可以将父类替换为子类而不报错
- Least Knowledge,最小知道原则,高内聚,低耦合
- Interface Segregation 接口隔离原则,类和类之间的依赖,应该依赖最小的接口
- Dependence Inversion,依赖倒置原则,面向接口编程
具体实现
- 责任链模式
- 建造者模式
- 观察者模式
- 单例模式
- 代理模式
- 策略模式
- 工厂模式
数据集合
ArrayList
LinkedList
HashMap
LinkedHashMap
HashSet
ConcurrentHashMap
SparseArrayList
线程与并发
线程
锁
线程池
排序算法
冒泡排序
选择排序
插入排序
希尔排序
归并排序
快速排序
树
二叉树
二叉查找树
二叉平衡树
红黑树
性能优化
常见的性能问题
内存泄漏
OOM
耗电量问题
内存抖动
造成性能问题常见的原因
- 在 ui 线程中进行了好使的操作,导致 ui 线程卡顿
如何解决这些问题
专项优化
RecyclerView 优化
数据存储
包体积优化
启动优化
Crash 优化
UI 优化
内存优化
进阶
组件化
什么是组件化
为什么要组件化
隔离
应用架构
实现技术
插件化
什么是插件化
为什么要用插件化
实现技术
热修复
主流修复框架的原理
Robust
AOP
AOP 概念
AOP 的使用场景
AOP 的实现
- 静态植入(编译期)
- 动态植入(运行期)
AOP 实战
- Android 的打包过程
- Transform
- ASM code