├── .idea
├── .gitignore
├── codeStyles
├── dictionaries
├── markdown.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── TODO.md
├── TODO
└── md.java
├── android
├── gradle
│ ├── gradle进阶.md
│ └── resource
│ │ ├── apk构建流程.png
│ │ └── gradle概念.png
├── 基础
│ ├── Android线程之间交互.md
│ ├── App编译打包过程.md
│ ├── Binder与AIDL.md
│ ├── Choreographer.md
│ ├── Context.md
│ ├── Fragment.md
│ ├── Framework层App启动流程.md
│ ├── Handler.md
│ ├── Intent.md
│ ├── RecycleView原理回收复用.md
│ ├── SharePreference.md
│ ├── WebView.md
│ ├── resource
│ │ ├── AMS与WMS交互.png
│ │ ├── Activity启动时序2.png
│ │ ├── Activity启动时序图.png
│ │ ├── Activity界面层级.png
│ │ ├── Activity的onCreate之前流程.png
│ │ ├── Activity的启动流程.png
│ │ ├── Binder性能.png
│ │ ├── Binder机制.png
│ │ ├── Binder机制跨进程.png
│ │ ├── Context继承关系.png
│ │ ├── Fragment生命周期.png
│ │ ├── Handler所有类.png
│ │ ├── JSBridge.png
│ │ ├── ThreadLocal与Looper.png
│ │ ├── ViewGroup的Draw过程.png
│ │ ├── ViewGroup的Layout过程.png
│ │ ├── ViewGroup的measure过程.png
│ │ ├── View与Window逻辑结构.png
│ │ ├── View的Draw过程.png
│ │ ├── View的Measure过程.png
│ │ ├── View绘制.png
│ │ ├── WebView的启动过程.png
│ │ ├── handleResumeActivity.png
│ │ ├── invalidate刷新流程.png
│ │ ├── 事件分发.png
│ │ ├── 事件流程源码.png
│ │ ├── 生产者消费者.png
│ │ ├── 生命周期.png
│ │ └── 生命周期交织.png
│ ├── 事件分发机制.md
│ ├── 四大组件.md
│ ├── 布局.md
│ └── 自定义View.md
├── 性能
│ ├── Carsh优化.md
│ ├── resource
│ │ ├── Activity启动流程.png
│ │ ├── cpu和gpu区别.png
│ │ ├── 反射修改系统sdk.png
│ │ ├── 可达性分析.png
│ │ ├── 堆区数据结构.png
│ │ ├── 对象头.png
│ │ ├── 屏幕分类.png
│ │ ├── 引发内存问题常见的十种优化.png
│ │ ├── 渲染机制主线.png
│ │ └── 运行时.png
│ ├── 保活.md
│ ├── 内存优化.md
│ ├── 卡顿优化.md
│ ├── 启动优化.md
│ └── 屏幕适配.md
├── 性能优化2.png
├── 框架
│ ├── ARouter原理解析.md
│ ├── BlockCannery原理解析.md
│ ├── Glide源码分析.md
│ ├── Jetpack架构之DataBinding.md
│ ├── Jetpack架构之Lifecycle.md
│ ├── Jetpack架构之LiveData.md
│ ├── Jetpack架构之ViewModel.md
│ ├── LeakCannary原理解析.md
│ ├── MVP、MVVM架构.md
│ ├── OKHttp源码分析.md
│ ├── Retrofit源码分析.md
│ ├── RxJava.md
│ └── resource
│ │ ├── BlockCannery核心流程.png
│ │ ├── LeakCannery工作流程.png
│ │ ├── LeakCannery工作流程2.png
│ │ ├── LeakCannery工作流程3.png
│ │ ├── MVC.png
│ │ ├── MVP.png
│ │ ├── MVVM.png
│ │ ├── Mvvm架构.png
│ │ ├── OkHttp流程.png
│ │ ├── ViewModel与Activity生命周期.png
│ │ ├── databinding布局.png
│ │ ├── observeOn.png
│ │ ├── retrofit处理流程.png
│ │ ├── subscribeOn.png
│ │ ├── 分发器异步请求.png
│ │ ├── 单个任务执行流程.png
│ │ ├── 异步请求流程.png
│ │ ├── 组件化架构图.png
│ │ └── 观察者的Lifecycle.png
└── 设计模式
│ ├── View绘制理解设计模式-桥接.md
│ ├── 代理模式.md
│ ├── 六大原则.md
│ ├── 单例模式.md
│ ├── 工厂模式.md
│ ├── 策略模式.md
│ ├── 观察者.md
│ ├── 责任链模式.md
│ └── 适配器.md
├── interview.iml
├── java基础
├── JVM.md
├── StringBulider和StringBuffer.md
├── equal与hashCode.md
├── final关键字.md
├── resouse
│ ├── ConcurrentHashMap分段锁.png
│ ├── Executors的默认方法.png
│ ├── ThradLocal.png
│ ├── ThradLocal弱引用.png
│ ├── ThreadPoolExecutorExtend.png
│ ├── ThreadPoolExecutor变量.png
│ ├── ThreadPoolExecutor状态.png
│ ├── execute提交流程.png
│ ├── gcRoot.png
│ ├── hashmap容量.png
│ ├── hashmap长度.png
│ ├── hash运算.png
│ ├── java注解.png
│ ├── jdk7中CHM.png
│ ├── jdk8中CHM.png
│ ├── 不同jdk的区别.png
│ ├── 为什么不推荐使用Executors的默认方法.png
│ ├── 双亲委派机制.png
│ ├── 可达性分析.png
│ ├── 哈希表.png
│ ├── 堆分代设计.png
│ ├── 对象创建过程.png
│ ├── 对象头.png
│ ├── 扩容要做的事情.png
│ ├── 栈帧结构.png
│ ├── 类加载器.png
│ ├── 类加载机制.png
│ ├── 线程池工作流程.png
│ ├── 线程池提交任务.png
│ ├── 线程池线程的复用.png
│ ├── 运行时数据区.png
│ └── 锁的升级.png
├── 并发
│ ├── ThreadLocal.md
│ ├── 线程模型.md
│ ├── 线程池.md
│ └── 锁.md
├── 引用类型.md
├── 接口和抽象类.md
├── 注解.md
├── 集合
│ ├── ArrayList与LinkedList.md
│ ├── ConcurrentHashMap.md
│ └── HashMap.md
├── 静态类、静态方法、成员内部类、静态内部类、局部内部类、和匿名内部类的理解.md
└── 面向对象.md
├── kotlin
├── kt进阶.md
└── 协程.md
├── 算法
├── resource
│ └── 最大子数组.png
├── 算法-概述.md
└── 算法.md
└── 网络
├── HTTP协议.md
├── TCP协议.md
├── get和post.md
└── resource
├── OKHttp.png
├── SSL加密过程.png
├── TCP和UDP.png
├── socket.png
├── 七层模型.png
├── 三次握手.png
├── 四次挥手.png
└── 报文格式.png
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/codeStyles:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | xmlns:android
13 |
14 | ^$
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | xmlns:.*
24 |
25 | ^$
26 |
27 |
28 | BY_NAME
29 |
30 |
31 |
32 |
33 |
34 |
35 | .*:id
36 |
37 | http://schemas.android.com/apk/res/android
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | .*:name
47 |
48 | http://schemas.android.com/apk/res/android
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | name
58 |
59 | ^$
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | style
69 |
70 | ^$
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | .*
80 |
81 | ^$
82 |
83 |
84 | BY_NAME
85 |
86 |
87 |
88 |
89 |
90 |
91 | .*
92 |
93 | http://schemas.android.com/apk/res/android
94 |
95 |
96 | ANDROID_ATTRIBUTE_ORDER
97 |
98 |
99 |
100 |
101 |
102 |
103 | .*
104 |
105 | .*
106 |
107 |
108 | BY_NAME
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/dictionaries:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/markdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 安卓工程师面试指南
2 | - 由于外部环境,加上行业发展限制,最近互联网企业员工找工作需求增多。本人打算总结共享安卓面试的一些基础知识点和常问的问题,整理期间,尽量做到每周更新2-3次。希望能够对大家有所启发。有错误或者讲解不清晰之处欢迎大家指正,希望与诸君共勉---爱学习的小小酥
3 |
4 | ## Java篇
5 | - [面向对象](./java基础/面向对象.md)
6 | - [final、finally、finalize、Serializable和Parcelable](./java基础/final关键字.md)
7 | - [静态类、静态方法、成员内部类、静态内部类、局部内部类、和匿名内部类的理解](./java基础/静态类、静态方法、成员内部类、静态内部类、局部内部类、和匿名内部类的理解.md)
8 | - [StringBulider和StringBuffer](./java基础/StringBulider和StringBuffer.md)
9 | - [equal与hashCode](./java基础/equal与hashCode.md)
10 | - [接口和抽象类](./java基础/接口和抽象类.md)
11 | - [注解](./java基础/注解.md)
12 | - [引用类型](./java基础/引用类型.md)
13 | - [类加载与JVM](./java基础/JVM.md)
14 | ### 集合
15 | - [ArrayList与LinkedList](./java基础/集合/ArrayList与LinkedList.md)
16 | - [HashMap](./java基础/集合/HashMap.md)
17 | - [ConcurrentHashMap](./java基础/集合/ConcurrentHashMap.md)
18 |
19 | ### 并发
20 | - [线程模型](./java基础/并发/线程模型.md)
21 | - [ThreadLocal](./java基础/并发/ThreadLocal.md)
22 | - [锁](./java基础/并发/锁.md)
23 | - [线程池](./java基础/并发/线程池.md)
24 |
25 | ## 网络
26 | - [TCP协议](./网络/TCP协议.md)
27 | - [HTTP协议](./网络/HTTP协议.md)
28 | - [get和post](./网络/get和post.md)
29 |
30 | ## Android篇
31 | ### 基础
32 | - [四大组件](./android/基础/四大组件.md)
33 | - [Context](./android/基础/Context.md)
34 | - [SharedPreference](./android/基础/SharePreference.md)
35 | - [Intent](./android/基础/Intent.md)
36 | - [Fragment](./android/基础/Fragment.md)
37 | - [RecycleView](./android/基础/RecycleView原理回收复用.md)
38 | - [WebView](./android/基础/WebView.md)
39 | - [App与Activity启动](./android/基础/Framework层App启动流程.md)
40 | - [自定义View](./android/基础/自定义View.md)
41 | - [事件分发](./android/基础/事件分发机制.md)
42 | - [Handler](./android/基础/Handler.md)
43 | - [Binder](./android/基础/Binder与AIDL.md)
44 | - [线程交互](./android/基础/Android线程之间交互.md)
45 | - [App编译流程](./android/基础/App编译打包过程.md)
46 |
47 | ### 性能
48 | - [屏幕适配](./android/性能/屏幕适配.md)
49 | - [卡顿优化](./android/性能/卡顿优化.md)
50 | - [Carsh优化](./android/性能/Carsh优化.md)
51 | - [内存优化](./android/性能/内存优化.md)
52 | - [启动优化](./android/性能/启动优化.md)
53 |
54 | ### 三方框架
55 | - [OKHttp源码分析](./android/框架/OKHttp源码分析.md)
56 | - [Retrofit源码分析](./android/框架/Retrofit源码分析.md)
57 | - [Glide源码分析](./android/框架/Glide源码分析.md)
58 | - [组件化之ARouter原理解析](./android/框架/ARouter原理解析.md)
59 | - [LeakCannary原理解析](./android/框架/LeakCannary原理解析.md)
60 | - [BlockCannery原理解析](./android/框架/BlockCannery原理解析.md)
61 |
62 | ### 架构
63 | - [Rxjava](./android/框架/RxJava.md)
64 | - [MVP、MVVM架构](./android/框架/MVP、MVVM架构.md)
65 | - [Jetpack架构之DataBinding](./android/框架/Jetpack架构之DataBinding.md)
66 | - [Jetpack架构之ViewModel](./android/框架/Jetpack架构之ViewModel.md)
67 | - [Jetpack架构之Lifecycle](./android/框架/Jetpack架构之Lifecycle.md)
68 | - [Jetpack架构之LiveData](./android/框架/Jetpack架构之Lifecycle.md)
69 |
70 | ### gradle进阶
71 |
72 |
73 | ### 设计模式
74 | - [六大原则](./android/设计模式/六大原则.md)
75 | - [单例模式](./android/设计模式/单例模式.md)
76 | - [工厂模式](./android/设计模式/工厂模式.md)
77 | - [代理模式](./android/设计模式/代理模式.md)
78 | - [适配器模式](./android/设计模式/适配器.md)
79 | - [观察者模式](./android/设计模式/观察者.md)
80 | - [策略模式](./android/设计模式/策略模式.md)
81 | - [责任链模式](./android/设计模式/责任链模式.md)
82 | - [View绘制理解设计模式-桥接模式](./android/设计模式/View绘制理解设计模式-桥接.md)
83 |
84 |
85 | ## 算法
86 | - [数据结构](./算法/数据结构.md)
87 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | - 加固的原理,
2 | - dex如何加载使用的,
3 | - apk打包流程
4 | - 保活,
5 |
6 |
7 | - apk瘦身
8 | - 内存优化,内存泄漏:https://juejin.cn/post/6863622872754683912
9 | - 冷启动优化,
10 | - 卡顿优化
11 | - 优化系列课程:https://www.bilibili.com/video/BV1T44y1u7g6?spm_id_from=333.1007.top_right_bar_window_history.content.click
12 | - AOP编程, 字节码插桩 https://blog.csdn.net/shulianghan/article/details/120298969
13 |
14 | - 自定义注解,反射
15 | - 如何实现一个长连接,
16 | - 如何实现捕捉所有view的onClick时间,并打出日志
17 |
18 | - RxJava 中的观察者
19 | - 策略+工厂模式
20 |
21 | - EventBus 源码
22 |
23 | - BlockCannery原理
24 |
25 | - lifecycle
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/TODO/md.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Description:
3 | * Created by small small su
4 | * Date: 2022/5/7
5 | * Email: surao@foryou56.com
6 | */
7 | class md {
8 | }
9 |
--------------------------------------------------------------------------------
/android/gradle/gradle进阶.md:
--------------------------------------------------------------------------------
1 | ### gradle可以做的事情
2 | - 使用的groovy语法,类似于shell脚本,可以完成数据处理,文件操作。groovy可以调用java,两者相辅相成
3 | - gradle.wrapper.jar相当于执行脚本文件的jar包
4 | 1. 打apk包
5 | 2. 打插件包
6 | 3. hook插件
7 | 4. 自动化构建
8 | 5. 多渠道打包
9 | 6. 自动化签名
10 | 7. 后台java打包
11 | 8. 在项目中生成文件
12 |
13 | ### Gradle 配置分析
14 | #### apk 打包的流程
15 | 
16 | - apk解压出来后: META-INF(依赖包的版本信息),res(资源,图片,layout,drawable,color等),AndroidManifest.xml,app-debug(),classes.dex(主包,热修复只能修复非主包的内容),classes2.dex(分包),resources.arsc(多语种)
17 | - 打包时会把java,kotlin,ndk,资源文件等进行整合,最后打成一个apk包
18 |
19 | #### gradle 是什么,特性有哪些
20 | 
21 | ##### wrapper 脚本文件,Closure闭包,Project一个gradle的项目,tasks任务,hooks生成代码,plugin插件
22 | - wrapper,一个gradle的封装体。有了gradle wrapper,即便机器上没有装gradle,也可以执行gradle的构建工作。
23 |
24 | #### 根目录下的build.gradle
25 | ```groovy
26 | buildscript{ //执行的脚本
27 | repositories{ //gradle插件的 依赖仓库
28 | //下载包的时候 是由上往下 去下载
29 | maven{ //国内的git仓库 写在最上面
30 | url ''
31 | }
32 | google() //国外的
33 | jcenter() //国外的 github
34 | }
35 | dependencies{ // 依赖的插件,包括gradle插件,自定义插件
36 |
37 | }
38 | }
39 | allprojects{
40 | //项目本身需要的依赖,配置所有的Modyle公共依赖,跟上面的地址写的一样
41 | repositories{
42 |
43 | }
44 |
45 | }
46 |
47 | //任务 ,清除所有build文件。
48 | task clean(type: Delete){
49 | delete rootProject.buildDir
50 | }
51 | // 可以加一个生命周期的监听
52 | gradle.beforeProject{project ->
53 | println "beforeProject: $project.name"
54 | //在生命周期里面去触发自己的插件
55 | }
56 |
57 | gradle.afterProject{ project, projectState ->
58 | println("afterProject:$project.name")
59 | }
60 | this.project.subprojects{sub ->
61 | sub.beforeEvaluate{project
62 | println "#### Evaluate before of" + project.path
63 | }
64 | }
65 | afterEvaluate{
66 | println "afterEvalute start"
67 | // do somthing
68 | println "afterEvaluate end"
69 | }
70 | ```
71 | #### app的 build.gradle 文件
72 | ```groovy
73 | android{
74 |
75 | buildTypes{
76 | debug{
77 |
78 | }
79 | release{
80 |
81 | }
82 | }
83 | }
84 | //依赖相关信息
85 | dependencies{
86 |
87 | }
88 | ```
89 |
90 |
91 |
92 |
93 | #### gradle 安装在哪里,各个文件和目录的作用是什么?
94 |
95 |
96 |
97 |
98 | #### gradle 的 project 和 task 的概念
99 | #### app打包慢的解决方案
100 |
101 |
102 | ### groovy语法介绍
103 | 1. grdle clean : clean 命令
104 | 2. gradle assembleDebug
105 | 3. gradle assembleRelease
106 | - 这里我们学习 字符串拼接,list集合,map集合,闭包,方法,对象,task任务,文件操作。
107 |
108 |
109 | ### 什么是闭包与文件读取
110 |
111 |
112 |
113 | ### 自动配置签名
114 |
115 |
116 |
117 | ### 自定义配置gradle参数
118 |
119 |
120 |
121 | ### 代码混淆与反混淆
122 |
123 |
124 |
125 | ### 如何提高编译效率
126 |
127 |
128 |
129 | ### 多渠道打包以及自定义gradle插件
130 |
131 |
132 |
--------------------------------------------------------------------------------
/android/gradle/resource/apk构建流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/gradle/resource/apk构建流程.png
--------------------------------------------------------------------------------
/android/gradle/resource/gradle概念.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/gradle/resource/gradle概念.png
--------------------------------------------------------------------------------
/android/基础/Android线程之间交互.md:
--------------------------------------------------------------------------------
1 | ### Android线程交互
2 | #### 子线程与主线程的Handler通信
3 | - 主线程自带Looper机制,所以不需要创建Looper
4 |
5 | #### 子线程之间的Handler通信
6 | - 子线程在处理消息时的handler需要创建Looper,并在Handler之后需要Looper.loop();
7 | - 发消息时,必须保证MyThread的run方法执行,因为只有looper准备好之后,才会开启消息队列的循环
8 | ```java
9 | class MyThread extends Thread {
10 | @Override
11 | public void run() {
12 | Looper.prepare();
13 | mSubHandler = new Handler() {
14 | @Override
15 | public void handleMessage(Message msg) {
16 | switch (msg.what) {
17 | case 1:
18 | Log.i(TAG, (String) msg.obj);
19 | break;
20 | }
21 | }
22 | };
23 | Looper.loop();
24 | }
25 | }
26 | }
27 | ```
28 | #### HandlerThread
29 | - HandlerThread是一个包含Looper的Thread,我们可以直接使用这个 Looper 创建 Handler。
30 | - HandlerThread适用于单线程+异步队列模型场景,相对Handler + Thread简洁。
31 | ```java
32 | Handler mThreadHandler = new Handler(mHandlerThread.getLooper()) {
33 | @Override
34 | public void handleMessage(Message msg) {
35 | switch (msg.what) {
36 | case 1:
37 | Log.i(TAG, "threadName--" + Thread.currentThread().getName() + (String) msg.obj);
38 | break;
39 | }
40 | }
41 | };
42 |
43 | // 发送消息至HanderThread
44 | mThreadHandler.sendMessage(msg);
45 | ```
46 |
47 | #### runOnuiThread 子线程更新主线程ui
48 | ```java
49 | new Thread(new Runnable() {
50 | @Override
51 | public void run() {
52 | try {
53 | Thread.sleep( 1000 );
54 | } catch (InterruptedException e) {
55 | e.printStackTrace();
56 | }
57 |
58 | runOnUiThread(new Runnable() {
59 | @Override
60 | public void run() {
61 | // 执行UI操作
62 | Toast.makeText(MainActivity.this, "Test", Toast.LENGTH_SHORT).show();
63 | }
64 | });
65 | }
66 | }).start();
67 | ```
68 | #### view.post(Runnable r);view.postDelay(Runnable r)
69 |
70 | #### AsyncTask
71 | - AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类。它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI
72 |
73 | - https://blog.csdn.net/weixin_30254435/article/details/98309798
74 |
--------------------------------------------------------------------------------
/android/基础/App编译打包过程.md:
--------------------------------------------------------------------------------
1 | ### App编译流程
2 | #### ⼀个Android应⽤从开始编译到打包⼤致上会经历三个步骤:编译前检查--> 编译(资源⽂件和源码)-->打包签名
3 | - 我们以最熟悉的Android Studio开发应⽤为例,⾸先,我们把Gradle构建项⽬从本⽂中剖离开,不讨论具体的Gradle构建是怎么回事,只关注于代码是如何通过⼀系列的Android编译⼯具最终⽣成APK的。
4 | - 编译步骤如下:
5 | - 1. ⾸先在编译阶段,会对资源⽂件⽂件进⾏检查,包括AndroidManifest.xml 、res⽬录下的所有⽂件(⽂件名是否合理)
6 | - 2.在检查通过后,对资源⽂件进⾏处理,⼀次编译⽣成resources.arsc 和 R.java⽂件
7 | - 3.在完成资源编译后,会针对res⽬录下的xml⽂件和AndroidManifest.xml分别进⾏编译,这样处理过的xml⽂件就被简单的“加密”(如果你解压⼀个apk,会发现所有的xml布局⽂件都是乱码),
8 | - 4.最后处理后的res&AndroidManifest.xml&resources.arsc会打包压缩成resources.ap_⽂件,在AndroidStudio的项⽬下/app/build/intermediates/res目录下可以看到编译打包后⽣成的资源打包⽂件。
9 | - 5.如果项⽬中使⽤了NDK来编译native代码,则也会在这个阶段编译并⽣成.so库⽂件。在apkbuilder的时候作为三⽅库⽂件打包进应⽤。
10 | - 6.处理AIDL⽂件,在编译阶段会将.aidl的⽂件编译⽣成相应的java代码供程序调⽤。
11 | - 7.编译⼯程源码,⽣成相应的class⽂件,位于项⽬ /app/build/intermediates/classes ⽬录下。
12 | - 8.在class⽂件⽣成后,如果使⽤了混淆,则会调⽤proguard.jar⽂件对class⽂件和资源⽂件进⾏混淆优化处理,同时⽣成混淆的mapping⽂件,如果没有使⽤混淆,则直接转换所有的class⽂件⽣成classes.dex⽂件。
13 | - 9.打包⽣成APK⽂件。
--------------------------------------------------------------------------------
/android/基础/Binder与AIDL.md:
--------------------------------------------------------------------------------
1 | ## Linux内核部分基础
2 | ### 进程隔离/虚拟地址空间
3 | - 进程之间是不允许互相访问的,内存是不共享的;
4 | - 每一个进程分为用户空间和内核空间,用户空间 和 内核空间也是隔离的。
5 | - 用户空间和内核空间之间相互通信只能通过系统调用,例如通过native的API,copy_from_user,copy_to_user
6 | - 我们说的内存空间 只是拟内存,只是一个地址,会映射到具体的物理地址。不同进程内核空间映射的都是同一块,内核空间是共享的;用户空间每个进程映射到不同的物理地址
7 |
8 |
9 | ### 系统调用
10 | - 用户可以从应用层通过系统调用来访问内核的方法
11 |
12 | ### 多进程的情况
13 | - 虚拟机给各个进程的运行内存是有限的,回收时优先回收对系统资源占用过多的进程
14 | - 突破内存限制,如图库占用内存过多
15 | - 功能稳定性,独立的通信进程需要保持长链接的稳定性,如推送
16 | - 规避系统内存泄漏:独立的webview进程阻隔内泄漏导致的问题
17 | - 隔离风险:不稳定功能放入独立进程,避免导致主进程崩溃
18 | - 和系统服务,系统提供的功能通信
19 |
20 | ### binder驱动
21 | - 相当于硬件接口
22 |
23 | ## Binder通信机制介绍
24 | - Binder是跨进程的通信机制
25 | - 对Server来说,Binder指的是Binder本地对象,对Client来说,Binder指的是Binder代理对象
26 | - 对传输过程,Binder是可以进行跨进程传递的对象,会自动完成服务器和客户端对象转换
27 |
28 | ### 为什么使用binder
29 | 
30 | - Android使用Linux内核,它有很多快进程通信机制,IPC通信,如管道,消息队列,共享内存,信号量,socket,文件等
31 | - 性能:广泛IPC会提出性能要求
32 | - 安全:传统IPC没有对通信双方进行身份验证,Binder双方支持通信双方身份校验
33 |
34 | ### Binder通信模型
35 | - 客户端持有一个服务代理,代理对象协助驱动,完成跨进程通信。
36 | 
37 | - 1.Server在ServiceManager,注册某方法add
38 | - 2.Client从ServiceManager中查询是否有该方法,如果有,SM会返回Client一个代理空方法。
39 | - 3.当Client调用该方法时,他会返回给内核驱动,内核驱动调用Server的add方法,把结果返回给驱动,驱动返回给Client
40 |
41 | ### Binder是如何做到一次拷贝的
42 | 
43 | - Binder机制中,接收方的用户空间 与 共享的内核空间 有一块共享区域的物理内存,这样用户空间就可以直接拿到 共享区域的数据了。
44 | - 而共享内存机制无需拷贝,是因为 发送方和接收方都映射到同一块内存了,容易导致死锁
45 |
46 | ### mmap的原理
47 | - Linux通过一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,称作内存映射(Memory Mapping)
48 | - 对文件进行mmap,会在进程的虚拟内存分配空间,建立映射关系。
49 | - 之后就可以采用指针的方式读写这一段内存,系统会自动回写到对应的文件磁盘上
50 |
51 | ### Binder机制是如何跨进程的
52 | - 普通方式:发送方与接收方之间,用户空间不共享,但内核空间是共享的
53 | - binder中binder_mmap方法会开辟一块物理空间,让内核 和接收方 共同映射到这一块物理空间(也就是文件)。
54 | - 发送方通过copy_from_user把数据拷贝到内核空间指定的虚拟内存后,这样 接收方也可以拿到。
55 |
56 | #### AIDL实现进程通信
57 | - 要实现两个进程间的通信,要通过操作系统,因此要约束一定的规则。
58 | - AIDL 是一种接口自定义语言,用来实现设备上两个进程间的通信
59 | - 进程间的通信信息会被妆画成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后在转化成相应的对象。
60 |
61 |
62 | ### 描述AIDL的java类细节(手写AIDL)
63 | - 客户端ClientActivity,服务端RemoteService
64 | ```java
65 | // 客户端 接收IBinder对象,之后才能用 iPersonManager 调用addPerson方法
66 | private ServiceConnection connection = new ServiceConnection(){
67 | public void onServiceConnected(ComponentName name,IBinder service){
68 | iPersonManager = Stub.asInterface(service);
69 | }
70 | public void onServiceDisconnected(ComponentName name ){
71 | iPersonManager = null;
72 | }
73 | }
74 |
75 | ```
76 | ```java
77 | import java.util.ArrayList;
78 | // 服务端
79 | public class RemoteService extends Service {
80 |
81 | private ArrayList psersons;
82 |
83 | public IBinder onBinder(Intent intent) {
84 | persons =new ArrayList<>;
85 | return iBinder;
86 | }
87 |
88 | private IBinder iBinder = new Stub() {
89 | public void addPerson(Pserson person) {
90 | psersons.add(person)
91 | }
92 |
93 | public List getPsersonList(){
94 | return psersons;
95 | }
96 | }
97 | }
98 | ```
99 | - 三个类:
100 | - 接口IPersonManager类,定义需要调用的功能方法类名。接口的实现在RemoteService,通过IBinder跨进程接口实现的对象返会给客户端。
101 | - 代理类Proxy,需要实现IPersonManager接口。之后 客户端就可以拿到iPersonManager对象调用 addPerson方法了。这个iPersonManager对象就是由IBinder返回来的。由于是跨进程的,客户端拿到的实际上是代理的,需要进行转换:iPersonManager = Stub.asInterface(service)
102 | - 代理类Proxy,实现的方法中,有两个包 客户端发送的数据data, 和 服务端返回的数据reply。然后调用 remote.transact()方法,客户端刮起,进入到驱动,驱动进行一系列处理,会进入服务端stub的onTransact方法。
103 | - Stub类 继承Binder 表示这个服务端可以跨进程,是抽象类,具体的方法实现由服务端实现。服务端会把接口提供的方法设置id来区分,Stub的onTransact方法,里面会通过ID来确定客户端调用的是哪个方法,然后到真正实现的地方,如果有返回值,就会放到reply里面。
104 | ```java
105 | // Proxy类 实现了 IPersonManager 接口
106 | public void addPerson(Person person){
107 | Parcel data = Parcel.obtain();
108 | Parcel replay = Parcel.obtain();
109 | // ...
110 |
111 | }
112 | public List getPsersonList(){
113 | return psersons;
114 | }
115 | ```
116 |
117 | ### 四大组件底层通信机制
118 | - 以BindService为例,上述已经讲完了。
119 |
120 | ### 为什么Intent不能传递大数据
121 | - 最大限制是一个具体的值 1M-8K 一兆减八k
122 | - 服务端在注册的时候,会调用mmap方法去开辟一块共享空间,这块空间的大小就是限制。在底层源码中明确设置了 1*1024*1024 - 4096*2
123 | - 实际上比这个数字还要小。类似于网络通信,数据会有包头,命令等,跟数据无关项。因此数据传递的时候要打包。
124 | - 如果同步的话 要小于1M-8K,异步的话 要小于(1M-8K)/2
125 | - java层 Binder.java代表binder
126 | - natvie层 BBinder,javaBBinder代表binder
127 | - 在驱动层binder_node结构体代表binder
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/android/基础/Choreographer.md:
--------------------------------------------------------------------------------
1 | ## Choreographer “编舞者”
2 |
3 |
4 | ### 参考致谢
5 | - https://www.huzhamiao.com/blog/resource/1902.html
--------------------------------------------------------------------------------
/android/基础/Context.md:
--------------------------------------------------------------------------------
1 | ## Context继承关系
2 | - Context是一个接口,ContextImp 和 ContextWrapper 都是其实现类。
3 | - Activity,Service,Application都是直接或者间接继承自ContextWrapper
4 | 
5 | ## 面试问题
6 | ### 1. 一个应用有几个Context
7 | - Application,Activity,Service都继承自Context,而应用有几个进程就会有几个Application对象。
8 | - Context个数 = Activity个数 + Service个数 + 进程个数
9 | ### 2. 为什么Activity、Service、Application都继承自Context,Context的作用是什么
10 | - Context是一个接口,定义了一系列标准和规范,因为有了Context,各种应用组件才能访问系统服务,系统资源。
11 | - 其源码如下:
12 | ```java
13 | public abstract class Context {
14 |
15 | // 四大组件相关
16 | public abstract void startActivity(@RequiresPermission Intent intent);
17 | public abstract void sendBroadcast(@RequiresPermission Intent intent);
18 | public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
19 | IntentFilter filter);
20 | public abstract void unregisterReceiver(BroadcastReceiver receiver);
21 | public abstract ComponentName startService(Intent service);
22 | public abstract boolean stopService(Intent service);
23 | public abstract boolean bindService(@RequiresPermission Intent service,
24 | @NonNull ServiceConnection conn, @BindServiceFlags int flags);
25 | public abstract void unbindService(@NonNull ServiceConnection conn);
26 | public abstract ContentResolver getContentResolver();
27 |
28 | // 获取系统/应用资源
29 | public abstract AssetManager getAssets();
30 | public abstract Resources getResources();
31 | public abstract PackageManager getPackageManager();
32 | public abstract Context getApplicationContext();
33 | public abstract ClassLoader getClassLoader();
34 | public final @Nullable T getSystemService(@NonNull Class serviceClass) { ... }
35 |
36 | public final String getString(@StringRes int resId) { ... }
37 | public final int getColor(@ColorRes int id) { ... }
38 | public final Drawable getDrawable(@DrawableRes int id) { ... }
39 | public abstract Resources.Theme getTheme();
40 | public abstract void setTheme(@StyleRes int resid);
41 | public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }
42 |
43 | // 获取应用相关信息
44 | public abstract ApplicationInfo getApplicationInfo();
45 | public abstract String getPackageName();
46 | public abstract Looper getMainLooper();
47 | public abstract int checkPermission(@NonNull String permission, int pid, int uid);
48 |
49 | // 文件相关
50 | public abstract File getSharedPreferencesPath(String name);
51 | public abstract File getDataDir();
52 | public abstract boolean deleteFile(String name);
53 | public abstract File getExternalFilesDir(@Nullable String type);
54 | public abstract File getCacheDir();
55 | // SharePreference
56 | public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
57 | public abstract boolean deleteSharedPreferences(String name);
58 |
59 | // 数据库相关
60 | public abstract SQLiteDatabase openOrCreateDatabase(...);
61 | public abstract boolean deleteDatabase(String name);
62 | public abstract File getDatabasePath(String name);
63 |
64 | // 其它
65 | public void registerComponentCallbacks(ComponentCallbacks callback) { ... }
66 | public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }
67 | ...
68 | }
69 | public interface ComponentCallbacks {
70 | void onConfigurationChanged(Configuration newConfig);
71 | void onLowMemory();
72 | }
73 | ```
74 | - 通过分析源码,我们可以知道,Context作用包含以下几个方面:
75 | 1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
76 | 2. 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等
77 | 3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
78 | 4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
79 | 5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生
80 |
81 | ### 3. 为什么Activity需要继承自ContextThemeWrapper,而Service和Application直接继承自ContextWrapper?
82 | ```java
83 | // ContextThemeWrapper的源码
84 | public class ContextThemeWrapper extends ContextWrapper {
85 | private int mThemeResource;
86 | private Resources.Theme mTheme;
87 | private LayoutInflater mInflater;
88 | private Configuration mOverrideConfiguration;
89 | private Resources mResources;
90 |
91 | public ContextThemeWrapper() {
92 | super(null);
93 | }
94 |
95 | public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
96 | super(base);
97 | mThemeResource = themeResId;
98 | }
99 |
100 | public ContextThemeWrapper(Context base, Resources.Theme theme) {
101 | super(base);
102 | mTheme = theme;
103 | }
104 |
105 | @Override
106 | protected void attachBaseContext(Context newBase) {
107 | super.attachBaseContext(newBase);
108 | }
109 |
110 | public void applyOverrideConfiguration(Configuration overrideConfiguration) {...}
111 |
112 | public Configuration getOverrideConfiguration() {...}
113 |
114 | @Override
115 | public AssetManager getAssets() {...}
116 |
117 | @Override
118 | public Resources getResources() {...}
119 |
120 | private Resources getResourcesInternal() {...}
121 |
122 | @Override
123 | public void setTheme(int resid) {...}
124 |
125 | @Override
126 | public int getThemeResId() {...}
127 |
128 | @Override
129 | public Resources.Theme getTheme() {...}
130 |
131 | @Override
132 | public Object getSystemService(String name) {...}
133 |
134 | protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
135 |
136 | private void initializeTheme() {...}
137 | }
138 | ```
139 | - 其内部包含了与Theme相关的接口,当然,只有Activity才需要主题,Service和Application是不需要主题的,因为Service是没有界面的后台场景,所以Service和Application直接继承于ContextWrapper
140 |
141 | ### 4. 为什么ContextWrapper中存在一个ContextImp类型的变量mBase,同时它又实现了Context呢
142 | ```java
143 | public class ContextWrapper extends Context {
144 | Context mBase;
145 | public ContextWrapper(Context base) {
146 | mBase = base;
147 | }
148 | protected void attachBaseContext(Context base) {
149 | if (mBase != null) {
150 | throw new IllegalStateException("Base context already set");
151 | }
152 | mBase = base;
153 | }
154 | public Context getBaseContext() {
155 | return mBase;
156 | }
157 | @Override
158 | public AssetManager getAssets() {
159 | return mBase.getAssets();
160 | }
161 | @Override
162 | public Resources getResources() {
163 | return mBase.getResources();
164 | }
165 | @Override
166 | public PackageManager getPackageManager() {
167 | return mBase.getPackageManager();
168 | }
169 | @Override
170 | public ContentResolver getContentResolver() {
171 | return mBase.getContentResolver();
172 | }
173 | }
174 | ```
175 | - ContextWrapper只是一个Context静态代理类,所有的操作都是通过内部成员 mBase 完成的,而mBase就是ContextImp对象。
176 | - 一般情况下,使用代理而不直接使用某个对象,目的可能有两个:1.定制自己的行为,2.不影响原对象
177 | - 其中 Servcie 和 Application 的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为
178 | - 对于 Service 和 Application 而言,不直接继承ContextImp,是担心用户修改了ContextImp而导致错误的发生
179 | - 对于 Activity 而言,除了担心用户的修改之外,ContextImp和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的
180 |
181 | ## 使用Context注意点
182 | ### 1.单例模式中使用Context注意内存泄漏
183 | - 单例模式的生命周期是和应用的生命周期保持一致的,所以在单例模式中使用Context,不能使用Activity Context,需要使用Application Context
184 | ### 2.创建dialog需要使用Activtiy Context
185 | ### 3.Activity的this和getBaseContext()有什么区别?
186 | - Activity就是继承Context的,所以this是返回Activity自己 ,getBaseContext()返回的是ContextWrapper里面的mBase
187 | ### 4.getApplication()和getApplicationContext()有什么区别?
188 | - 都是返回Applicatoin对象,但是他们的作用域不一样,getApplicatoin() 是Activity和Service里面特有的,其他地方不能用;BroadcastReceiver的onReceive()中的第一个参数,拿到的Context对象是不能调用getApplication()的只能调getApplicationContext()
189 | ### 5.Application构造方法中调用父类方法会发生闪退(如getResource()等)
190 | #### Application的创建过程主要是下面三步
191 | ```java
192 | //1.创建ContextImpl
193 | ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
194 | //2.创建Application
195 | Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
196 | //3.调用Application#onCreate()
197 | app.onCreate();
198 | ```
199 | #### Instrumentation#newApplication()中
200 | ```java
201 | public Application newApplication(ClassLoader cl, String className, Context context)
202 | throws InstantiationException, IllegalAccessException,
203 | ClassNotFoundException {
204 | Application app = getFactory(context.getPackageName())
205 | .instantiateApplication(cl, className);
206 | app.attach(context);
207 | return app;
208 | }
209 | ```
210 | #### Application#attach()
211 | ```java
212 | final void attach(Context context) {
213 | attachBaseContext(context);
214 | mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
215 | }
216 | ```
217 | #### Application#attachBaseContext()
218 | ```java
219 | protected void attachBaseContext(Context base) {
220 | if (mBase != null) {
221 | throw new IllegalStateException("Base context already set");
222 | }
223 | mBase = base;
224 | }
225 | ```
226 | - 在Application对象刚被创建的时候,其内部的mBase变量是空的,直到执行attachBaseContext()后,mBase才会有值,之后才会调用Application#onCreate()。所以在Application中使用Context接口中的相关方法,可以在onCreate()里面调用,而不能在构造方法中调用
227 |
228 | ## 总结
229 | - 借用原作者的话就是:Context是一个抽象类,Activity就像是一个”皇帝“,Activity可以做很多很多的事情,但是Context就像是他手中的权利,如果没有Context,Activity其实只是一个”普通人“(普通java类)而已。
230 | - 很多人往往把Activity理解成它继承了Context,是的没错,它确实继承自Context,但我认为,把Activity理解成它代理了Context,会更贴合实际意义一些
231 |
232 | ### 参考致谢
233 | - https://www.jianshu.com/p/492ec35ea552
--------------------------------------------------------------------------------
/android/基础/Fragment.md:
--------------------------------------------------------------------------------
1 | ### Fragment为什么被称为第五大组件
2 | #### 为什么被称为第五大组件
3 | - 在使用频率上,不输四大组件,它不是独立的,必须加载在Activity中,可以灵活展现UI
4 |
5 | #### Fragment加载到Activity两种方式
6 | - 静态加载到activity布局中。
7 | - 通过FragmentManager创建transaction,调用transaction的添加或者替换方法,最后调用commit方法完成fragment加载,利用容器资源作为标志位,设置所要显示的位置。
8 |
9 | #### FragmentPagerAdapter和FragmentStatePagerAdaper区别
10 | - FragmentPagerAdapter 页面较少,会保存内存,在destroyItem方法中,调用了detach方法,并不是真正的内存回收,把fragment的ui和activity的ui脱离开来。
11 | - FragmentStatePagerAdaper 用于页面较多,每次切换都会回收内存,在destroyItem方法中,调用了remove方法,真正释放了内存,节省内存。
12 |
13 | ### Fragment生命周期
14 | 
15 | 
16 |
17 | ### Fragment之间的通信
18 | - 在Fragment中调用Activity方法:getActivity方法会获得Activity对象
19 | - 在Activity中调用Fragment方法:通过接口回调
20 | - Fragment中调用Fragment方法:getActivity 获取到Activity,findFragmentById 获取Fragment
21 | - Activity在创建的时候通过Bundle传输数据
22 | - viewmodel
23 |
24 | ### FragmentManageer中方法
25 | - repalce:销毁原有的Fragment,创建新的Fragemnt
26 | - add:只是添加,不销毁
27 | - remove:销毁
28 | - 当Fragment不可见时,如果你要保留Fragment中的数据以及View的显示状态,那么可以使用add操作,后续中针对不同的状态隐藏和显示不同的Fragment。
29 | - 当Fragment不可见时,你不需要保留Fragment中的数据以及View的显示状态,那么可以使用replace。
30 |
31 | ### 懒加载
32 | - https://www.bilibili.com/video/BV1oa4y1J7qC?p=5&spm_id_from=pageDriver&vd_source=a77b2d1e92c4bda82707549b246c89fe
33 | ### 懒加载是怎么优化的?通过预加载
34 | - ArrayList<>里面以前保存了fragment的大量数据的view,通过懒加载,arrayList里面缓存了空白的fragment
35 | - 空白的fragment 是可见的时候加载,在可见的时候把空白的给替换掉
36 | - 预加载是不可以避免的
37 |
38 | ### 参考致谢
39 | - https://www.bilibili.com/video/BV1CL4y1a7Uz?p=4
--------------------------------------------------------------------------------
/android/基础/Framework层App启动流程.md:
--------------------------------------------------------------------------------
1 | ### AMS,PMS,以及WMS等都是运⾏在system_server这个进程中的线程
2 | #### AMS
3 | - ActivityManagerService 简称AMS,是Android内核的核心功能之一,在系统启动SystemServer时启动此服务。 AMS在Android系统中扮演很重要的角色,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似。当发起进程启动或者组件启动时,都会通过Binder通信机制将请求传递给AMS,AMS再做统一处理。
4 |
5 | #### WMS
6 | - WindowManagerService 负责管理系统中所有的窗口,包括Activity的窗口、壁纸窗口、输入法窗口、弹窗子窗口等,即管理屏幕上展示上的一切窗口。在Android系统系统的过程中,在SystemServer进程中也把WMS服务启动起来,注册到ServiceManager中。
7 |
8 | #### PMS
9 | - PMS⽤来管理所有的package信息,包括安装、卸载、更新以及解析AndroidManifest.xml以组织相应的数据结构,这些数据结构将会被PMS、ActivityMangerService等等service和application使⽤到
10 |
11 | ### App启动流程
12 | 
13 | #### 四大进程
14 | - Launcher进程:app启动流程的起点,负责接收用户点击屏幕事件
15 | - app进程:你要启动的app所运行的进程。
16 | - SystemServer进程:它是属于application Framework层的,Android中的所有服务,例如AMS, WindowsManager, PackageManagerService等等都是由这个SystemServer fork出来的
17 | - Zygote进程:Zygote是Android系统中的进程,由用户空间的第一个进程Init进程启动的,是Android系统运行的第一个AndroidRuntime进程,同时也是打通Native和Java的桥梁。创建SystemServer进程;是用于管理整个Java framework层,包含ActivityManager,PowerManager等各种系统服务;孵化其他应用程序进程。
18 |
19 | #### 六大类
20 | - ActivityManagerService:(AMS)AMS是Android中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,它本身也是一个Binder的实现类
21 | - Instrumentation:监控应用程序和系统的交互
22 | - ActivityThread:应用的入口类,通过调用main方法,开启消息循环队列。ActivityThread所在的线程被称为主线程
23 | - applicationThread:applicationThread提供Binder通讯接口,AMS则通过代理调用此app进程的本地方法
24 | - ActivityManagerProxy:AMS服务在当前进程的代理类,负责与AMS通信
25 | - applicationThreadProxy:applicationThread在AMS服务中的代理类,负责与applicationThread通信。
26 |
27 | #### 启动流程总结
28 | - ①点击桌面app图标,Launcher进程采用Binder向system_server进程发起startActivity请求
29 | - ②system_server进程接收到请求后,通过socket向zygote进程发送创建进程的请求
30 | - ③Zygote进程fork出新的子进程,即app进程
31 | - ④app进程通过Binder向sytem_server进程发起attachapplication请求
32 | - ⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder向app进程发送scheduleLaunchActivity请求
33 | - ⑥app进程的applicationThread线程在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息
34 | - ⑦主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。
35 | - ⑧到此,app便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到app的主界面。
36 | - 参考致谢:https://baijiahao.baidu.com/s?id=1730780037727135079&wfr=spider&for=pc
37 |
38 | ### AMS对Activity启动管理,Activity的启动流程
39 | - Activity启动最终会调用start()方法,而start()方法里面最终会跑到startActivity
40 | - startActivity最终会跑到startActivityForResult(),startActivityForResult()里面执行的是 Instrumentation.execStartActivity()
41 | - 通过Binder跨进程,执行的AMS中的ATM的startActivity,接下来以此分析。首先来看是怎么拿到ATM服务的
42 | - 首先会调用AcitvityTaskManager.getService,而getService是通过ServiceManager通过代理模式拿到的ATM服务
43 | - 执行ATM的startActivity方法,判断需要启动的Activity所在的App进程是否已经启动
44 | - 如果进程存在,最终会执行到realStartActivityLocked方法,如果进程不存在,创建出进程后,最终也会执行到realStartActivityLocked方法
45 | - 如果进程不存在,执行mService.startProcessAsync,AMS会创建socket连接zygote ,通过socket传递ActivityThread给zygote
46 | - zygote通过反射去直接执行ActivityThread的main方法,在main方法中会执行thread.attach方法,内部拿到AMS,
47 | - Activity通过跨进程调用AMS的attachApplication把ActivityThread对象传递给AMS
48 | - AMS执行到realStartActivityLocked方法,去启动Activity,执行activity生命周期
49 | - 参考致谢:https://www.jianshu.com/p/d6562ac93767
50 | - 参考致谢:https://www.bilibili.com/video/BV1TF411u7m2?spm_id_from=333.337.search-card.all.click
51 | 
52 | - 
53 |
54 | ### Activity的onCreate之前执行的流程
55 | 
56 | - 在启动activity的时候,ActivityThread 会调用scheduleLaunchActivity -> handleLaunchActivity -> performLaunchActivity
57 | - 在 performLaunchActivity 中会调用Activity的attach方法创建PhoneWindow并却与Activity绑定,同时创建windowManager
58 | - 之后ActivityThread 会调用 Instrumentation的 callActivityOnCreat 方法,callActivityOnCreat 会调用 Activity 的onCreate方法
59 | - Activity 的onCreate方法 会把生命周期分到我们真正的要启动的Activity中去,执行他们的onCreate方法并且setContentView解析xml布局
--------------------------------------------------------------------------------
/android/基础/Intent.md:
--------------------------------------------------------------------------------
1 | ### Intent
2 | - Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,通过 Intent 可以实现各种系统组件的调用与激活
3 |
4 | ### 显示意图
5 | - 显式意图:调用Intent.setComponent()或Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给哪个组件,它一般用在知道目标组件名称的前提下,显式Intent更多用于在应用程序内部传递消息。
6 |
7 | ### 隐式意图
8 | - 隐式意图:没有明确指定组件名的Intent为隐式意图,通过Intent Filter来实现的,Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(Uri和数据类型)找到最合适的组件来处理这个意图,它一般用在没有明确指出目标组件名称的前提下,它更广泛地用于在不同应用程序之间传递消息。
9 |
10 |
--------------------------------------------------------------------------------
/android/基础/RecycleView原理回收复用.md:
--------------------------------------------------------------------------------
1 | ### RecyclerView架构中核心组件
2 | - 1.回收池:是一个View的集合,能回收任意Item控件,并返回符合类型的Item控件,比如onBinderHolder方法中的第一个参数就是从回收池中返回的
3 | - 2.适配器:Adapter接口,辅助RecyclerView实现列表展示,适配器模式,将用户界面展示与交互分离
4 | - 3.RecyclerView,根据用户触摸反馈,协调回收池对象与适配器对象之间的工作
5 |
6 | ### RecyclerView的复用机制,View的回收与复用过程
7 | - 加载无限view,不会发生卡顿,为什么?
8 | - 第一屏加载方式:recyclerView 加载第一屏,将需求交给回收池,而回收池中是空的,就会找适配器,适配器中会协调onCreateViewHolder,它会返回一个View,这样依次把第一屏加载满。
9 | - 第一屏的第一个view划出去了:会把划出去的View放到栈中,它是多个栈,因为view对应不同的布局类型。回收的时候有个type类型。
10 | - 第二屏加载:底部出现空缺,触发加载机制,从回收池中找到对应的view,通过Adapter中onBinderViewHolder进行数据刷新
11 |
12 | ### 多种布局类型
13 | - 重写getItemViewType ,和 getViewTypeCount 来实现不同类型的布局
14 |
15 | ### RecyclerView支持多个不同类型的布局,他们怎么缓存并查找的
16 | - 1.在回收池中有不同类型的stack集合,回收池中通过type来区分不同类型的View
17 | - 2.回收池是为了回收复用经常存或者取的view
18 |
19 | ### RecyclerView适配器的原理,为什么要适配器模式
20 | - 适配器就是为了 ui和处理业务的分离
21 |
22 | ### RecyclerView中每一个View的tag是空的吗?
23 | - 不是空的,打tag是通过键值对的方式。因为在移除view的时候是获取不到type值的,所以在填充view的时候,需要把type打进tag值,在remove的时候获取。
24 | - 用到键值对是因为要把tag值留给用户用。
25 |
26 | ### RecyclerView一屏加载个数是怎么确定的,他是怎么做到不显示item的Item缓存到内存中
27 | - onLyaout中确定的。在填充过程中,每填充一个item,那它的bottom都会加上它的高度,直到大于等于屏幕的高度,我们就认为填充完。
28 |
29 | ### 手写RecyclerView
30 |
31 | ### notifyDataSetChanged 刷新原理,观察者模式
32 | -
33 |
34 | ### notifyItemChanged 刷新原理
35 | - Adapter.notifyItemChanged(int position, @Nullable Object payload) 方法会导致 RecyclerView 的 onMeasure() 和 onLayout() 方法调用。
36 | - 在 onLayout() 方法中会调用 dispatchLayoutStep1()、dispatchLayoutStep2() 和 dispatchLayoutStep3() 三个方法
37 | - 其中 dispatchLayoutStep1() 将更新信息存储到 ViewHolder 中
38 | - dispatchLayoutStep2() 进行子 View 的布局,dispatchLayoutStep3()触发动画。在dispatchLayoutStep2()中,会通过DefaultItemAnimator的canReuseUpdatedViewHolder()方法判断position处是否复用之前的ViewHolder,如果调用notifyItemChanged()时的payload不为空,则复用;否则,不复用。在dispatchLayoutStep3()中,如果position处的ViewHolder与之前的ViewHolder相同,则执行DefaultItemAnimator的move动画;如果不同,则执行DefaultItemAnimator的change动画,旧View动画消失(alpha值从1到0),新View动画展现(alpha值从0到1),这样就出现了闪烁效果。
39 |
40 |
41 |
42 | ### AsyncListDiffer原理(性能提升)
43 |
44 | ### 参考致谢
45 | - https://www.bilibili.com/video/BV1Fi4y1x7p5?spm_id_from=333.337.search-card.all.click
--------------------------------------------------------------------------------
/android/基础/SharePreference.md:
--------------------------------------------------------------------------------
1 | ## 简单介绍
2 | - SharedPreferences(以下统称为sp)是Android提供的数据持久化的一种手段,适合单进程、小批量的数据存储与访问
3 | - 由于sharedPreferences是基于xml文件实现的,所有持久化数据都是一次性加载,如果数据过大是不适合采用SP存放。
4 | - 实际上是用xml文件存放数据,文件存保存放在/data/data//shared_prefs/
5 |
6 | ### 实现原理
7 | #### 基本使用
8 | ```java
9 | mSharedPreferences = context.getSharedPreferences("test", Context.MODE_PRIVATE);
10 | SharedPreferences.Editor editor = mSharedPreferences.edit();
11 | editor.putString(key, value);
12 | editor.commit(); // 线程安全,性能慢,同步操作,在当前线程完成写文件操作
13 | editor.apply(); // 线程不安全的,性能高,是异步处理IO操作
14 | ```
15 | #### context.getSharedPreferences其实就是简单的调用ContextImpl的getSharedPreferences,具体实现如下:
16 | ````java
17 | @Override
18 | public SharedPreferences getSharedPreferences(String name, int mode) {
19 | SharedPreferencesImpl sp;
20 | synchronized (ContextImpl.class) {
21 | if (sSharedPrefs == null) {
22 | sSharedPrefs = new ArrayMap>();
23 | }
24 |
25 | final String packageName = getPackageName();
26 | ArrayMap packagePrefs = sSharedPrefs.get(packageName);
27 | if (packagePrefs == null) {
28 | packagePrefs = new ArrayMap();
29 | sSharedPrefs.put(packageName, packagePrefs);
30 | }
31 | sp = packagePrefs.get(name);
32 | if (sp == null) {
33 |
34 | File prefsFile = getSharedPrefsFile(name);
35 | sp = new SharedPreferencesImpl(prefsFile, mode);
36 |
37 | packagePrefs.put(name, sp);
38 | return sp;
39 | }
40 | }
41 |
42 | if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
43 | getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
44 | sp.startReloadIfChangedUnexpectedly();
45 | }
46 | return sp;
47 | }
48 | ````
49 | - SharePreference 先去内存中查询与xml对应的SharePreferences是否已经被创建加载,如果没有那么该创建就创建,然后加载,在加载之后,要将所有的key-value保存到内存中。
50 | #### 数据加载是new SharedPreferencesImpl 对象时候开始的
51 | ```java
52 | SharedPreferencesImpl(File file, int mode) {
53 | mFile = file;
54 | mBackupFile = makeBackupFile(file);
55 | mMode = mode;
56 | mLoaded = false;
57 | mMap = null;
58 | startLoadFromDisk();
59 | }
60 | ```
61 | #### startLoadFromDisk很简单,就是读取xml配置,如果其他线程想要在读取之前就是用的话,就会被阻塞,一直wait等待,直到数据读取完成。
62 | ````java
63 | private void loadFromDiskLocked() {
64 | ...
65 | Map map = null;
66 | StructStat stat = null;
67 | try {
68 | stat = Os.stat(mFile.getPath());
69 | if (mFile.canRead()) {
70 | BufferedInputStream str = null;
71 | try {
72 |
73 | str = new BufferedInputStream(
74 | new FileInputStream(mFile), 16*1024);
75 | map = XmlUtils.readMapXml(str);
76 | }...
77 | mLoaded = true;
78 | ...
79 |
80 | notifyAll();
81 | }
82 | ````
83 | #### 使用xml解析工具XmlUtils,直接在当前线程读取xml文件,所以,如果xml文件稍大,尽量不要在主线程读取,读取完成之后,xml中的配置项都会被加载到内存,再次访问的时候,其实访问的是内存缓存。
84 | ### 数据的更新
85 | - Editor是一个接口,这里的实现是一个EditorImpl对象,它首先批量预处理更新操作,之后再提交更新,在提交事务的时候有两种方式,一种是apply,另一种commit,两者的区别在于:何时将数据持久化到xml文件,前者是异步的,后者是同步的。
86 | - Google推荐使用前一种,因为,就单进程而言,只要保证内存缓存正确就能保证运行时数据的正确性,而持久化,不必太及时,这种手段在Android中使用还是很常见的,比如权限的更新也是这样,况且,Google并不希望SharePreferences用于多进程,因为不安全
87 | #### 两者源码
88 | ```java
89 | public void apply() {
90 |
91 | final MemoryCommitResult mcr = commitToMemory();
92 | final Runnable awaitCommit = new Runnable() {
93 | public void run() {
94 | try {
95 | mcr.writtenToDiskLatch.await();
96 | } catch (InterruptedException ignored) {
97 | }
98 | }
99 | };
100 |
101 | QueuedWork.add(awaitCommit);
102 | Runnable postWriteRunnable = new Runnable() {
103 | public void run() {
104 | awaitCommit.run();
105 | QueuedWork.remove(awaitCommit);
106 | }
107 | };
108 |
109 | SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
110 |
111 | notifyListeners(mcr);
112 | }
113 |
114 | public boolean commit() {
115 | MemoryCommitResult mcr = commitToMemory();
116 | SharedPreferencesImpl.this.enqueueDiskWrite(
117 | mcr, null /* sync write on this thread okay */);
118 | try {
119 | mcr.writtenToDiskLatch.await();
120 | } catch (InterruptedException e) {
121 | return false;
122 | }
123 | notifyListeners(mcr);
124 | return mcr.writeToDiskResult;
125 | }
126 | ```
127 | #### 从上面可以看出两者最后都是先调用commitToMemory,将更改提交到内存,在这一点上两者是一致的,之后又都调用了enqueueDiskWrite进行数据持久化任务,不过commit函数一般会在当前线程直接写文件,而apply则提交一个事务到已给线程池,之后直接返回,实现如下:
128 | ```java
129 | private void enqueueDiskWrite(final MemoryCommitResult mcr,
130 | final Runnable postWriteRunnable) {
131 | final Runnable writeToDiskRunnable = new Runnable() {
132 | public void run() {
133 | synchronized (mWritingToDiskLock) {
134 | writeToFile(mcr);
135 | }
136 | synchronized (SharedPreferencesImpl.this) {
137 | mDiskWritesInFlight--;
138 | }
139 | if (postWriteRunnable != null) {
140 | postWriteRunnable.run();
141 | }
142 | }
143 | };
144 | final boolean isFromSyncCommit = (postWriteRunnable == null);
145 | if (isFromSyncCommit) {
146 | boolean wasEmpty = false;
147 | synchronized (SharedPreferencesImpl.this) {
148 | wasEmpty = mDiskWritesInFlight == 1;
149 | }
150 |
151 | if (wasEmpty) {
152 | writeToDiskRunnable.run();
153 | return;
154 | }
155 | }
156 | QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
157 | }
158 | ```
159 | #### 其实简单来说,如果硬要说两者的区别的话,直接说commit同步,apply异步也不是没有问题,坦白来说,sp不适用于多线程
160 | ### SP的多线程
161 | - 其实在sp创建的时候可以指定的加载模式中有个MODE_MULTI_PROCESS,它是Google提供的一个在多线程模式。但是这种模式并不是我们说的支持多进程同步更新等,它的作用只会在getSharedPreferences的时候,才会重新从xml重加载,如果我们在一个进程中更新xml,但是没有通知另一个进程,那么另一个进程的SharePreferences是不会自动更新的
162 | ```java
163 | @Override
164 | public SharedPreferences getSharedPreferences(String name, int mode) {
165 | SharedPreferencesImpl sp;
166 | ...
167 | if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
168 | getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
169 | // If somebody else (some other process) changed the prefs
170 | // file behind our back, we reload it. This has been the
171 | // historical (if undocumented) behavior.
172 | sp.startReloadIfChangedUnexpectedly();
173 | }
174 | return sp;
175 | }
176 | ```
177 | #### MODE_MULTI_PROCESS只是个花瓶,对于多线程的支持几乎为0,简单来说,最好不需要用
178 |
179 |
180 | ### 总结
181 | - 1.SharePreferences是Android基于xml实现的一种数据持久化手段
182 | - 2.SharePreferences不支持多进程
183 | - 3.SharePreferences的commit与apply一个是同步一个是异步(大部分场景下)
184 | - 4.不要使用SharePreferences存储太大的数据
185 | - 5.不要使用SharePreferences进行使用多线程调用
186 |
187 | ### 参考致谢
188 | - 作者:RainyJiang
189 | - https://juejin.cn/post/6926825172066369544
--------------------------------------------------------------------------------
/android/基础/WebView.md:
--------------------------------------------------------------------------------
1 | ### WebView的启动过程
2 | 
3 |
4 | ### 如何提高WebView的加载速度
5 | #### 1.常规设置
6 | - 1.提高渲染的优先级:webSettings.setRenderPriority(RenderPriority.HIGH);
7 | - 2.使把图片加载放在最后来加载渲染: webSettings.setBlockNetworkImage(true);
8 | - 3.开启H5(APPCache)缓存功能:webSettings.setAppCacheEnabled(true);
9 | - 4.webView数据缓存分为两种:AppCache和DOM Storage(Web Storage)。
10 | - 5.开启 DOM storage 功能:webSettings.setDomStorageEnabled(true);
11 | - 6.应用可以有数据库:webSettings.setDatabaseEnabled(true);
12 |
13 | #### 2.预加载WebView。
14 | - App中打开WebView的第一步并不是建立连接,而是启动浏览器内核。 常见的方法是全局WebView,在客户端刚启动时,就初始化一个全局的WebView待用,并隐藏;这种方法可以比较有效的减少WebView在App中的首次打开时间。当用户访问页面时,不需要初始化WebView的时间。
15 | - 当然这也带来了一些问题,包括: 额外的内存消耗。 页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。
16 |
17 | #### 3.asset存放离线包
18 |
19 | ### Java调用JS
20 | #### 1.evaluateJavascript()方式
21 | ```java
22 | //javascript示例
23 | //(1)javascript = "javascript:javaCallJsNoParam()"
24 | //(2)javascript = "javascript:javaCallJs(" + "'" + numebr + "'" + ")"
25 | webView.evaluateJavascript(javascript, new ValueCallback() {
26 | @Override
27 | public void onReceiveValue(String response) {
28 | //response为响应数据
29 | }
30 | }
31 | });
32 |
33 | ```
34 | #### 2.loadUrl()方式
35 | ```java
36 | // js中
37 | // function javaCallJsNoParam(){
38 | // document.getElementById("result").innerHTML= 'JAVA调用JS成功!';
39 | // }
40 | //java调用
41 | webView.loadUrl("javascript:javaCallJsNoParam()");
42 | ```
43 | ### JS调用Java
44 | #### 1.addJavascriptInterface()方式将提供的Java对象注入到此WebView中。
45 | ```java
46 | class JsObject {
47 | @JavascriptInterface
48 | public String toString() { return "injectedObject"; }
49 | }
50 | webview.getSettings().setJavaScriptEnabled(true);
51 | webView.addJavascriptInterface(new JsObject(), "injectedObject");
52 | webView.loadData("", "text/html", null);
53 | webView.loadUrl("javascript:alert(injectedObject.toString())");
54 | ```
55 | #### 2.通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
56 | #### 3.Android通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt(方法回调分别拦截JS对话框(即上述三个方法),得到他们的消息内容,然后解析即可。
57 |
58 | ### WebView内存泄漏
59 | #### WebView内存泄漏原因
60 | - WebView 内存泄露的主要原因是引用了 Activity/Fragment 的 Context,加之 WebView 本身的设计问题,导致 Activity/Fragment 无法被即时释放
61 |
62 | #### WebView内存泄漏解决
63 | - 不要使用 xml 方式创建,而是使用代码把 WebView 给 new 出来
64 | - 引用全局的 Context:val webview = WebView(this.applicationContext)
65 | - 为了保证 Activity/Fragment 界面销毁时将 WebView 从其父控件中移除 ,让 WebView 停止加载页面并释放
66 | ```kotlin
67 | override fun onDestroy() {
68 | // webview?.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
69 | // webview?.clearView()
70 | webview?.loadUrl("about:blank")
71 | webview?.parent?.let {
72 | (it as ViewGroup).removeView(webview)
73 | }
74 | webview?.stopLoading()
75 | webview?.settings?.javaScriptEnabled = false
76 | webview?.clearHistory()
77 | webview?.clearCache(true)
78 | webview?.removeAllViewsInLayout()
79 | webview?.removeAllViews()
80 | webview?.webViewClient = null
81 | webview?.webChromeClient = null
82 | webview?.destroy()
83 | webview = null
84 | super.onDestroy()
85 | }
86 |
87 | ```
88 | ### 低版本WebView的漏洞
89 | #### 远程代码执行漏洞
90 | - 由于程序没有正确限制使用addJavascriptInterface方法,当JS拿到Android这个对象后,通过Java反射机制,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime类),从而进行任意代码执行。
91 | - 解决:如果一定要使用addJavascriptInterface接口,需使用以下方法:Android4.2以上,允许被JavaScript调用的方法必须以@JavascriptInterface进行注解声明,从而避免漏洞攻击。Android4.2以下,采用setWebChromeClient重新onJsPrompt()拦截prompt()消息进行交互。
92 |
93 | #### 密码明文存储漏洞
94 | - WebView默认开启密码保存功能 :mWebView.setSavePassword(true),开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码; 如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险。
95 | - 解决:通过 WebSettings.setSavePassword(false) 关闭密码保存提醒功能,防止明文密码存在本地被盗用。
96 |
97 | #### 域控制不严格漏洞
98 | - A应用可以通过B应用导出的Activity(android:exported=“true”),让B应用加载一个恶意的file 协议的url,从而可以获取B应用的内部私有文件,从而带来数据泄露威胁。
99 | - 解决:对于不需要使用file协议的应用,禁用file协议,setAllowFileAccess(false); 对于需要使用 file 协议的应用,允许file协议加载 JavaScript。
100 |
101 | ### JsBridge原理
102 | - JSBridge 就像其名称中的Bridge的意义⼀样,是 Native 和⾮ Native 之间的桥梁,它的核⼼是构建 Native 和⾮ Native 间消息通信的通道,⽽且这个通信的通道是双向的。
103 | - JS 调⽤ Native 逻辑:接收到 JavaScript 消息 => 解析参数,拿到 bridgeName、data 和 callbackId => 根据 bridgeName 找到功能⽅法,以 data 为参数执⾏ => 执⾏返回值和 callbackId ⼀起回传前端。
104 | - Native调⽤ JS 也同样简单,直接⾃动⽣成⼀个唯⼀的 ResponseId,并存储句柄,然后和 data ⼀起发送给前端即可。
105 | 
106 |
--------------------------------------------------------------------------------
/android/基础/resource/AMS与WMS交互.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/AMS与WMS交互.png
--------------------------------------------------------------------------------
/android/基础/resource/Activity启动时序2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Activity启动时序2.png
--------------------------------------------------------------------------------
/android/基础/resource/Activity启动时序图.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Activity启动时序图.png
--------------------------------------------------------------------------------
/android/基础/resource/Activity界面层级.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Activity界面层级.png
--------------------------------------------------------------------------------
/android/基础/resource/Activity的onCreate之前流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Activity的onCreate之前流程.png
--------------------------------------------------------------------------------
/android/基础/resource/Activity的启动流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Activity的启动流程.png
--------------------------------------------------------------------------------
/android/基础/resource/Binder性能.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Binder性能.png
--------------------------------------------------------------------------------
/android/基础/resource/Binder机制.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Binder机制.png
--------------------------------------------------------------------------------
/android/基础/resource/Binder机制跨进程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Binder机制跨进程.png
--------------------------------------------------------------------------------
/android/基础/resource/Context继承关系.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Context继承关系.png
--------------------------------------------------------------------------------
/android/基础/resource/Fragment生命周期.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Fragment生命周期.png
--------------------------------------------------------------------------------
/android/基础/resource/Handler所有类.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/Handler所有类.png
--------------------------------------------------------------------------------
/android/基础/resource/JSBridge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/JSBridge.png
--------------------------------------------------------------------------------
/android/基础/resource/ThreadLocal与Looper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/ThreadLocal与Looper.png
--------------------------------------------------------------------------------
/android/基础/resource/ViewGroup的Draw过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/ViewGroup的Draw过程.png
--------------------------------------------------------------------------------
/android/基础/resource/ViewGroup的Layout过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/ViewGroup的Layout过程.png
--------------------------------------------------------------------------------
/android/基础/resource/ViewGroup的measure过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/ViewGroup的measure过程.png
--------------------------------------------------------------------------------
/android/基础/resource/View与Window逻辑结构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/View与Window逻辑结构.png
--------------------------------------------------------------------------------
/android/基础/resource/View的Draw过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/View的Draw过程.png
--------------------------------------------------------------------------------
/android/基础/resource/View的Measure过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/View的Measure过程.png
--------------------------------------------------------------------------------
/android/基础/resource/View绘制.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/View绘制.png
--------------------------------------------------------------------------------
/android/基础/resource/WebView的启动过程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/WebView的启动过程.png
--------------------------------------------------------------------------------
/android/基础/resource/handleResumeActivity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/handleResumeActivity.png
--------------------------------------------------------------------------------
/android/基础/resource/invalidate刷新流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/invalidate刷新流程.png
--------------------------------------------------------------------------------
/android/基础/resource/事件分发.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/事件分发.png
--------------------------------------------------------------------------------
/android/基础/resource/事件流程源码.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/事件流程源码.png
--------------------------------------------------------------------------------
/android/基础/resource/生产者消费者.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/生产者消费者.png
--------------------------------------------------------------------------------
/android/基础/resource/生命周期.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/生命周期.png
--------------------------------------------------------------------------------
/android/基础/resource/生命周期交织.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/基础/resource/生命周期交织.png
--------------------------------------------------------------------------------
/android/基础/四大组件.md:
--------------------------------------------------------------------------------
1 | ## Activity
2 | ### 生命周期
3 | - onCreate onStart onResume onPause onStop onDestroy
4 | - onStart 可见与 onStop 不可见;onResume 可编辑(即焦点)与 onPause
5 |
6 | ### 安卓进程优先级
7 | - 前台进程:与用户前台交互的,或者在前台activity绑定了service
8 | - 可见进程:activity并不是处于前台,用户不能点击
9 | - 服务进程:在后台开启service服务
10 | - 后台进程:按了Home键,后台进程不会立马kill掉
11 | - 空进程:不属于以上进程,没有活跃组建,安卓随时杀掉它
12 |
13 | ### 如何保存 Activity 的状态或者(Activiy 重启怎么保存数 据?)
14 | - 当系统内存不足时, 调用 onPause()和 onStop()方法后的 activity 可能会被系统摧毁, 此时内存中就不会存有该 activity 的实例对象了。如果之后这个 activity 重新回到前台, 之前所作的改变就会消失。
15 | - 为了避免此种情况的发生, 我们可以覆写 onSaveInstanceState()方法。onSaveInstanceState()方法接受一 个 Bundle 类型的参数, 开发者可以将状态数据存储到这个 Bundle 对象中, 这样即使 activity 被系统摧毁 , 当用户重新启动这个 activity 而调用它的 onCreate()方法时, 上述的 Bundle 对象会作为实参传递给 onCreate()方法, 开 发者可以从 Bundle 对象中取出保存的数据, 然后利用这些数据将 activity 恢复 到被摧毁之前的状态。
16 | - 如果调用 onSaveInstanceState()方 法, 调用将发生在 onPause()或 onStop()方法之前。
17 |
18 | ### stardand模式下:从A跳转B
19 | - A 会调用 onPause()方法,然后 B 调用 onCreate() ,onStart(), onResume()。这个时候 B 覆盖了窗体, A 会调用 onStop()方法。
20 | - B返回A:B 调用onPause,A调用 onRestart,onStart,onResume,B调用onStop,onDestroy。
21 |
22 | ### A是singleTask,B是stardand
23 | 
24 |
25 | ### scheme跳转协议
26 | - 是一种页面跳转协议,通过定义自己的scheme协议,可以方便的跳转app中的各个页面。可以通过通知栏跳转,H5跳转等。
27 |
28 | ### 横竖屏切换时 Activity 的生命周期(★★★★)
29 | - Android横竖屏切换时会触发onSaveInstanceState(),而还原时会调用onRestoreInstanceState()
30 | - 1.不设置 Activity 的 android:configChanges 时,首先销毁当前 activity,然后重新加载。onPause-onStop-onSaveInstanceState-onDestroy-onCreate-onStart-onRestoreInstanceState-onResume
31 | - 2.设置 Activity android:configChanges="orientation|keyboardHidden|screenSize"时,切 屏不会重新调用各个生命周期,只会执行 onConfigurationChanged
32 |
33 | ### Task任务栈
34 | - 安卓通过任务栈来管理activity。任务栈并不唯一。
35 |
36 | ### Activity的四种启动模式
37 | - standard 会不断地新建activity实例,都放⼊同⼀个task中,即每次转到到该Activity就会创建⼀个本activity的实例。适用于大多数场景。
38 | - singleTop 栈内复用:如果当前实例在当前的task的栈顶,则直接复⽤,如果当前实例不再栈顶,则新建实例。应用场景:通知消息打开的页面,登录页面。
39 | - singleTask 栈顶复用:只要是task栈中有这个实例,就会⼀直复⽤,⽽且每次复⽤这个已存在的实例,都会清空上⾯的其他实例, 将⾃⼰直接提升到栈顶位置,显⽰⾃⼰。对于不同的task栈,则会创建该实例。应用场景:返回主页面。
40 | - singleInstance 新开栈:初始时都是新建⼀个task栈给这个页⾯,然后后⾯⼀直复⽤这个栈内的页⾯,具有全局唯⼀性,即整个系统中只会存在⼀个这样的实例。应用场景:系统闹钟页面、来电显示、系统通讯录。
41 |
42 | ### onNewIntent
43 | - 前提:ActivityA已经启动过,处于当前应用的Activity栈中
44 | - SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()-onRestart()-onStart-onResume
45 | - SingleInstance、SingleTask :onPause—>跳转其它页面—>onCreate—>onStart—>onResume—onPause—>跳转A—>onNewIntent—>onRestart—>onStart—>onResume
46 |
47 | ### Android 中的 Context, Activity,Appliction 有 什么区别?(★★)
48 | - 相同:Activity 和 Application 都是 Context 的子类。 Context 从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理 上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源。
49 | - 一个应用 Context 数量=Activity 个数+service个数+1(Application)
50 | - 不同:维护的生命周期不同。 Context 维护的是当前的 Activity 的生命周期, Application 维护的是整个项目的生命周期。
51 |
52 | ### 使用 context 的时候,小心内存泄露,防止内存泄露,注意一下几个方面
53 | - 1.不要让生命周期长的对象引用 activity context,即保证引用 activity 的对象 要与 activity 本身生命周期是一样的。
54 | - 2.对于生命周期长的对象,可以使用 application,context。
55 | - 3.避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化。
56 |
57 | ### 两个 Activity 之间传递数据,除了 intent,广播接 收者,content provider 还有啥?
58 | - 利用 static 静态数据,public static 成员变量
59 | - viewModel数据持久化
60 | - 利用外部存储的传输,File,SharePreferences,Sqlite
61 |
62 | ### activity和Service传值
63 | - 在 activity 中通过 startService(intent)即可,同样 intent.putStringExtra(), 然后再 service 中的 onStart 函数中获取该值,this.getIntent(),intent.getString()
64 | - service 可以从 public int onStartCommand(Intent intent, int flags, int startId)中取出从 activity 中传过来的值。intent.getExtra()获得 bundle 对象, 可从中取值。
65 | - activity 也可以用 bindService(intent, conn,BIND_AUTO_CREATE);传值,把要传的值绑定在 intent 里,在 service 的 public IBinder onBind(Intent intent) 方法里取得 intent。
66 | - 同时也可以在 reseiver 里面注册一个广播,在 activity 里 sendbroadcast (intent)传值。 可以传递图片,用 Intent 把图片的地址或者把图片对象用 Intent 传过去,用 bitmap 对象。
67 |
68 | ## Service
69 | ### Service 是否在 main thread 中执行, service 里面是否能执 行耗时的操作?(★★)
70 | - Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。 service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 ) 特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行
71 |
72 | ### Service启动
73 | - 在 Activity 中可以通过 startService 和 bindService 方法启动 Service。一 般情况下如果想获取 Service 的服务对象那么肯定需要通过 bindService()方 法,比如音乐播放器,第三方支付等。如果仅仅只是为了开启一个后台任务那么 可以使用 startService()方法。
74 |
75 | ### Service生命周期
76 | - 一个原则是 Service 的 onCreate 的方法只会被调用一次,就是你无论多少次的 startService 又 bindService,Service 只被创建一次。
77 | - 如果先是 bind 了,那么 start 的时候就直接运行 Service 的 onStart 方法, 如果先是 start,那么 bind 的时候就直接运行 onBind 方法。
78 | - 如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,就需要startService和bindService一起使用了。
79 |
80 | #### 1. 通过 startService
81 | - onCreate —》onStartCommand —》onStart
82 | - 而后,多次调用startService:onStartCommand —》onStart
83 | - 调用stopServcie:—》onDestroy
84 | - 通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
85 |
86 | #### 2. 通过 bindService
87 | - onCreate —》onBind —》(onServiceConnected)
88 | - 多次调用bindService,服务本身未执行任何操作。所以一次unBindService就能结束服务。(若多次调用unBindService,第一次有用,后面会出错)
89 | - 调用unBindService:onUnbind —》onDestroy
90 | - bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。
91 |
92 | ### 先startService,后bindService
93 | - onCreate —》onStartCommand —》onStart —》onBind —》(onServiceConnected)
94 | - 先unBindServcie,后stopService:unBindService会执行到onUnbind,stopService会执行到onDestroy。
95 | - 先stopService,后unBindServcie:onUnbind —》onDestroy。需注意的是:stopService不会执行任何操作,unBindService会执行到onUnbind—》onDestroy。
96 |
97 | ### 先bindServcie,后startService
98 | - onCreate —》onBind —》(onServiceConnected) —》onStartCommand —》onStart
99 | - 先unBindService,后stopService 与 先stopService,后unBindService。服务结束的执行过程:onUnbind —》onDestroy
100 |
101 | ### IntentService
102 | - 一个封装了HandlerThread和Handler的异步框架。
103 | - 会创建独立的 worker 线程来处理所有的 Intent 请求;
104 | - 会创建独立的 worker 线程来处理 onHandleIntent()方法实现的代码,无需处理多线程问题;
105 | - 所有请求处理完成后,IntentService 会自动停止,无需调用 stopSelf()方法 停止 Service;
106 | - 为 Service 的 onBind()提供默认实现,返回 null;
107 | - 为 Service 的 onStartCommand 提供默认实现,将请求 Intent 添加到队列 中;
108 | - HandlerThread会串行的取出任务并且执行,会调用ServiceHandler的handleMessage去处理任务。
109 | - handlerMessage会去调用我们自定义的onHandleIntent
110 | - 任务执行完毕后通过stopSelf(startId)停止Service。
111 | - 任务结束后,在onDestory()中会退出HandlerThread中Looper的循环。
112 |
113 | ### BroadcastReceiver广播
114 | - 通过 Android 系统的 Binder 机制实现通信。
115 | - 静态注册:在清单文件中进行如下配置,只要 app 在系统中运行则一直可以接收到广播消息。
116 | - 动态注册:在代码中进行如下注册,当注册的Activity或者Service销毁了那么就接收不到广播 了。
117 | - 为什么这么设计?底层原理是什么?binder机制
118 |
119 | ## ContentProvider
120 | ### ContentProvider 是如何实现数据共享的(★★★)
121 | - ContentProvider 是应用程序之间共享数据的接口。使用的时候首先自定义 一个类继承 ContentProvider,然后覆写 query、insert、update、delete 等 方法。因为其是四大组件之一因此必须在 AndroidManifest 文件中进行注册。 把自己的数据通过 uri 的形式共享出去,android 系统下 不同程序 数据默认 是不能共享访问,需要去实现一个类去继承承 ContentProvider第三方可以通过 ContentResolver 来访问该 Provider。
122 |
123 | ### 为什么要用 ContentProvider?它和 sql 的实现上有什么差 别?(★★★)
124 | - ContentProvider 屏蔽了数据存储的细节,内部实现对用户完全透明,用户只 需要关心操作数据的 uri 就可以了,ContentProvider 可以实现不同 app 之间 共享。
125 | - Sql 也有增删改查的方法,但是 sql 只能查询本应用下的数据库。而 ContentProvider 还可以去增删改查本地文件. xml 文件的读取等。
126 |
127 | ### 说说 ContentProvider、ContentResolver、ContentObserver 之间的关系(★★★★)
128 | - ContentProvider 内容提供者,用于对外提供数据
129 | - ContentObserver 内容监听器,可以监听数据的改变状态
130 | - ContentResolver.notifyChange(uri)发出消息
131 | - ContentResolver 内容解析者,用于获取内容提供者提供的数据
132 | - ContentResolver.registerContentObserver()监听消息。
133 |
134 | ### 使用 contentProvider 获取本地所有的音频文件(★★★)
135 | - Android 中,系统为多媒体类型的文件(比如图片、音频、视频等)建立了 数据库(sqlite 数据库),将文件的名称、大小等信息存储起来,从而完成多媒体 数据的维护工作;
136 | - 可以根据 ContentResover 获取到一个 Cursor,然后根据这个游标,遍历所 有的歌曲的信息,设置给实体类,得到你想要的音频文件。因为是从本地读取数 据,所以需要添加读权限
137 |
138 | ### 参考致谢
139 | - https://www.bilibili.com/video/BV1CL4y1a7Uz?p=2&spm_id_from=pageDriver
--------------------------------------------------------------------------------
/android/基础/布局.md:
--------------------------------------------------------------------------------
1 | https://www.jianshu.com/p/568e3c22e9db
2 | https://www.jianshu.com/p/28bc3619fc82
3 | https://www.jianshu.com/p/502127a493fb
4 |
5 |
6 | android:layout_width="wrap_content"
7 | 与 app:layout_constrainedWidth="true" 组合使用
8 |
--------------------------------------------------------------------------------
/android/基础/自定义View.md:
--------------------------------------------------------------------------------
1 | ### 自定义View
2 | - 重写构造方法
3 | - 重写onMeasure测量
4 | - 重写onDraw
5 |
6 | ### 自定义ViewGroup
7 | - 重写构造方法
8 | - 重写onMeasure测量
9 | - 重写onLayout
10 |
11 | ### Activity界面层级的刷新
12 | 
13 | - Activity的刷新是从上到下的,decorView都不存在,view是没办法刷新的
14 |
15 | ### View的绘制在Activity的哪个生命周期方法执行的?activity与window与view一起工作的?(图中两个流程都要掌握)
16 | 
17 | 
18 | - ⚠️:addView是创建流程,在addView之后,setView中调用的requestLayout进行view的刷新
19 | - 在执行完 Activity的 onResume 方法之后,才真正开始了View的绘制工作
20 | - 1.AMS:UI的绘制实际上是在ActivityThread中 handleResumeActivity 方法中执行的, 首先会执行 ActivityClientRecord r = performResumeActivity(token, clearHide);而performResumeActivity就是内部最终会调用Activity的onResume生命周期。所以在onResume之前是没有进行ui绘制的
21 | - 2.WMS:handleResumeActivity中 ViewManager wm= a.getWindowManager()来获取(windowManager是从Framework层## Activity的onCreate之前执行的流程 涉及到创建的)
22 | - 3.window的出现是用来缓和activity和view的关系,绑定activity和view,建立联系之后ui就开始渲染了,所以activity启动的时候就会创建phonewindow同时创建WindowManager
23 | - 4.setContentView中会创建decorview,并未跟Activity产生联系,在 handleResumeActivity方法中 赋值给Activity.mDecor 才完成绑定
24 | - 5.之后 wm会调用UpdateViewLayout方法。(wm是在attach中建立的。wm实际上调用的mGlobal.updateViewLayout,WindowManagerGlobel是WindowManager的一个实现类,WM会把ui刷新会交给WindowManagerGlobel来进行,而WMG会把渲染交给ViewRootImpl来执行)
25 | - 6.WindowManagerGlobel#updateViewLayout中获取到ViewRootImpl对象root,调用它的 ViewRootImpl#setlayoutParams -> ViewRootImpl#setlayoutParams -> ViewRootImpl#requestLayout -> ViewRootImpl#scheduleTraversals(这才是ui绘制的起点)-> doTraversals() -> performTraversals()
26 | - 7.scheduleTraversals、doTraversals 中 都是用handler也是post一个消息。
27 | - 参考致谢:https://www.bilibili.com/video/BV1uK4y1G7Mo?p=3&spm_id_from=pageDriver
28 |
29 | ### 为什么在子线程中不能更新ui
30 | ```java
31 | @Override
32 | public void requestLayout() {
33 | if (!mHandlingLayoutInLayoutRequest) {
34 | checkThread();
35 | mLayoutRequested = true;
36 | scheduleTraversals();
37 | }
38 | }
39 |
40 | void checkThread() {
41 | if (mThread != Thread.currentThread()) {
42 | throw new CalledFromWrongThreadException(
43 | "Only the original thread that created a view hierarchy can touch its views.");
44 | }
45 | }
46 | ```
47 | - 从以上的源码分析可得知,ViewRootImpl对象是在onResume方法回调之后才创建,那么就说明了为什么在生命周期的onCreate方法里,甚至是onResume方法里都可以实现子线程更新UI,因为此时还没有创建ViewRootImpl对象,并不会进行是否为主线程的判断;
48 | - scheduleTraversals()里是对View进行绘制操作,而在绘制之前requestLaout中,都会调用checkThread()检查当前线程是否为主线程mThread,如果不是主线程,就抛出异常;这样做法就限制了开发者在子线程中更新UI的操作;
49 | - 访问UI是没有加对象锁的,在子线程环境下更新UI,会造成不可预期的风险;
50 | - 参考致谢:https://blog.csdn.net/cpcpcp123/article/details/121779098
51 |
52 | ### View绘制和加载过程
53 | 
54 | 
55 | - ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的绘制流程开始于ViewRoot的performTraversals()方法,只有经过measure、layout、draw三个流程才能最终把View绘制出来
56 | - 当Activity对象创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将该对象和DecorView建立关联,在ViewRootImpl里面performTraversals分发。
57 | - 这个流程在onCreate之前,所以会有一段时间的黑屏、白屏。所以在启动的时候解决黑白屏问题,我们会设置主题,因为在onCreate之前会把主题设置进来,之后才会加载xml布局中的问题。
58 | - performTraversals()依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级View的绘制。
59 | - 其中performMeasure()会调用view的measure(),measure()中又调用view的onMeasure(),实现对其所有子元素的measure过程,这样就完成了一次measure过程;
60 | - 接着子元素会重复父容器的measure过程,如此反复至完成整个View树的遍历
61 | - onLayout和onDraw跟measure方法类似
62 |
63 | #### View的measure过程
64 | 
65 | - View的测量过程首先会调用View的measure()方法,而measure()方法又会调用onMeasure()方法实现具体的测量。onMeasure()主要通过setMeasuredDimension()方法来设置View宽高的测量值
66 | - View的实际测量宽高是通过getDefaultSize()方法来获取的(返回值实际上就是View的SpecSize),该方法的主要逻辑是:根据传进来的View的MeasureSpec,获取对应的SpecMode值和SpecSize值,并判断SpecMode三种测量模式下对应的View的SpecSize的取值。
67 | - 在这里主要关注EXACTLY和AT_MOST两种模式,这两种模式下都是直接返回View的SpecSize值,这个SpecSize就是View的测量宽高大小。
68 | - UNSPECIFIED测量模式的话,则会使用getSuggestedMinimumWidth()和getSuggestedMinimumHeight()提供的默认大小,那么默认大小是多少呢?通过getSuggestedMinimumWidth()方法可以看到:如果View没有设置背景,那么View的测量宽度等于XML布局文件中android:minWidth属性指定的值,如果没有指定则默认为0;如果View设置了背景,那么View的测量宽度等于android:minWidth属性指定的值与背景图Drawable的原始宽度(若无原始宽度则默认为0)两者中的最大值。
69 |
70 | #### ViewGroup的measure过程
71 | 
72 | - ViewGroup的测量过程除了完成自身的测量之外,还会遍历去调用子View的measure()方法。
73 | - ViewGroup是一个抽象类,没有重写View的onMeasure()方法,所以需要子类去实现onMeasure()方法规定具体的测量规则。
74 | - ViewGroup子类复写onMeasure()方法一般有如下三个步骤:
75 | - (1)遍历所有子View并测量其宽高,直接调用ViewGroup的measureChildren()方法;
76 | - (2)合并计算所有子View测量的宽高,最终得到父View的实际测量宽高;
77 | - (3)存储父View实际测量宽高值;
78 | - ViewGroup中提供了measureChildren()方法,该方法主要遍历所有的子View并调用其measureChild()方法,measureChild()主要的逻辑是:取出子View的LayoutParams参数,结合传进来的父View的MeasureSpec参数,通过getChildMeasureSpec()来计算并创建子View的MeasureSpec
79 | - getChildMeasureSpec()方法主要获取父View测量规格中的SpecMode值和SpecSize值,并根据三种SpecMode模式结合子View的LayoutParams参数计算出子View的SpecMode值和SpecSize值,并通过makeMeasureSpec()方法创建对应的MeasureSpec测量规格,然后再把MeasureSpec传递给子View的measure()方法进行测量
80 | - 如此递归下去遍历所有的子View并测量子View的宽高从而得出ViewGroup的实际测量大小。
81 |
82 | ### MeasureSpec测量规格
83 | #### MeasureSpec的定义
84 | - MeasureSpec是一个32位的int值,前2位表示SpecMode测量模式,后30位表示SpecSize测量大小;在一个View控件的measure过程中,系统会将这个View的LayoutParams结合父容器的MeasureSpec生成一个MeasureSpec,这个MeasureSpec即规定好了如何去测量这个View的规格大小。
85 | - 子View的MeasureSpec = 父View的MeasureSpec + 子View的LyaoutParams
86 |
87 | #### SpecMode有三种测量模式
88 | - UNSPECIFIED:不确定模式,父控件不会对子控件有任何约束;
89 | - EXACTLY:精确模式,父容器知道View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值;对应于LyaoutParams中的match_parent或具体数值。
90 | - AT_MOST:最多模式,父容器指定了一个可用的大小SpecSize,View的大小不能超过这个值,View的最终大小要看View的具体实现;对应于LyaoutParams中的wrap_content。
91 |
92 | #### View的MeasureSpec创建规则
93 | - 当子View的LyaoutParams为固定宽高时,不管父View的SpecMode是什么,子View的MeasureSpec = EXACTLY模式 + LyaoutParams的实际大小。
94 | - 当子View的LyaoutParams为match_parent时:如果父View的SpecMode为EXACTLY精确模式时,子View的MeasureSpec = EXACTLY模式 + 父容器的剩余空间;如果父View的SpecMode为AT_MOST最大模式时,子View的MeasureSpec = AT_MOST模式 + 父容器的剩余空间(不允许超过)。
95 | - 当子View的LyaoutParams为wrap_content时:不管父View的SpecMode是EXACTLY精确模式还是AT_MOST最大模式,子View的MeasureSpec的SpecMode总是AT_MOST模式且SpecSize不会超过父容器的剩余空间(SpecSize具体大小由子View实现)。
96 |
97 | #### 为什么ViewGroup要设计成抽象类自己去实现onMeasure
98 | - ViewGroup抽象类需要子类自己实现onMeasure()方法,因为不同的ViewGroup具有不同的布局特性(LinearLayout、RelativeLayout等),导致测量的细节也不一样,所以没有跟View测量过程一样做onMeasure()的统一处理。
99 |
100 | #### 如何正确获取View的测量宽高
101 | - onLayout()中去获取View的测量宽高和最终宽高,getMeasureWidth()和getMeasureHeight()用来获取测量宽高,getWidth()和getHeight()用来获取最终宽高。
102 |
103 | #### Handler与View的刷新关系
104 | -
105 |
106 | #### 在Activity启动时,如何正确获取一个View的宽高?在onResume中获取高度有效吗?
107 | - 在首次执行onResume中getWidth与getHeigh无效,为0。因为执行onResume的时候,decor还未与activity绑定。
108 | - 首次onResume中可以 view.post(Runnable)或者new handler.postDelay(runnable,1000)可以,new handler.post(runnable)不可以。
109 | - 调用view.post方法并不是实时调用,而是被存储在RunQueue类中的集合 mActions中,等外部调用executeActions方法,而executeActions是在View的dispatchAttachedToWindow方法中被调用。
110 | - 因为addView中 都是用handler也是post一个消息;而onResume中直接post(runnable)消息是在刷新addView之前执行
111 | - 再次调用onResume有效,再次调用时不会走onCreat,再次调用时已经完成了绘制。
112 | - 由于View的measure过程和Activity的生命周期是不同步的,所以无法保证Activity的onCreate()或者onResume()方法执行时某个View已经测量完毕,可以通过以下方法来解决:
113 | - (1)在onWindowFocusChanged()方法中获取View的宽高,该方法可能会被频繁调用;
114 | - (2)通过ViewTreeObserver的OnGlobalLayoutListener监听接口,当View树的状态发生改变或者View树内部的View的可见性发生改变时,就会回调onGlobalLayout()方法,在该方法中可以准确获取View的实际宽高;
115 |
116 | #### recycleview,listview,viewpager高度无效?
117 | - 都是因为onMeasure度量失效
118 |
119 | #### View的Layout过程
120 | - 首先调用View的layout()方法,在该方法中通过setFrame()方法来设定View的四个顶点的位置,即 初始化mLeft、mTop、mRight、mBottom这四个值,View的四个顶点一旦确定,那么View在父容器的位置也就确定了。
121 | - 接着会调用onLayout()方法确定所有子View在父容器中的位置,由于是单一View的layout过程,所以 onLayout()方法为空实现,因为没有子View(如果是ViewGroup需要子类实现 onLayout()方法)。
122 |
123 | #### ViewGroup的Layout过程
124 | 
125 | - 首先会调用自身layout()方法,但和View的layout过程不一样的是,ViewGroup需要子类实现onLayout()方法,循环遍历所有的子View并调用其layout()方法确定子View的位置,从而最终确定ViewGroup在父容器的位置。
126 |
127 | #### 如何实现ViewGroup的onLayout(),以LinearLayout为例
128 | - 在onLayout()中首先判断水平或者垂直方向进入相应的处理函数
129 | - 查看垂直处理函数layoutVertical()主要遍历所有子View并调用setChildFrame()
130 | - 在setChildFrame()中调用子View的layout()来确定每个子View的位置,从而最终确定自身的位置。
131 |
132 | #### View的Draw过程 和 ViewGroup的Draw过程
133 | 
134 | - drawBackground():绘制背景;
135 | - 保存当前的canvas层(不是必须的);
136 | - onDraw(): 绘制View的内容,这是一个空实现,需要子View根据要绘制的颜色、线条等样式去具体实现,所以要在子View里重写该方法;
137 | - dispatchDraw(): 对所有子View进行绘制;单一View的dispatchDraw()方法是一个空方法,因为单一View没有子View,不需要实现dispatchDraw ()方法,而ViewGroup就不一样了,它实现了dispatchDraw()方法去遍历所有子View进行绘制;
138 | - onDrawForeground():绘制装饰,比如滚动条;
139 | 
140 |
141 | ### 刷新View的方法
142 | 
143 | - invalidate(): 该方法递归调用父View的invalidateChildInParent()方法,直到调用ViewRootImpl的invalidateChildInParent()方法,最终触发ViewRootImpl的performTraversals()方法,此时mLayoutRequested为false,不会触发onMesaure()与onLayout()方法,但是会触发onDraw()方法 ;所以如果仅仅是内容的改变不涉及宽高和位置的改变,可以使用这个。
144 | - postInvalidate():该方法功能和invalidate()一样,只是它可以在非UI线程中调用。
145 | - requestLayout() :该方法会递归调用父窗口的requestLayout()方法,直到触发ViewRootImpl的performTraversals()方法,此时mLayoutRequested为true,会触发onMesaure()与onLayout()方法,不一定会触发onDraw()方法。如果是改变了目标控件的大小和位置则需要使用。
146 |
147 | ### 参考致谢
148 | - https://www.jianshu.com/p/a5ea8174d912
149 |
--------------------------------------------------------------------------------
/android/性能/Carsh优化.md:
--------------------------------------------------------------------------------
1 | ### android是否会断掉进程
2 | - android线程中异常会断掉进程
3 | - java线程中异常 会断掉 线程
4 |
5 | ### app崩溃的主要原因
6 | - 未捕获异常的崩溃:java中未捕捉异常的处理流程,native未捕获异常的处理流程
7 | - anr崩溃:ANR的处理方案,Service处理的ANR过程
8 | - 内存引起的崩溃:抖动,益出。实际上是前面两者的崩溃
9 |
10 | ### android未捕捉异常处理机制
11 | - 未捕捉异常出现会交给JVM处理,JVM收集对应的堆栈信息,然后调用dispatchUncaughtException,开始执行异常的分发处理流程
12 | - dispatchUncaughtException中,先判断有无异常预处理器,即uncaughtExceptionPreHandler,有的话,将会先调用异常预处理器的uncaughtException方法
13 | - 接下来获取异常处理器,并调用其uncaughtException方法。至此,整个异常分发处理流程完毕
14 | - App进程启动时,系统已经在RuntimeInit.java类中注册了一个默认的异常预处理器loggingHandler
15 | - 在App进程启动时,系统会自动注入loggingHandler对象,作为异常预处理器。当有未捕获异常发生时,以此会自动调用loggingHandler对象的uncaughtException方法,以完成默认的日志输出。
16 | - App启动时,系统会默认为其设置一个线程默认的异常处理器,当未捕获异常发生时,默认情况下的闪退就是这个线程默认的异常处理器,即KillApplicationHandler去具体触发的。
17 |
18 | ### anr崩溃参考卡顿优化
19 | - [卡顿优化](./android/性能/卡顿优化.md)
20 |
21 | ### 内存引起的崩溃参考内存优化
22 | - [内存优化](./android/性能/内存优化.md)
23 |
24 | ### 如何优化Crash
25 | - 首先讲卡顿崩溃的原因
26 | - 然后讲怎么去定位问题,等位代码
27 | - 最后解决解决
--------------------------------------------------------------------------------
/android/性能/resource/Activity启动流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/Activity启动流程.png
--------------------------------------------------------------------------------
/android/性能/resource/cpu和gpu区别.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/cpu和gpu区别.png
--------------------------------------------------------------------------------
/android/性能/resource/反射修改系统sdk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/反射修改系统sdk.png
--------------------------------------------------------------------------------
/android/性能/resource/可达性分析.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/可达性分析.png
--------------------------------------------------------------------------------
/android/性能/resource/堆区数据结构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/堆区数据结构.png
--------------------------------------------------------------------------------
/android/性能/resource/对象头.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/对象头.png
--------------------------------------------------------------------------------
/android/性能/resource/屏幕分类.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/屏幕分类.png
--------------------------------------------------------------------------------
/android/性能/resource/引发内存问题常见的十种优化.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/引发内存问题常见的十种优化.png
--------------------------------------------------------------------------------
/android/性能/resource/渲染机制主线.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/渲染机制主线.png
--------------------------------------------------------------------------------
/android/性能/resource/运行时.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能/resource/运行时.png
--------------------------------------------------------------------------------
/android/性能/保活.md:
--------------------------------------------------------------------------------
1 | ### 常见四种方式
2 | #### 1像素保活
3 | - 很久以前据说 QQ 就是用的这套方案,在 app 开熄屏、网络切换、接收短信等时候监听广播,拉起一个仅有1个透明像素的 Activity 使 app 变成前台服务来提升进程优先级,以达到尽可能不被 Kill 的目的。
4 | ```java
5 | @Override
6 | protected void onCreate(@Nullable Bundle savedInstanceState) {
7 | super.onCreate(savedInstanceState);
8 | setContentView(R.layout.activity_1);
9 |
10 | Window window = getWindow();
11 | window.setGravity(Gravity.LEFT|Gravity.TOP);
12 | WindowManager.LayoutParams layoutParams = window.getAttributes();
13 | layoutParams.width = 1;
14 | layoutParams.height = 1;
15 | layoutParams.x = 1;
16 | layoutParams.y = 1;
17 | window.setAttributes(layoutParams);
18 | }
19 |
20 | // 重要的配置,防止被最近的任务列表发现
21 | // 使用自定义透明style
26 |
27 | ```
28 | #### 前台服务
29 | ```java
30 | public class ForegroundService extends Service {
31 | private final static int SERVICE_ID = 1;
32 | @Nullable
33 | @Override
34 | public IBinder onBind(Intent intent) {
35 | return null;
36 | }
37 |
38 | @Override
39 | public void onCreate() {
40 | super.onCreate();
41 |
42 | if (Build.VERSION.SDK_INT= Build.VERSION_CODES.O){
89 | startJob(this);
90 | }
91 | return false;
92 | }
93 |
94 | private void startJob(Context context) {
95 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
96 | JobInfo.Builder builder = new JobInfo.Builder(27,
97 | new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName()))
98 | .setPersisted(true);
99 | if (Build.VERSION.SDK_INT图像刷新速度:跳帧 造成卡顿(展示的时候错过了)
17 | - 制图速度跟不上刷新速度:容易黑屏或白屏
18 | - android不能放任他们随便推数据和更新数据。安卓设置的是一秒钟60帧。1秒/60 = 16.66毫秒。
19 | - SurfaceFlinger所以就采用16.66毫秒启动一次帧缓冲区刷新。在刷新前判定,图像数据是否准备好,如果准备好就推送+刷新
20 | - SurfaceFlinger在发送垂直同步信号的时候,不会管别人的因素,只要一到16.66毫秒,同时满足数据准备完成 就会推送+刷新,否则将会等到下一个16.66毫秒完成
21 | - 16ms主要处理两件事:1.将UI对象转换成多边形和纹理 2.CPU传递数据到GPU,GPU进行绘制
22 |
23 | ### 跳造成条帧的因素
24 | #### 常规因素(写代码造成的)3条
25 | - 层级过多,当前cup 三四层不算什么
26 | - CPU处理加载或内部的for循环计算有超时
27 | - 当前绘图太多了,过度绘制
28 | #### 核心因素2条
29 | - 内存引起,数据业务和图像绘制可能不到16.66就完成了,可能会有STW(在ART虚拟机里面,对象的内存控件一般是12M,24M,36M,当内存满的是时候会调用GC,GC的时候会stop the world,暂停当前所有业务线程进行内存清理,但是垂直同步信号不会等你)例如View绘制时高频调用的方法内部新建对象,导致内存满造成频繁STW
30 | - 线程引起,时间片用完进行线程切换 ,IO阻塞时间 ,锁的排队等待时间,当前阻塞主线程的代码都有可能造成卡顿
31 |
32 |
33 | ### 定位问题
34 | #### systrace:分析卡顿原因,给我们去找事故原因
35 | - 主要是看当前线程状态哪一段呈现深色,例如出现大面积绿色,代表代码实际运行时间确实超过了,代表要么层级多,要么有耗时操作,要么绘制问题。
36 | - 如果大面积紫色,代表代码中有高频创建对象的代码,要么发生在事件,要么发生在draw里面
37 | - 如果大面积灰色,有锁
38 | - 如果大面积蓝色,系统资源不够
39 | - 如果大面积橙色,有io问题
40 |
41 | ### 定位代码
42 | #### BlockCannary
43 | - 本质原理是在主线程里面 处理消息之前 和之后的打印日志时间之差 看是否大于 时间阈值
44 |
45 |
46 | ### 解决问题
47 | - 具体看业务,看实际处理过程,看原因
48 | - loading动画下产生的问题
--------------------------------------------------------------------------------
/android/性能/启动优化.md:
--------------------------------------------------------------------------------
1 | ### Android系统启动流程及原理
2 | - 打开电源,引导芯片会将BootLoader引导进RAM
3 | - 然后拉起Linux系统,内核会开一个init进程,读取一个init.rc文件,会开启关键的服务(打电话,摄像头等)
4 | - init开启zygote进程,zygote会fork多个进程(第一个是system_server,JVM初始化,JNI等)
5 | - system_server进程主要功能管理Binder,里面有Binder线程池,进程之间是Binder通信,还有SystemServiceManager
6 | - 之后会开启 AMS,WMS,PMS等80多个。之后Luanch启动,app冷启动流程
7 |
8 | ### App启动流程
9 | - [App与Activity启动](../基础/Framework层App启动流程.md)
10 | - 冷启动走的是mInstrumentation.execStartActivity,zygote进程会fork出ActivityThrea并执行main方法。
11 | - 热启动走的是Binder通信
12 | - 
13 |
14 | ### App启动有哪些任务要处理?任务写在哪里?
15 | - 文件系统、数据库、网络
16 | - 签名校验、安全、隐私条款、权限的提示
17 | - 引导页、广告
18 | - 更新提示
19 | - 各种SDK初始化
20 |
21 | ### 如何查看方法内耗时时间与方法分析
22 | - adb shell am start -W 命令
23 | - profile工具
24 |
25 | ### 启动过程必处理的七个问题
26 | #### 1.黑白屏问题:图片或动画
27 | #### 2.setContentView 处理过度绘制
28 | - setContentView方法内部,会执行inflate解析布局文件。
29 | - 内部会循环调用rInflateChildren方法来解析子标签。
30 | - 如果ViewGroup层次过多,循环调用rInflateChildren会越多,方法区栈帧就会越多,会造成栈溢出
31 | #### 3.减少布局中ViewGroup的层次
32 | #### 4.尽量不要在欢迎页面 和 application的onCreate写初始化代码,那在哪里呢?
33 | - 在ActivityThread里面,会调用handleResumeActivity,里面会调用performResume,里面mInstrumentation会走onResume方法。
34 | - performResume 之后,会执行wm.addView(decor,1); 会通过桥接模式跑到ViewRootImpl里面走setView,之后会走requestLayout
35 | - 接下来就执行performTraversals-performMeasure-performLayout-performDraw 来绘制展现ui
36 | - 之后wms通过handler发送一个窗口焦点事件,走handleWindowFoucsChanaged(),分发窗口焦点事件,执行Activity的onWindowFocusChanged(true):第一个能交互的方法
37 | - 注:这个加载写到 欢迎界面 Activity#onWindowFocusChanged 中完成第一次初始化
38 | #### 5.使用启动框架(把任务进行多线程排序,拓扑排序) + 懒加载 + 预加载
39 | - 懒加载:ViewStup,HashMap扩容,ViewPager,Fragment,jetpack做分页的pagging,APT的执行,网络访问对象,数据库对象
40 | - 伪优化:欢迎页 两三秒做初始化,跳过button 设置很小,很难点
41 | - 预加载:根据业务需求,预加载本地数据,然后其他数据慢慢填上去
42 | #### 6.有些初始化放到contentProvider的onCreate里面执行
43 | #### 7.代码质量上的优化:阿里安卓开发规范(https://edu.aliyun.com/certification/cldt04)
44 |
45 | ### 字节码插桩
46 | - https://blog.csdn.net/shulianghan/article/details/120298969
--------------------------------------------------------------------------------
/android/性能/屏幕适配.md:
--------------------------------------------------------------------------------
1 | ### 屏幕适配核心原理
2 | - 分屏,水滴屏,曲面屏,刘海屏,全面屏,凹屏
3 | 
4 |
5 | #### 屏幕适配两个关键点
6 | - 核心一:要解决超长屏,的适配问题。超长评,以宽度为标杆,高度可以滚动
7 | - 核心二:防止内容被刘海遮挡。刘海屏如果不做适配,顶部会空出一块黑色区域,整体布局下移动。通过反射解决。
8 |
9 | #### 刘海屏
10 | - 刘海就是statusBar区域,安卓P Build.VERSION.SDK_INT = 28,全屏与刘海屏关系
11 | - 其中有个变量layoutInDisplayCutoutMode:DEFAULT 默认,默认情况,全屏页面不可用刘海区域,非全屏页面可以进行使用
12 | - NEVER:不允许使用刘海区域,会出现黑色效果,不占用刘海(窗口不允许和刘海屏重叠);
13 | - SHOT_EDGE:允许页面延伸到刘海区域。
14 | - 根据需求设置完参数后,还要获取到statusBar的高度,动态的把布局整体下移,空出statusBar的内容
15 |
16 | ### 传统屏幕适配
17 | - px = dp * density
18 | - density = dpi / 160
19 | - density屏幕密度
20 | - ppi(每寸存储多少颗粒)越大,越清晰。ppi减小了,意味着同样的像素要更多的尺寸去展示
21 | - 而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的
22 |
23 | ### 头条适配方案
24 | - DisplayMetrics 类中有 density = 2.75,width = 1080,height = 2061,scaledDensity=2.75,xdpi =397.565 ,ydpi=474.688
25 | - 如果我们想再所有设备上显示完全一致,是不现实的。因为屏幕高宽比是不固定的。只需要保证所有设备中宽的维度显示一致即可
26 | - px = dp * density,设计稿上宽度的像素px是定死的,我们动态控制density去适配不同机型
27 | - 假设设计图的宽度是360dp,适配后的density = 设备真实宽度(px)/360,保证设计图总宽度不变
28 | - 缺陷:所有控件强制使用我们自身设计的尺寸,当系统或三方控件设计图尺寸和我们自身设计图尺寸差距非常大时就有缺陷了
29 | - 这个问题怎么解决:借助于设计模式深度解决。这个尺寸我们需要更灵活
30 | ```java
31 | // 系统的Density
32 | private static float sNoncompatDensity;
33 | // 系统的ScaledDensity
34 | private static float sNoncompatScaledDensity;
35 |
36 | public static void setCustomDensity(Activity activity, Application application) {
37 | DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
38 | if (sNoncompatDensity == 0) {
39 | sNoncompatDensity = displayMetrics.density;
40 | sNoncompatScaledDensity = displayMetrics.scaledDensity;
41 | // 监听在系统设置中切换字体
42 | application.registerComponentCallbacks(new ComponentCallbacks() {
43 | @Override
44 | public void onConfigurationChanged(Configuration newConfig) {
45 | if (newConfig != null && newConfig.fontScale > 0) {
46 | sNoncompatScaledDensity=application.getResources().getDisplayMetrics().scaledDensity;
47 | }
48 | }
49 |
50 | @Override
51 | public void onLowMemory() {
52 | }
53 | });
54 | }
55 | // 此处以360dp的设计图作为例子
56 | float targetDensity=displayMetrics.widthPixels/360;
57 | float targetScaledDensity=targetDensity*(sNoncompatScaledDensity/sNoncompatDensity);
58 | int targetDensityDpi= (int) (160 * targetDensity);
59 | displayMetrics.density = targetDensity;
60 | displayMetrics.scaledDensity = targetScaledDensity;
61 | displayMetrics.densityDpi = targetDensityDpi;
62 |
63 | DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
64 | activityDisplayMetrics.density = targetDensity;
65 | activityDisplayMetrics.scaledDensity = targetScaledDensity;
66 | activityDisplayMetrics.densityDpi = targetDensityDpi;
67 | }
68 | ```
69 |
70 | ### ScreenAdaptation框架分析
71 | - 设计图最小宽度dp = 设计图宽度 / 几倍图
72 | - smallestWidth限定符适配原理:系统根据限定符去找对应的dimens.xml文件
73 | - 会生成很多不同类型的dimens文件,增加App体积;高入侵性,商业化不能用;不能自动支持横竖屏切换。
--------------------------------------------------------------------------------
/android/性能优化2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/性能优化2.png
--------------------------------------------------------------------------------
/android/框架/ARouter原理解析.md:
--------------------------------------------------------------------------------
1 | ### 组件化开发
2 | 
3 | - 模块可以单独运行测试,提高编译速度
4 | - 超级解耦,模块之间不存在耦合,便于更新维护
5 | - 功能重用
6 | - 便于团队开发
7 |
8 | ### 组件话开发的问题
9 | - 注意包名和资源名冲突,要遵循命名规则
10 | - Gradle中版本号要统一管理
11 | - 组件在Application和Library之间如何做到随意切换,在最终打包发布的时候让所有模块都为library
12 | - AdnroidManifest.xml文件的区分
13 | - Library不能在gradle中有application
14 |
15 | #### 版本号要统一管理
16 | - 把版本号变为公共变量来管理:
17 | - 1.直接定义在gradle.properties里面
18 | ```java
19 | MIN_SDK_VERSION = 21
20 | TAR_SDK_VERSION = 30
21 | COMPILER_SDK_VERSION = 30
22 | BUILD_TOOL_VERSION = 29.0.0
23 | APP_COMPAT = 'androidx.appcompat:appcompat:1.1.0'
24 | VER_CODE = 1
25 | VER_NAME = 1
26 | IS_APPLICATION = true
27 | ```
28 | - 2.自己定义在build.gradle里面
29 | - 3.自己定义一个gradle文件,记得要加载
30 |
31 | #### 组件在Application和Library之间如何做到随意切换
32 | - 业务逻辑模块的gradle中
33 | ```java
34 | if(IS_APPLICATION.toBoolean()){
35 | apply plugin 'com.android.application'
36 | }else{
37 | apply plugin 'com.android.library'
38 | }
39 | // 如果是'com.android.library'模块,则不需要设置applicationid
40 | ```
41 | - 针对不同情况加载不同的AndroidManifest.xml,那么在何时加载呢?
42 | - 在主app的gradle中,只有library的时候才加载无application设置的清单文件
43 | ```java
44 | sourceSets{
45 | main{
46 | if(IS_APPLICATION.toBoolean()){
47 | manifest.srcFile 'src/main/AndroidManifest.xml'
48 | }else{
49 | manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
50 | }
51 | }
52 | }
53 | ```
54 | - 在主app的gradle中,只有其他模块是library的时候才会加载
55 | ```java
56 | dependencies{
57 | if(!IS_APPLICATION.toBoolean()){
58 | imlementation project(path:':login')
59 | }
60 | }
61 | ```
62 | ### 手写实现ARouter
63 | - https://github.com/chsring/SrRouter
64 |
65 | ### 自定义ARouter
66 | - 注解处理器的依赖方式: annotationProcessor()
67 | - annotationProcessor 被此标记的 模块,会找到这个模块,遍历这个模块里面所有的类,看哪个类中带有@AutoService()注解,如果获取到这个类,他就会获取到注解处理器,回调它的Process方法,同时把需要的东西对象化
68 | - 因为好多模块都会依赖 annotationProcessor,那么有几个模块依赖,就会执行多少次。因为是编译时技术,所以只会影响编译效率,不会影响运行速度
69 |
70 |
71 | ### 参考致谢
72 | - https://www.bilibili.com/video/BV1Uv411i7i4/?spm_id_from=333.788.recommend_more_video.-1
--------------------------------------------------------------------------------
/android/框架/BlockCannery原理解析.md:
--------------------------------------------------------------------------------
1 | ## 卡顿问题
2 | ### 破译Android性能优化中的16ms问题
3 | - 在优化卡顿问题时,理想的标准就是绘制一帧的时间不要超过16ms
4 | - 在GPU控制的一块缓冲区中,这块缓冲区叫做Frame Buffer(也就是帧缓冲区)
5 | - Frame Buffer中的数据是不断变化的,为了应对这种变化,手机屏幕的逻辑电路会定期用Frame Buffer中的数据刷新屏幕上的像素点。
6 | - 目前,主流的刷新频率是60次/秒,折算出来就是16ms刷新一次。
7 |
8 | ### 丢帧
9 | - 红色的小球向上移动了一段距离。但由于你的应用没能在16ms内完成界面更新,导致你的用户盯着第一个屏幕看了32ms,然后发现小球跳到了一个新的高度,而不是平滑地移动到了新的高度。 这种情况称作“丢帧”。
10 |
11 | ### 作为开发者,怎样优化应用避免丢帧
12 | - 为了让用户有流畅的动画体验,我们优化的目标就是不要丢帧,也就是在动画进行的过程中,我们要确保更新一帧的时间不要超过16ms。
13 | - 减少视图层次,尽量使用扁平化的视图布局,相对布局。
14 | - 减少不必要的View的invalidate调用。
15 | - 去除View中不必要的background,因为许多background并不会显示在最终的屏幕上。比如ImageView, 假如它显示的图片填满了它的空间,你就没有必要给它设置一个背景色。
16 | - 在绘制时减少不必要的流程,这16ms不是全给你绘制界面的,还有layout、measure呢,Android的一些子系统也要占用这宝贵的16ms完成一些自己的任务,真正留给你绘制自己的界面的时间肯定是少于16ms。
17 | - 减少频繁的gc
18 |
19 |
20 | ### BlockCannery原理
21 | 
22 | - 整个应用的主线程,只有这一个loopr
23 | - 每个消息的处理都是在 msg.target.dispatchMessage(msg) 这里面处理,如果主线程卡住了,不就是在dispatchMessage里卡住了吗?
24 | - 而在msg.target.dispatchMessage(msg);前后都有一个打印语句。
25 | ```java
26 | public static void loop() {
27 | ...
28 | for (;;) {
29 | ...
30 | Printer logging = me.mLogging;
31 | if (logging != null) {
32 | logging.println(">>>>> Dispatching to " + msg.target + " " +
33 | msg.callback + ": " + msg.what);
34 | }
35 | msg.target.dispatchMessage(msg);
36 | if (logging != null) {
37 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
38 | }
39 | ...
40 | }
41 | }
42 | ```
43 | - 该组件利用了主线程的消息队列处理机制,通过Looper.getMainLooper().setMessageLogging(mainLooperPrinter);并在mainLooperPrinter中判断start和end,来获取主线程dispatch该message的开始和结束时间
44 | - 判定该时间超过阈值(如2000毫秒)为主线程卡慢发生,并dump出各种信息,提供开发者分析性能瓶颈。
--------------------------------------------------------------------------------
/android/框架/Jetpack架构之DataBinding.md:
--------------------------------------------------------------------------------
1 | ### Jetpack架构
2 | - Jetpack 主要包括 4 个部分,分别是【Architecture:架构】、【UI:界面】、【behavior:行 为】和【foundation:基础】
3 | - Architecture: databinding,lifecycles,livedata,viewmodel
4 | - ui:Animation and Transition,Emoji,Fragment
5 | - Behavior:CameraX,媒体,通知,权限
6 | - Foundationy:Android KTX,AppCompa,检测,多Dex处理
7 | 
8 |
9 | ### DataBinding
10 | - 使用DataBinding可以轻松实现MVVM模式,并实现数据的双向绑定
11 | - 在Google推出的Android Jetpack组件中,也将DataBinding放在了Architecture类别之中
12 |
13 | ### apt编译
14 | - 布局中引入引入layout、data标签,并在gradle文件中开启dataBinding,Databinding在编译时会生成对应的DataBinding类,利用的技术是Apt
15 | - 编译期间会扫描res/layout目录下所有的布局文件,只要有data标签,都会生成两个布局文件:配置文件和带标签的布局文件
16 | - 布局文件主要是生成控件的tag值
17 | - 配置文件主要是通过tag能够快速定位绑定的控件
18 |
19 | ### databinding双向绑定源码分析,布局文建以下图为例
20 | 
21 | - 点击 DataBindingUtil.setContentView()进入源码中。依次跟踪方法setContentView()—>bindToAddedViews()—>bind()—>sMapper.getDataBinder。最终发现这个方法是一个抽象方法,那么我们就需要去子类中查看该方法,这个子类就是我们编译时生成的DataBinderMapperImpl.class
22 | - 进入app/build/generated/ap_generated_sources/debug/output/com/包名/DataBinderMapperImpl.class中,找到getDataBinder()方法,内部最终返回一个ActivityMainBindingImpl对象
23 | ```java
24 | public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
25 | int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
26 | if(localizedLayoutId > 0) {
27 | //找到布局文件的父节点是否有tag,没有的话就抛出异常
28 | final Object tag = view.getTag();
29 | if(tag == null) {
30 | throw new RuntimeException("view must have a tag");
31 | }
32 | switch(localizedLayoutId) {
33 | case LAYOUT_ACTIVITYDATABINDING: {
34 | //找到的话,进入ActivityDatabindingBindingImpl
35 | if ("layout/activity_databinding_0".equals(tag)) {
36 | return new ActivityDatabindingBindingImpl(component, view);
37 | }
38 | throw new IllegalArgumentException("The tag for activity_databinding is invalid. Received: " + tag);
39 | }
40 | }
41 | }
42 | return null;
43 | }
44 | ```
45 | - ActivityDatabindingBindingImpl.class 构造方法中,首先将bindings数组强转为我们的控件类型
46 | - 然后调用invalidateAll()方法
47 | ```java
48 | // views
49 | public ActivityDatabindingBindingImpl(@Nullable android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
50 | this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
51 | }
52 | private ActivityDatabindingBindingImpl(android.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
53 | super(bindingComponent, root, 2
54 | );
55 |
56 | //根据tag找到控件
57 | this.mboundView0 = (android.widget.LinearLayout) bindings[0];
58 | this.mboundView0.setTag(null);
59 | this.mboundView1 = (android.widget.EditText) bindings[1];
60 | this.mboundView1.setTag(null);
61 | this.mboundView2 = (android.widget.EditText) bindings[2];
62 | this.mboundView2.setTag(null);
63 | setRootTag(root);
64 | // listeners
65 | invalidateAll();
66 | }
67 | ```
68 | - invalidateAll()内部调用了 requestRebind()方法,requestRebind()方法内部 会通过通过handler形式发送一个mRebindRunnable任务
69 | ```java
70 | protected void requestRebind() {
71 | if (mContainingBinding != null) {
72 | mContainingBinding.requestRebind();
73 | } else {
74 | ...
75 | if (USE_CHOREOGRAPHER) {
76 | mChoreographer.postFrameCallback(mFrameCallback);
77 | } else {
78 | //通过handler形式更新ui
79 | mUIThreadHandler.post(mRebindRunnable);
80 | }
81 | }
82 | }
83 | ```
84 | - mRebindRunnable任务的run方法会执行,内部最终会调用到 ActivityDatabindingBindingImpl 的executeBindings()
85 | ```java
86 | protected void executeBindings() {
87 | long dirtyFlags = 0;
88 | synchronized(this) {
89 | dirtyFlags = mDirtyFlags;
90 | mDirtyFlags = 0;
91 | }
92 | java.lang.String dataBeanUserPwdGet = null;
93 | java.lang.String dataBeanUserNameGet = null;
94 | android.databinding.ObservableField dataBeanUserPwd = null;
95 | android.databinding.ObservableField dataBeanUserName = null;
96 | com.xinyartech.mymvpdemo.dataBinding.DataBean2 dataBean = mDataBean;
97 | if ((dirtyFlags & 0xfL) != 0) {
98 | if ((dirtyFlags & 0xdL) != 0) {
99 | if (dataBean != null) {
100 | // read dataBean.userPwd
101 | dataBeanUserPwd = dataBean.userPwd;
102 | }
103 | updateRegistration(0, dataBeanUserPwd);
104 | if (dataBeanUserPwd != null) {
105 | // read dataBean.userPwd.get()
106 | dataBeanUserPwdGet = dataBeanUserPwd.get();
107 | }
108 | }
109 | if ((dirtyFlags & 0xeL) != 0) {
110 | if (dataBean != null) {
111 | // read dataBean.userName
112 | dataBeanUserName = dataBean.userName;
113 | }
114 | updateRegistration(1, dataBeanUserName);
115 | if (dataBeanUserName != null) {
116 | // read dataBean.userName.get()
117 | dataBeanUserNameGet = dataBeanUserName.get();
118 | }
119 | }
120 | }
121 | // batch finished
122 | if ((dirtyFlags & 0xeL) != 0) {
123 | // api target 1
124 | android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, dataBeanUserNameGet);
125 | }
126 | ...
127 | if ((dirtyFlags & 0xdL) != 0) {
128 | // api target 1
129 | android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, dataBeanUserPwdGet);
130 | }
131 | }
132 | ```
133 | - 至此我们一目了然了,Model->View,是因为在executeBindings中执行了注释为 // api target 1的setText方法
134 | - View->Model,是因为executeBindings中添加了mboundView1androidTextAttrChanged监听,在监听的onChange方法中调用了viewmodel的dataBeanUserName.set(((java.lang.String) (callbackArg_0)));方法
135 |
136 | ### databinding带来的问题
137 | - 所有的布局控件都会放到一个数组对象中,那么这个数组对象大小是不定的,如果你有多个activity就会存在多个数组对象,这是比较占用内存的。
138 | - 所有的model更新都是通过handler通知view进行更新的,handler内部拥有一个Looper对象,是不断的在执行消息循环。每一个activity都会存在一个handler,这样也会导致内存的大量消耗。
139 |
140 |
--------------------------------------------------------------------------------
/android/框架/Jetpack架构之Lifecycle.md:
--------------------------------------------------------------------------------
1 | ### 使用Lifcycle解耦页面与组件
2 | ```java
3 | //自定义view 需要实现LifecycleOberver
4 | //添加生命周期的注解
5 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
6 | private void start(){}
7 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
8 | private void stop(){}
9 | ```
10 | ```java
11 | //在activity 中注册生命周期的方法
12 | getLifecycle().addObserver(chronometer);
13 | ```
14 | 
15 | ### Lifecycle使用与实现原理
16 | - 注解+反射+中间的状态机
17 |
18 | ### addObserver方法
19 | - addObserver 是一个抽象方法,具体实现在LifecycleRegistry中,首先会把observer(就是我们的Presenter)和状态信息initialState包装在ObserverWithState中
20 | ```java
21 | // LifecycleRegistry
22 | public void addObserver(@NonNull LifecycleObserver observer) {
23 | State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
24 | ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
25 | ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
26 | // ...
27 | }
28 | ```
29 | - 接下来会把observer放到一个mObserverMap中,这样map中会包含观察者的所有类,而且做了包装。
30 | ```java
31 | static class ObserverWithState {
32 | State mState;
33 | LifecycleEventObserver mLifecycleObserver;
34 |
35 | ObserverWithState(LifecycleObserver observer, State initialState) {
36 | mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
37 | mState = initialState;
38 | }
39 |
40 | void dispatchEvent(LifecycleOwner owner, Event event) {
41 | State newState = getStateAfter(event);
42 | mState = min(mState, newState);
43 | mLifecycleObserver.onStateChanged(owner, event);
44 | mState = newState;
45 | }
46 | }
47 | ```
48 | - ObserverWithState 构造方法中会把我们的observer最终传到 ReflectiveGenericLifecycleObserver 中
49 | ```java
50 | class ReflectiveGenericLifecycleObserver implements LifecycleEventObserver {
51 | private final Object mWrapped;
52 | private final CallbackInfo mInfo;
53 |
54 | ReflectiveGenericLifecycleObserver(Object wrapped) {
55 | mWrapped = wrapped;
56 | // mInfo存储了所有 有注解的方法。
57 | mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());
58 | }
59 |
60 | @Override
61 | public void onStateChanged(LifecycleOwner source, Event event) {
62 | mInfo.invokeCallbacks(source, event, mWrapped);
63 | }
64 | }
65 | ```
66 | - 在 ReflectiveGenericLifecycleObserver 构造方法中,会通过反射拿到 对应Oberver(presenter)的class对象,然后进到getInfo的api
67 | - getInfo再往里走 调用createInfo 会通过反射拿到Presenter的所有方法对应的反射对象。
68 | - 然后会进行遍历所有方法,如果方法头上有OnLifecycleEvent注解,则会将方法用MethodReference包装一次,放到Map集合中,然后将map存到CallbackInfo中,即上面的mInfo
69 | ```java
70 | private CallbackInfo createInfo(Class klass, @Nullable Method[] declaredMethods) {
71 | //拿到注解对象
72 | Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);
73 | boolean hasLifecycleMethods = false;
74 | // 遍历
75 | for (Method method : methods) {
76 | // 方法头上是否有OnLifecycleEvent注解
77 | OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
78 | if (annotation == null) {
79 | continue;
80 | }
81 | //...
82 |
83 | MethodReference methodReference = new MethodReference(callType, method);
84 | verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
85 | }
86 | CallbackInfo info = new CallbackInfo(handlerToEvent);
87 | mCallbackMap.put(klass, info);
88 | mHasLifecycleMethods.put(klass, hasLifecycleMethods);
89 | return info;
90 | ```
91 | - addObserver 执行完成之后,监听哪些生命周期的方法,都绑定到mInfo里面的map集合里面了
92 |
93 | ### 当生命周期变化之后,怎么去通知观察者执行对应的方法呢?
94 | - 基本原理:去mInfo中的集合去找,找到之后通过反射的方式去发通知。
95 | - 在ComponentActivity中,会创建一个不带布局文件的的fragment,如果acitivity生命周期发生变化,则这个fragment对应的生命周期也会执行
96 | ```java
97 | public class ReportFragment extends Fragment {
98 | // ...
99 |
100 | private ActivityInitializationListener mProcessListener;
101 |
102 | private void dispatchCreate(ActivityInitializationListener listener) {
103 | if (listener != null) {
104 | listener.onCreate();
105 | }
106 | }
107 |
108 | private void dispatchStart(ActivityInitializationListener listener) {
109 | if (listener != null) {
110 | listener.onStart();
111 | }
112 | }
113 |
114 | private void dispatchResume(ActivityInitializationListener listener) {
115 | if (listener != null) {
116 | listener.onResume();
117 | }
118 | }
119 |
120 | @Override
121 | public void onActivityCreated(Bundle savedInstanceState) {
122 | super.onActivityCreated(savedInstanceState);
123 | dispatchCreate(mProcessListener);
124 | dispatch(Lifecycle.Event.ON_CREATE);
125 | }
126 |
127 | @Override
128 | public void onStart() {
129 | super.onStart();
130 | dispatchStart(mProcessListener);
131 | dispatch(Lifecycle.Event.ON_START);
132 | }
133 |
134 | @Override
135 | public void onResume() {
136 | super.onResume();
137 | dispatchResume(mProcessListener);
138 | dispatch(Lifecycle.Event.ON_RESUME);
139 | }
140 |
141 | @Override
142 | public void onPause() {
143 | super.onPause();
144 | dispatch(Lifecycle.Event.ON_PAUSE);
145 | }
146 |
147 | @Override
148 | public void onStop() {
149 | super.onStop();
150 | dispatch(Lifecycle.Event.ON_STOP);
151 | }
152 |
153 | @Override
154 | public void onDestroy() {
155 | super.onDestroy();
156 | dispatch(Lifecycle.Event.ON_DESTROY);
157 | // just want to be sure that we won't leak reference to an activity
158 | mProcessListener = null;
159 | }
160 |
161 | private void dispatch(Lifecycle.Event event) {
162 | Activity activity = getActivity();
163 | if (activity instanceof LifecycleRegistryOwner) {
164 | ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
165 | return;
166 | }
167 |
168 | if (activity instanceof LifecycleOwner) {
169 | Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
170 | if (lifecycle instanceof LifecycleRegistry) {
171 | ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
172 | }
173 | }
174 | }
175 |
176 | void setProcessListener(ActivityInitializationListener processListener) {
177 | mProcessListener = processListener;
178 | }
179 |
180 | interface ActivityInitializationListener {
181 | void onCreate();
182 |
183 | void onStart();
184 |
185 | void onResume();
186 | }
187 | }
188 | ```
189 | - 在fragment生命周期方法执行时,会调用dispatch 遍历mObserverMap中所有的观察者,然后调用每一个观察者的observer.diapatchEvent分发生命周期事件
190 | - 而dispatchEvent方法正是 ObserverWithState 类中的dispatchEvent方法,里面调用mLifecycleObserver.onStateChanged(owner, event);
191 | ```java
192 | void dispatchEvent(LifecycleOwner owner, Event event) {
193 | State newState = getStateAfter(event);
194 | mState = min(mState, newState);
195 | mLifecycleObserver.onStateChanged(owner, event);
196 | mState = newState;
197 | }
198 | ```
199 | - onStateChanged是一个抽象方法,它的实现类正是 我们上面所说的 ReflectiveGenericLifecycleObserver,执行它的onStateChanged方法,通过event事件从map中找到对应的方法,然后通过反射去执行该方法。
200 |
201 |
--------------------------------------------------------------------------------
/android/框架/Jetpack架构之LiveData.md:
--------------------------------------------------------------------------------
1 | ### 使用
2 | ```java
3 | liveData.observe(this,Observer()){
4 | public void onChanged(String data){
5 |
6 | }
7 | }
8 | ```
9 |
10 | - Livedata是一个观察者,可以观察到Activity和Fragment生命周期的变化,通过lifecycle完成观察者被观察者的绑定
11 | - 同时也是被观察者,例如数据变化时,会发送一个通知告诉观察者。
12 | - Livedata有两个方法,setValue():主线程发消息,postValue():从子线程发消息
13 |
14 |
15 | ### Livedata使用与实现原理
16 |
17 |
18 | ### Livedata粘性事件源码分析
19 |
20 |
21 | ### Jetpack中状态机如何管理生命周期
22 |
23 |
24 | ### hook实现Livedata非粘性功能
25 |
26 |
27 | ### Livedata递归调用源码中如何做容错
28 |
29 |
30 | ### 企业级LivedataBus封装实现
--------------------------------------------------------------------------------
/android/框架/Jetpack架构之ViewModel.md:
--------------------------------------------------------------------------------
1 | ### ViewModel
2 | - ViewModel旨在以生命周期意识的方式存储和管理用户界面相关的数据
3 | - 可以用来管理Activity和Fragment中的数据。还可以拿来处理Fragment与Fragment之间的通信等等
4 | - 用来做数据的持久化
5 | - 当Activity或者Fragment创建了关联的ViewModel,那么该Activity或Fragment只要处于活动状态,那么该ViewModel就不会被销毁,即使是该Activity屏幕旋转时重建了。所以也可以拿来做数据的暂存
6 | - ViewModel主要是拿来获取或者保留Activity/Fragment所需要的数据的,开发者可以在Activity/Fragment中观察ViewModel中的数据更改(这里需要配合LiveData使用)。
7 |
8 | ### ViewModel创建
9 | 
10 | - ViewModel的初始化很简单,通过ViewModelProvider就可以获取到ViewModel实例。那么从这里入手开始分析ViewModel的创建过程。
11 | ```java
12 | final UserModel userModel = ViewModelProvider.get(UserModel.class);
13 | public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
14 | this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
15 | ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
16 | : NewInstanceFactory.getInstance());
17 | }
18 | public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
19 | this(owner.getViewModelStore(), factory);
20 | }
21 | ```
22 | - 从ViewModelProvider的构造方法中可以看到最终是需要两个参数ViewModelStoreOwner以及Factory。这两个参数中ViewModelStoreOwner是用来存储ViewModel对象的,Factory是用来创建ViewModel对象。
23 | - 第二步是通过ViewModelProvider的get()方法获取ViewModel实例。
24 | ```java
25 | public T get(@NonNull String key, @NonNull Class modelClass) {
26 | ViewModel viewModel = mViewModelStore.get(key); // 1.是否有缓存ViewModel实例缓存
27 |
28 | ...
29 | if (mFactory instanceof KeyedFactory) {
30 | viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
31 | } else {
32 | viewModel = (mFactory).create(modelClass);
33 | }
34 | mViewModelStore.put(key, viewModel);
35 | return (T) viewModel;
36 | }
37 | ```
38 | - 在get()方法中可以看到是通过mFactory的类型来创建ViewModel的。而Factory的类型是由ViewModelStoreOwner决定的,这是ViewModelProvider的构造方法中的逻辑。
39 | - 其中有两种Factory,一种是SavedStateViewModelFactory,另一种是NewInstanceFactory。
40 | ### SavedStateViewModelFactory创建ViewModel
41 | ```java
42 | public T create(@NonNull String key, @NonNull Class modelClass) {
43 | boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
44 | Constructor constructor;
45 | ...
46 | SavedStateHandleController controller = SavedStateHandleController.create(
47 | mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
48 | try {
49 | T viewmodel;
50 | if (isAndroidViewModel) {
51 | viewmodel = constructor.newInstance(mApplication, controller.getHandle());
52 | } else {
53 | viewmodel = constructor.newInstance(controller.getHandle());
54 | }
55 | viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
56 | return viewmodel;
57 | }
58 | ...
59 | }
60 | ```
61 | - 在代码中可以看到,ViewModel有分为AndroidViewModel跟ViewModel,它们的区别是AndroidViewModel创建时会加入Application参数。
62 | ### NewInstanceFactory通过Class的newInstance()方法直接创建ViewModel实例。
63 | ```java
64 | public T create(@NonNull Class modelClass) {
65 | //noinspection TryWithIdenticalCatches
66 | try {
67 | return modelClass.newInstance();
68 | } catch (InstantiationException e) {
69 | throw new RuntimeException("Cannot create an instance of " + modelClass, e);
70 | } catch (IllegalAccessException e) {
71 | throw new RuntimeException("Cannot create an instance of " + modelClass, e);
72 | }
73 | }
74 | ```
75 | ### ViewModel销毁
76 | - 这里还要分开两处说,一个是在Activity中的销毁过程,一个是在Fragment中销毁过程。
77 | - 在ComponentActivity的构造方法中,可以看到通过Lifecycle在ON_DESTROY事件中销毁ViewModel。
78 | ```java
79 | public ComponentActivity() {
80 | getLifecycle().addObserver(new LifecycleEventObserver() {
81 | @Override
82 | public void onStateChanged(@NonNull LifecycleOwner source,
83 | @NonNull Lifecycle.Event event) {
84 | if (event == Lifecycle.Event.ON_DESTROY) {
85 | if (!isChangingConfigurations()) {
86 | // 销毁ViewModel
87 | getViewModelStore().clear();
88 | }
89 | }
90 | }
91 | });
92 | }
93 | ```
94 | - FragmentManagerViewModel类的clearNonConfigState()方法中找到了ViewModel的销毁逻辑
95 | ```java
96 | void clearNonConfigState(@NonNull Fragment f) {
97 | ...
98 | // Clear and remove the Fragment's ViewModelStore
99 | ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
100 | if (viewModelStore != null) {
101 | // 销毁ViewModel
102 | viewModelStore.clear();
103 | mViewModelStores.remove(f.mWho);
104 | }
105 | }
106 | ```
107 | ### ViewModel的生命周期绑定
108 | - 在上面分了ViewModel的创建与销毁过程中就可以得出ViewModel生命周期是如何与组件交互的了。主要还是通过Lifecycle和组件的生命周期方法来进行回调管理。
109 |
110 | ### 在Activity重建时会执行destory生命周期事件,那么为什么ViewModel没有销毁呢
111 | - 通过对ComponentActivity的getViewModelStore()方法进行分析。可以知道mViewModelStore变量如果是null的话,会从NonConfigurationInstances实例中取。
112 | - onRetainNonConfigurationInstance中会存储ViewModelStore实例
113 | ```java
114 | public ViewModelStore getViewModelStore() {
115 | ...
116 | if (mViewModelStore == null) {
117 | NonConfigurationInstances nc =
118 | (NonConfigurationInstances) getLastNonConfigurationInstance();
119 | if (nc != null) {
120 | // Restore the ViewModelStore from NonConfigurationInstances
121 | mViewModelStore = nc.viewModelStore;
122 | }
123 | if (mViewModelStore == null) {
124 | mViewModelStore = new ViewModelStore();
125 | }
126 | }
127 | return mViewModelStore;
128 | }
129 |
130 | ```
131 |
132 | ### Activity横竖屏切换
133 | - 在横竖屏切换的时候会调用ActivityThread中的handleRelaunchActivity方法,handleRelaunchActivity方法的核心是handleRelaunchActivityInner方法,在handleRelaunchActivityInner方法中处理了activity的销毁和重建,在销毁之前把ViewModelStore保存到NonConfigurationInstances起来,在重建的时候把保存的数据通过Activity的attach方法传入新的Activity中,这样就实现了ViewModel在横竖屏切换的时候不会丢失。
134 |
135 | ### 注意点
136 | - 不要向viewmodel中传入Activity的Context,否则会内存泄漏
137 | - 如果要是用Context,就是用AndroidViewmodle的Application
--------------------------------------------------------------------------------
/android/框架/LeakCannary原理解析.md:
--------------------------------------------------------------------------------
1 | ### 新版本LeakCannery不用在Appcliation做初始化就可以使用原因:
2 | - 在LeakCannery的清单文建中,提供一个provider
3 | - app打包流程中有个mergeManifest:会把引用到的module,aar中的清单文件merge到主app的清单文建中会合并
4 | - App启动的时候,会走到handlerBindApplication这个方法内部通过makeApplication创建出Application
5 | - 会检测如果注册了provider,会调用installContentProvider方法,
6 | ```java
7 | //ActivityThreadd.java
8 | private void handleBindApplication(){
9 | ...
10 | app = data.info.makeApplication(data.restrictedBackupMode, null);
11 | app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
12 | mInitialApplication = app;
13 | if (!data.restrictedBackupMode) {
14 | if (!ArrayUtils.isEmpty(data.providers)) {
15 | installContentProviders(app, data.providers);
16 | mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
17 | }
18 | }
19 | try {
20 | mInstrumentation.onCreate(data.instrumentationArgs);
21 | }
22 | ...
23 | }
24 | ```
25 | - installContentProvider内部经过层层调用会走到attachInfo方法中,最终会调用ContentProvider.this.onCreate()
26 | - 从上述方法出来之后,Instrumentation调用Application的onCreate的方法
27 | - 结论:如果app中有ContentProvider的话,ContentProvider的onCreate会比Application的onCreate先调用
28 | - 因为清单文件的存在,我可以在三方库中的ContentProvider的onCreate中拿到application实例,然后进行LeakCannery的初始化。
29 |
30 | ### 如果所有三方 都从ContentProvider中做初始化,不能乱用
31 | - 涉及到app启动速度优化
32 | - LeakCannery主要应用在开发debug阶段,所以可以这样用
33 |
34 | ### LeakCannery 如何检测内存泄漏的,检测Activity退出的原理
35 | 
36 | - 注册ActivityLifeCycleCallback 后,会走Activity生命周期的回调,可以检测Activity的生命周期
37 | - Fragment有个FragmentLifeCycleCallback,流程和 Activity类似
38 | - 当执行 onDestroyed 的时候,会检测该Activity是否被销毁,如果没有被销毁,可能会泄漏L e
39 | - 在Activity走到onDestroy的时候,会通过RefWatcher对象 执行watch方法,监控 activity
40 | - 内部会通过mainHandler 五秒之后去post一个消息,也就是过5秒后会检测 activity有没有真正被销毁
41 | - 如果没有被销毁,进行可达性分析看是否发生了泄漏
42 | - 真正负责检测的是在一个工作线程里面,通过HeapDumpTrigger进行分析
43 | ### 回顾java四种引用
44 | - 强:gc扫描即使oom也不会回收
45 | - 软:gc扫描到,内存够就不会回收;内存不够就会回收
46 | - 弱:gc扫描到,就会回收
47 | - 虚:一种标识
48 |
49 | ### ReferenceQueue引用队列
50 | - 保存的是四种引用对象
51 | - 所引用的对象被GC回收时,该引用对象就会加入引用队列的末尾
52 | - 如果经过GC后没有进入引用队列,说明该对象没有被回收,说明可能存在内存泄漏
53 |
54 | ### 能不能检测bitmap?
55 | - 在我们认为这个对象不需要使用了,进行null检测,我们自己调用AppWatcher.watch(该对象)
56 |
57 | ### 什么时候触发检测
58 | 
59 | 
60 |
61 | ### 常见内存泄漏场景
62 | - 结合类:当使用集合时,只有添加元素,没有对应的删除元素。如EventBus只有注册,没有注销
63 | - 静态成员/单例:作为GC ROOT 持有段生命周期引用(Activity)导致其短生命周期对象无法释放
64 | - 未关闭释放资源:FileOutputStream未close
65 | - Handler/Thread(非静态内部类):安卓程序员,70%的内存泄漏都来自于它。持有链:sendmsg-> MessageQueue -> looper -> msg -> handler -> Activity。如果是延迟消息,这个消息还在消息队列中没做处理,导致Activity没有释放
66 | - 内部类实际上会持有外部类的引用,如果Handler是内部类,会持有Activity引用。
67 | - TextView 设置成static,就会造成泄漏:textview常生命周期,会持有activity的引用
68 | - 系统Bug,自定义listView,WebView,inputMethodManager等
69 |
--------------------------------------------------------------------------------
/android/框架/MVP、MVVM架构.md:
--------------------------------------------------------------------------------
1 | ### MVC
2 | 
3 | - Android App原本就是MVC的,View对应布局文件Xml,Controller对应Activity,Model对应数据库、网络请求、业务计算等操作。
4 | - 举个简单的例子,按下界面上的一个Button去网络上下载一个文件,这个按钮是View层的,我们是通过Xml来定义的,而网络请求等复用性很高的相关代码写在一个专门的DownloadHelper类中,其实这就是我们所说的Model层,最后通过button.setOnClickListener()函数将View层和Model层联系起来,这个函数就写在了Activity中,即Controller层的实现。
5 |
6 | #### MVC缺点
7 | - View层的XML控制力实在是太弱了,众多View层的处理最终都放在了Activity中去做,从而导致Activity既包含了View又包含了Controller,两层之间耦合高,不利于维护拓展,可读性也变差。
8 |
9 | ### MVP
10 | 
11 | - Model:主要提供数据存取功能。View:可能是指Activity、Fragment或者View。Presenter:通过Model存取数据,连接View和Model,从Model中取出数据交给View。
12 |
13 | #### MVP优点
14 | - 1.降低了耦合度,实现了model和view的完全分离,解决了MVC中维护难的问题
15 | - 2.Presenter 可以复用,一个 Presenter 可以用于多个 View,而不需要更改 Presenter 的逻辑
16 | - 3.View 可以进行组件化,在MVP当中,View 不依赖 Model
17 |
18 | #### MVP缺点
19 | - 1.于我们使用了接口的方式去连接View层和Presenter层,如果有一个逻辑很复杂的页面,接口会有很多,维护接口的成本会很大。
20 | - 2.由于Presenter 经常性的持有Activity 的强引用,如果在一些请求结束之前Activity 被销毁了,Activity对象将无法被回收,此时就会发生内存泄露。
21 |
22 | #### MVP内存泄漏的解决
23 | - View层需要抽象出一层父类BaseActivity,并利用Activity的生命周期方法对View层和Presenter层实现绑定和解绑:在onCreate方法中调用P层的attachView,在onDestroy方法中去释放view
24 | - Presenter层也抽象出BasePresenter类,使Presenter层持有Activity的软引用
25 |
26 | ### MVVM
27 | 
28 | - MVVM和MVP的区别其实不大,只不过是把Presenter层换成了ViewModel层
29 | - 再有就是View层和ViewModel层是双向绑定的关系,当我们更新ViewModel层的数据的时候,View层会相应的更新UI。
30 | - MVVM在Android中的实现方式是通过data-binding框架来做的
31 |
32 | #### 优点
33 | - 解耦彻底
34 | - 视图(View)可以独立于Model变化和修改
35 |
36 | #### MVVM缺点
37 | - 数据绑定使得 Bug 很难被调试
38 | - 过大的项目,数据绑定需要花费更多的内存。DataBinding会带来内存问题。
--------------------------------------------------------------------------------
/android/框架/OKHttp源码分析.md:
--------------------------------------------------------------------------------
1 | ### http请求过程
2 | - 域名DNS解析,把域名解析出IP
3 | - 根据域名解析出的IP建立TCP连接,经过三次握手
4 | - 如果用代码表示的话 就是完成了一个socket连接,得到了socket对象
5 | - 把请求组装成报文,利用socket的outputStream写出去,完成http请求。
6 |
7 | ### OkHttp流程
8 | 
9 | - 通过OkHttpClientBuilder构建一个OkHttpClient对象
10 | - 把Request对象交给OkHttpClient的newCall方法得到realCall对象
11 | - Call的 execute 执行同步请求,enqueue 执行异步请求,无论是哪个请求,最终都要进入分发器完成任务的调配
12 | - 分发器Dispatcher:内部维护队列与线程池,完成请求调配
13 | - 拦截器Interceptors:完成整个请求过程,例如http请求过程。还完成一系列优化工作。
14 |
15 | ### RealCall源码
16 | ```java
17 | // RealCall的enqueue方法
18 | @Override public void enqueue(Callback responseCallback) {
19 | // 内置锁的作用:限制同一个call对象只能执行一次enqueue,否则抛异常,如果想再次执行,可以调用clone方法后再执行enqueue
20 | synchronized (this) {
21 | if (executed) throw new IllegalStateException("Already Executed");
22 | executed = true;
23 | }
24 | transmitter.callStart();
25 | // 最终调用分发器的 enqueue 方法。AsyncCall包装了请求信息
26 | client.dispatcher().enqueue(new AsyncCall(responseCallback));
27 | }
28 | ```
29 |
30 |
31 | ### 分发器
32 | - 里面维护了三个队列
33 | - readyAsyncCalls:准备队列;
34 | - runningAsyncCalls:异步running队列,包括未完成已经取消的请求;
35 | - runningSyncCalls:同步running队列,包括未完成已经取消的请求。
36 |
37 |
38 | #### 异步请求工作流程
39 | 
40 | 
41 | - 异步请求会调用enqueue方法,该方法传入一个AsyncCall对象,每个AsyncCall对象都封装了一个请求所需要的所有信息。同时,将当前请求加入到追呗队列,最后调用promoteAndExecute方法
42 | ```java
43 | // 分发器
44 | void enqueue(AsyncCall call) {
45 | synchronized (this) {
46 | //请求任务添加到一个准备队列去
47 | readyAsyncCalls.add(call);
48 | //...
49 | // 最后一行调用promoteAndExecute方法
50 | promoteAndExecute();
51 | }
52 | ```
53 | - promoteAndExecute 首先遍历准备队列
54 | - 其次判断如果正在执行的任务数量超过规定的最大请求数量则跳出;判断如果当前域名的请求数超过规定的最大单域名请求数量则当前请求不做处理;
55 | - 否则,从待执行列表里移除当前请求,将它添加到正在执行队列和可执行列表里
56 | - 遍历可执行列表,将刚才的待执行任务,全部丢到线程池里去执行
57 | ```java
58 | private boolean promoteAndExecute() {
59 | assert (!Thread.holdsLock(this));
60 |
61 | //生命一个AsyncCall集合
62 | List executableCalls = new ArrayList<>();
63 | boolean isRunning;
64 | synchronized (this) {
65 | //遍历准备队列
66 | for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
67 | AsyncCall asyncCall = i.next();
68 | //如果正在运行的任务队列大于设置的最大请求数maxRequests,直接跳出循环
69 | if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
70 | //如果同一个相同Host的请求数量,也就是请求同一个主机的请求数量大于等于设置的最大数量maxRequestsPerHost,continue
71 | if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
72 | //如果以上不满足,就将当前遍历的请求任务移除准备队列,放入运行队列
73 | i.remove();
74 | asyncCall.callsPerHost().incrementAndGet();
75 | executableCalls.add(asyncCall);
76 | runningAsyncCalls.add(asyncCall);
77 | }
78 | isRunning = runningCallsCount() > 0;
79 | }
80 | //同时给每一个请求任务设置一个线程池对象。改线程池对象是单例的
81 | for (int i = 0, size = executableCalls.size(); i < size; i++) {
82 | AsyncCall asyncCall = executableCalls.get(i);
83 | // executorService 自定义一个没有核心线程,非核心线程不限的,且在闲置的时候,一分钟后会被回收的线程池
84 | asyncCall.executeOn(executorService());
85 | // executeOn方法中调用了 executorService.execute(this) 开启执行任务
86 | }
87 |
88 | return isRunning;
89 | }
90 | ```
91 | - 丢到线程池后,会执行每个请求AsyncCall的execute方法,execute()方法主要的两个功能:
92 | - 调用getResponseWithInterceptorChain方法,构造拦截器链,对请求进行处理
93 | - 任务结束时调用dispatcher.finish方法,将当前请求移出running队列,重新遍历准备行队列间,将待执行任务推到线程池中执行
94 | ```java
95 | // 接上面开启线程池之后,会执行AsuncCall的run方法,进而执行到AsuncCall#execute()方法。
96 | protected void execute() {
97 | //.....
98 | try {
99 | //构造拦截器链,执行各种拦截器后得到响应
100 | Response response = getResponseWithInterceptorChain();
101 | signalledCallback = true;
102 | responseCallback.onResponse(RealCall.this, response);
103 | } catch (IOException e) {
104 | //...
105 | } catch (Throwable t) {
106 | //...
107 | } finally {
108 | //任务结束,调用finished方法,将当前请求移出running队列,内部又去调用了promoteAndExecute()方法
109 | client.dispatcher().finished(this);
110 | }
111 | }
112 | }
113 |
114 | ```
115 | - getResponseWithInterceptorChain()方法,这个方法主要构造一个拦截器链对象,并调用它的proceed方法
116 | - 这几个拦截器 请求是从上往下,请求回来之后是冲下往上处理
117 | 
118 | ```java
119 | Response getResponseWithInterceptorChain() throws IOException {
120 | List interceptors = new ArrayList<>();
121 | //添加用户自定义的拦截器
122 | interceptors.addAll(client.interceptors());
123 | //添加重定向拦截器:重连和重定向
124 | interceptors.add(retryAndFollowUpInterceptor);
125 | //添加桥接拦截器:请求头、cookie、压缩处理
126 | interceptors.add(new BridgeInterceptor(client.cookieJar()));
127 | //添加缓存处理拦截器:判断缓存是否可用,得到响应之后是否缓存
128 | interceptors.add(new CacheInterceptor(client.internalCache()));
129 | //添加连接拦截器:找到或新建一个链接,并获得对应的socket流
130 | interceptors.add(new ConnectInterceptor(client));
131 | if (!forWebSocket) {
132 | interceptors.addAll(client.networkInterceptors());
133 | }
134 | //添加发送请求拦截器:想服务器发送数据,解析度去的响应数据
135 | interceptors.add(new CallServerInterceptor(forWebSocket));
136 | //构造拦截器链对象,index传入0
137 | Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),
138 | client.readTimeoutMillis(), client.writeTimeoutMillis());
139 | //调用链对象的proceed方法
140 | Response response = chain.proceed(originalRequest);
141 | if (retryAndFollowUpInterceptor.isCanceled()) {
142 | closeQuietly(response);
143 | throw new IOException("Canceled");
144 | }
145 | return response;
146 | }
147 |
148 | ```
149 | - 除了CallServerInterceptor外,其他所有的interceptor.intercept方法,都会调用chain.proceed()方法
150 | ```java
151 | public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
152 | RealConnection connection) throws IOException {
153 | //.....
154 | //使用原有的拦截器列表构造新的拦截链对象,index+1
155 | RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
156 | connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
157 | writeTimeout);
158 | //获取当前index的拦截器
159 | Interceptor interceptor = interceptors.get(index);
160 | //调用当前拦截器的intercept方法,并且传入新的拦截链对象(index已经+1的)
161 | Response response = interceptor.intercept(next);
162 | //.....
163 | return response;
164 | }
165 |
166 | ```
167 | ```java
168 | public Response intercept(Chain chain) throws IOException {
169 | //.....
170 | 1、做自身拦截器的职责
171 | 2、调用chain.proceed()方法,由于传入的chain为新构造的,index加了1,于是重新执行上面的chain.proceed方法时,获取的拦截器就是下一个的,并且又会构造index+2的新的拦截链对象,传入index+1的拦截器中
172 | }
173 |
174 | ```
175 | - 直到最后一个拦截器CallServerInterceptor就不会调用chain.proceed()方法,而是请求网络,构造返回Response对象,后面会单独将每个拦截器的代码
176 |
177 | ### 拦截器
178 | - 拦截器的构造主要是在 AsuncCall#execute()中,通过getResponseWithInterceptorChain构造的
179 | - 责任链模式,行为行设计模式,责任链上的处理者负责处理请求,客户只需要将请求放到责任链上,无需关心请求处理的细节,责任链将请求的发送者和处理者解耦了
180 |
181 | ### OkHttp3中使用到的设计模式
182 | - 责任链:拦截器
183 | - 建造者:OkHttpClientBulider
184 | - 工厂:CacheStartegy.Factory
185 | - 策略:CacheStartegy
186 |
187 |
188 |
189 |
190 | ### 参考致谢
191 | - https://blog.csdn.net/laigengsong/article/details/122786872
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
--------------------------------------------------------------------------------
/android/框架/Retrofit源码分析.md:
--------------------------------------------------------------------------------
1 | ### Retrofit把OkHttp进一步解耦,职责更加清晰
2 | ```java
3 | class RetrofitActivity : AppCompatActivity() {
4 | override fun onCreate(savedInstanceState: Bundle?) {
5 | super.onCreate(savedInstanceState)
6 | //初始化一个Retrofit对象
7 | val retrofit = Retrofit.Builder()
8 | .baseUrl("https://api.github.com/")
9 | .addConverterFactory(GsonConverterFactory.create())
10 | .build()
11 | //创建出GitHubApiService对象
12 | val service = retrofit.create(GitHubApiService::class.java)
13 | //返回一个 Call 对象
14 | val repos = service.listRepos("octocat")
15 | //调用 enqueue 方法在回调方法里处理结果
16 | repos.enqueue(object : Callback?> {
17 | override fun onFailure(call: Call?>, t: Throwable) {
18 | t.printStackTrace()
19 | }
20 |
21 | override fun onResponse(call: Call?>, response: Response?>) {
22 | "response.code() = ${response.code()}".logE()
23 | }
24 | })
25 |
26 | }
27 | }
28 | 复制代码
29 | //自己定义的 API 请求接口
30 | interface GitHubApiService {
31 | @GET("users/{user}/repos")
32 | fun listRepos(@Path("user") user: String?): Call>
33 | }
34 | ```
35 | ### 工作原理
36 | - 通过Retrofit.Builder来构建一个retrofit对象
37 | - 通过retrofit.create来获取接口代理对象
38 | - 获取具体请求业务方法
39 | - 发起请求
40 | 
41 | #### create方法
42 | - 动态代理在retrofit中其实就是在运行期间通过反射机制来动态生成方法接口的代理对象。我们在使用时都会创建一个interface类,里面再添加一些接口方法。
43 | - 在create方法中return一个代理类,通过这种方式组建okhttp的request 和 Call 进行请求。
44 | ```java
45 | public T create(final Class service) {
46 | return (T)
47 | Proxy.newProxyInstance(
48 | //有三个参数
49 | service.getClassLoader(), //获取一个 ClassLoader 对象
50 | new Class>[] {service}, // 将接口的字节码对象传到数组中去,也即是我们要代理的具体接口
51 | new InvocationHandler() { //InvocationHandler 的 invoke 是关键,会执行 InvocationHandler 的invoke的方法体。
52 | .....
53 | }
54 | }
55 |
56 | ```
57 | - 动态代理的主要操作是在InvocationHandler#invoke中,里面最终会走到loadServiceMethod方法,它返回一个HttpServiceMethod对象调用invoke方法
58 | - 接下来我们分别看一下 loadServiceMethod 方法和 invoke 方法
59 | ```java
60 | public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
61 | throws Throwable {
62 | // 因为有代理类的生成,默认继承 Object 类,所以如果是 Object.class 走,默认调用它的方法
63 | if (method.getDeclaringClass() == Object.class) {
64 | return method.invoke(this, args);
65 | }
66 | args = args != null ? args : emptyArgs;
67 | // loadServiceMethod(method).invoke(args);这个方法是我们这个 Retrofit 最关键的代码,也是分析的重点入口
68 | return platform.isDefaultMethod(method)
69 | ? platform.invokeDefaultMethod(method, service, proxy, args)
70 | // 调用 HttpServiceMethod 对象的 invoke方法
71 | : loadServiceMethod(method).invoke(args);
72 | }
73 | ```
74 | - loadServiceMethod主要是为了获取ServiceMethod对象,通过代理方式获取之后缓存起来下次直接取。如果本次没获取到将会调用ServiceMethod#parseAnnotations解析注解和方法。
75 | ```java
76 | ServiceMethod> loadServiceMethod(Method method) {
77 | // 从 ConcurrentHashMap 中取一个 ServiceMethod 如果存在直接返回
78 | ServiceMethod> result = serviceMethodCache.get(method);
79 | if (result != null) return result;
80 |
81 | synchronized (serviceMethodCache) {
82 | result = serviceMethodCache.get(method);
83 | if (result == null) {
84 | // 通过 ServiceMethod.parseAnnotations(this, method);方法创建一个 ServiceMethod 对象
85 | result = ServiceMethod.parseAnnotations(this, method);
86 | // 用 Map 把创建的 ServiceMethod 对象缓存起来,因为我们的请求方法可能会调用多次,缓存提升性能。
87 | serviceMethodCache.put(method, result);
88 | }
89 | }
90 | return result;
91 | }
92 |
93 | ```
94 | - ServiceMethod#parseAnnotations方法内调用 HttpServiceMethod.parseAnnotations 最终会返回一个HttpServiceMethod对象
95 | ```java
96 | //ServiceMethod.java
97 | static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
98 | // 开始注解解析
99 | RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
100 | Type returnType = method.getGenericReturnType();
101 | if (Utils.hasUnresolvableType(returnType)) {
102 | throw methodError( method, "Method return type must not include a type variable or wildcard: %s", returnType);
103 | }
104 | if (returnType == void.class) {
105 | throw methodError(method, "Service methods cannot return void.");
106 | }
107 | return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
108 | }
109 | ```
110 | - HttpServiceMethod#parseAnnotations 方法内部会通过反射解析注解解析出HttpServiceMethod对象,调用createCallAdapter创建CallAdapter;调用createResponseConvert来创建转换器,之后返回一个CallAdapter。我们先讨论主流程,看下它的 invoke 方法
111 | - HttpServiceMethod 的 invoke方法内部创建了一个Call对象,然后调用它的adapter方法。
112 | - Retrofit把网络请求最终封装成一个call,为了适配Rxjava等形式,会有不同的Adapter:CallAdapter(适配器模式来增强call的功能。在构建callAdapter的时候油菜永乐工厂模式)
113 | - 范型有个统一的父类Type,通过这个类解析成不同的范型信息
114 | - 常见的CallAdapter有:guava,java8,rxjava,rxjava2等
115 | - Convert接口,数据转换接口,例如:GsonConvertFactory,具体转换工作通过工厂模式定义不同的转换器进行转换。
116 | ```java
117 | //HttpServiceMethod.java
118 | @Override
119 | final @Nullable ReturnT invoke(Object[] args) {
120 | Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
121 | //是一个 adapt 方法,在不使用 Kotlin 协程的情况下,其实调用的是子类 CallAdapted 的 adapt,这个会在下面具体分析,包括 Kotlin 协程的 suspend 函数
122 | return adapt(call, args);
123 | }
124 | ```
125 | - CallAdapted 继承自HttpServiceMethod,HttpServiceMethod中invoke调用的adapter方法实际上调用的是CallAdapted的adapter方法
126 | ```java
127 | protected ReturnT adapt(Call call, Object[] args) {
128 | return callAdapter.adapt(call);
129 | }
130 | ```
131 | - 至此create中InvocationHandler的invoke方法就返回一个CallAdapter,并且调用到了adapter方法,是适配器模式,根据使用的不同类型进行适配。
132 | - 如果我们适配的是rxjava,那么就找对应的RxJava2CallAdapter类,里面的adapt方法返回一个 Observable 对象
133 | - 如果默认的,就找 DefaultCallAdapterFactory 里面adapter返回一个 Call