├── .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 | 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 | ![apk构建流程.png](resource/apk构建流程.png) 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 | ![img.png](resource/gradle概念.png) 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 | ![img.png](resource/Binder性能.png) 30 | - Android使用Linux内核,它有很多快进程通信机制,IPC通信,如管道,消息队列,共享内存,信号量,socket,文件等 31 | - 性能:广泛IPC会提出性能要求 32 | - 安全:传统IPC没有对通信双方进行身份验证,Binder双方支持通信双方身份校验 33 | 34 | ### Binder通信模型 35 | - 客户端持有一个服务代理,代理对象协助驱动,完成跨进程通信。 36 | ![img.png](resource/Binder机制.png) 37 | - 1.Server在ServiceManager,注册某方法add 38 | - 2.Client从ServiceManager中查询是否有该方法,如果有,SM会返回Client一个代理空方法。 39 | - 3.当Client调用该方法时,他会返回给内核驱动,内核驱动调用Server的add方法,把结果返回给驱动,驱动返回给Client 40 | 41 | ### Binder是如何做到一次拷贝的 42 | ![img.png](resource/Binder机制跨进程.png) 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 | ![img.png](resource/Context继承关系.png) 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 | ![img.png](resource/Fragment生命周期.png) 15 | ![img.png](resource/生命周期交织.png) 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 | ![img.png](resource/Activity的启动流程.png) 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 | ![img.png](resource/Activity启动时序图.png) 52 | - ![img.png](resource/Activity启动时序2.png) 53 | 54 | ### Activity的onCreate之前执行的流程 55 | ![img.png](resource/Activity的onCreate之前流程.png) 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 | ![img.png](resource/WebView的启动过程.png) 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 | ![img.png](resource/JSBridge.png) 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 | ![img.png](resource/生命周期.png) 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 | ![img.png](resource/Activity界面层级.png) 13 | - Activity的刷新是从上到下的,decorView都不存在,view是没办法刷新的 14 | 15 | ### View的绘制在Activity的哪个生命周期方法执行的?activity与window与view一起工作的?(图中两个流程都要掌握) 16 | ![img.png](resource/AMS与WMS交互.png) 17 | ![img.png](resource/handleResumeActivity.png) 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 | ![img.png](resource/View与Window逻辑结构.png) 54 | ![img.png](resource/View绘制.png) 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 | ![img.png](resource/View的Measure过程.png) 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 | ![img.png](resource/ViewGroup的measure过程.png) 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 | ![img.png](resource/ViewGroup的Layout过程.png) 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 | ![img.png](resource/View的Draw过程.png) 134 | - drawBackground():绘制背景; 135 | - 保存当前的canvas层(不是必须的); 136 | - onDraw(): 绘制View的内容,这是一个空实现,需要子View根据要绘制的颜色、线条等样式去具体实现,所以要在子View里重写该方法; 137 | - dispatchDraw(): 对所有子View进行绘制;单一View的dispatchDraw()方法是一个空方法,因为单一View没有子View,不需要实现dispatchDraw ()方法,而ViewGroup就不一样了,它实现了dispatchDraw()方法去遍历所有子View进行绘制; 138 | - onDrawForeground():绘制装饰,比如滚动条; 139 | ![img.png](resource/ViewGroup的Draw过程.png) 140 | 141 | ### 刷新View的方法 142 | ![img.png](resource/invalidate刷新流程.png) 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 | - ![img.png](resource/Activity启动流程.png) 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 | ![img.png](resource/屏幕分类.png) 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 | ![img.png](resource/组件化架构图.png) 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 | ![img.png](resource/BlockCannery核心流程.png) 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 | ![img.png](resource/Mvvm架构.png) 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 | ![img_1.png](resource/databinding布局.png) 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 | ![img.png](resource/观察者的Lifecycle.png) 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 | ![img.png](resource/ViewModel与Activity生命周期.png) 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 | ![img.png](resource/LeakCannery工作流程.png) 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 | ![img.png](resource/LeakCannery工作流程2.png) 59 | ![img.png](resource/LeakCannery工作流程3.png) 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 | ![img.png](resource/MVC.png) 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 | ![img.png](resource/MVP.png) 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 | ![img.png](resource/MVVM.png) 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 | ![img.png](resource/OkHttp流程.png) 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 | ![img.png](resource/分发器异步请求.png) 40 | ![img.png](resource/异步请求流程.png) 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 | ![img.png](resource/单个任务执行流程.png) 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 | ![img.png](resource/retrofit处理流程.png) 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 对象 134 | ```java 135 | public Object adapt(Call call) { 136 | Observable> responseObservable = 137 | isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call); 138 | //... 139 | return RxJavaPlugins.onAssembly(observable); 140 | } 141 | ``` 142 | #### 至此我们走完了create方法,接下来我们看 enqueue方法是如何创建OkHttp的: 143 | - 1.声明一个 okhttp3.Call 对象,并通过createRawCall() 内部通过callFactory来给call赋值,callFactory 实际是一个 OkHttpClient 对象,callFactory.newCall(requestFactory.create(args));方法中的 requestFactory.create(args)方法会返回一个 Request 的对象 144 | - 2.调用 okhttp3.Call 的 enqueue 方法,进行真正的网络请求 145 | - 3.解析响应,请求成功的回调,请求失败的回调 146 | 147 | #### 接下来我们看求响应结果是如何解析的 148 | - 比如我们在构造 Retrofit 的时候加上 addConverterFactory(GsonConverterFactory.create())这行代码,我们的响应结果是如何通过 Gson 直接解析成数据模型的? 149 | - 在 OkHttpCall 的enqueue方法的onResponse中调用了parseResponse方法 150 | ```java 151 | //OkHttpCall.java 152 | @Override 153 | public void enqueue(final Callback callback) { 154 | 155 | okhttp3.Call call; 156 | ... 157 | call.enqueue( 158 | new okhttp3.Callback() { 159 | @Override 160 | public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { 161 | Response response; 162 | try { 163 | //1 解析响应 通过parseResponse解析响应返回给回调接口 164 | response = parseResponse(rawResponse); 165 | } catch (Throwable e) { 166 | throwIfFatal(e); 167 | callFailure(e); 168 | return; 169 | } 170 | } 171 | ... 172 | }); 173 | } 174 | ``` 175 | - responseConverter内部调用了 responseConverter.convert方法,对应的Converter的具体实现类的convert方法 176 | ```java 177 | //OkHttpCall.java 178 | private final Converter responseConverter; 179 | Response parseResponse(okhttp3.Response rawResponse) throws IOException { 180 | ResponseBody rawBody = rawResponse.body(); 181 | ... 182 | ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); 183 | try { 184 | //1 通过 responseConverter 转换 ResponseBody 185 | T body = responseConverter.convert(catchingBody); 186 | return Response.success(body, rawResponse); 187 | } catch (RuntimeException e) { 188 | catchingBody.throwIfCaught(); 189 | throw e; 190 | } 191 | } 192 | ``` 193 | - 我们的responseConverter是在HttpServiceMethod#parseAnnotations内部调用createResponseConverter方法,继续深入后发现,是从 converterFactories工厂中遍历取出 194 | ```java 195 | public Converter nextResponseBodyConverter( 196 | @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { 197 | ... 198 | //1 从 converterFactories 遍历取出一个来调用 responseBodyConverter 方法,注意根据 responseType 返回值类型来取到对应的 Converter,如果不为空,直接返回此 Converter 对象 199 | int start = converterFactories.indexOf(skipPast) + 1; 200 | for (int i = start, count = converterFactories.size(); i < count; i++) { 201 | Converter converter = 202 | converterFactories.get(i).responseBodyConverter(type, annotations, this); 203 | if (converter != null) { 204 | //noinspection unchecked 205 | return (Converter) converter; 206 | } 207 | } 208 | ... 209 | } 210 | ``` 211 | 212 | ### 面试问题 213 | #### 什么是动态代理? 214 | - 动态指的是在运行期,而代理指的是实现了某个接口的具体类,称之为代理,会调用了 InvocationHandler 的 invoke方法。 215 | - retrofit中的实现:在代码运行中,会动态创建 GitHubApiService 接口的实现类,作为代理对象,代理接口的方法 216 | - 在我们调用GitHubApiService 接口的实现类的 listRepos方法时,会调用了 InvocationHandler 的 invoke方法 217 | - 本质上是在运行期,生成了 GitHubApiService 接口的实现类,调用了 InvocationHandler 的 invoke方法 218 | 219 | #### 整个请求流程 220 | - 我们在调用 GitHubApiService 接口的 listRepos方法时,会调用 InvocationHandler 的 invoke方法 221 | - 然后执行 loadServiceMethod方法并返回一个 HttpServiceMethod 对象并调用它的 invoke方法 222 | - 然后执行 OkHttpCall的 enqueue方法,本质执行的是 okhttp3.Call 的 enqueue方法 223 | - 当然这期间会解析方法上的注解,方法的参数注解,拼成 okhttp3.Call 需要的 okhttp3.Request 对象 224 | - 然后通过 Converter 来解析返回的响应数据,并回调 CallBack 接口 225 | 226 | #### 方法上的注解是什么时候解析的,怎么解析的? 227 | - 在 ServiceMethod.parseAnnotations(this, method); 方法中开始的 228 | - 具体内容是在 RequestFactory 类中,调用 RequestFactory.parseAnnotations(retrofit, method); 方法实现的 229 | 230 | #### Converter 的转换过程,怎么通过 Gson 转成对应的数据模型的? 231 | - 通过成功回调的 parseResponse(rawResponse);方法开始 232 | - 通过 responseConverter 的 convert 方法 233 | - responseConverter 是通过 converterFactories 通过遍历,根据返回值类型来使用对应的 Converter 解析 234 | 235 | #### CallAdapter 的替换过程,怎么转成 RxJava 进行操作的? 236 | - 通过配置 addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 在 callAdapterFactories 这个 list 中添加 RxJava2CallAdapterFactory 237 | - 如果不是 Kotlin 挂起函数最终调用的是 CallAdapted 的 adapt方法 238 | - callAdapter 的实例是通过 callAdapterFactories 这个 list 通过遍历,根据返回值类型来选择合适的CallAdapter 239 | 240 | #### Retrofit涉及到的设计模式有哪些? 241 | ##### 1.外观模式 242 | - 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。 243 | - Retrofit 对客户端模块提供统一接口,Retrofit 类内部封装了 ServiceMethod、CallAdapter 和 Converter 等组件。并且 CallAdapter 和 Converter 都是抽象为接口,用户可以扩展自定义的实现。 244 | 245 | ##### 2.动态代理 Proxy 246 | - Retrofit使用的是动态代理,是通过反射机制来动态生成方法接口的代理对象的。动态代理的实现是通过 JDK 提供的 InvocationHandler 接口,实现该接口重写其调用方法 invoke 247 | 248 | ##### 3.建造者模式 Builder 249 | - 建造者模式属于创建型模式,将构建复杂对象的过程和它的部件解耦,使构建过程和部件的表示隔离。 250 | - Retrofit 内部包含 Retrofit.Builder,Retrofit 包含的域都能通过 Builder 进行构建 251 | 252 | ##### 4.适配器模式 Adapter 253 | - 适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。 254 | - Retrofit 中的 CallAdapter 的接口,让已经存在的OkHttpCall,被不同的标准、平台来调用,让其他平台做不同的实现来转换。 255 | 256 | ##### 5.工厂模式 257 | - 创建型模式,其主要功能都是将对象的实例化部分抽取出来。只需要传给工厂一些参数信息,工厂解析参数返回相应的产品。对外隐藏产品细节,逻辑简单。一个工厂只生产一种产品,所有的工厂都实现同一个抽象接口。 258 | - 比如 RxJavaCallAdapterFactory,ConverterFactory等 259 | 260 | ##### 6.策略模式 261 | - 完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务 262 | - RxJavaCallAdapterFactory 的 get 方法返回 SimpleCallAdapter 对象(或 ResultCallAdapter 对象)就对应具体的策略实现 263 | 264 | #### 7.观察者 265 | - 在 OkHttpCall 的 enqueue 实现方法中,通过在 okhttp3.Callback() 的回调方法中调用上述入参 Callback 对象的方法,实现通知观察者。 266 | 267 | 268 | 作者:Ava_T 269 | 链接:https://www.jianshu.com/p/435a5296ee94 270 | 来源:简书 271 | 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 272 | 273 | ### 参考致谢 274 | - https://zhuanlan.zhihu.com/p/421401880 275 | - https://www.bilibili.com/video/BV1DZ4y1N7nL?p=2 -------------------------------------------------------------------------------- /android/框架/RxJava.md: -------------------------------------------------------------------------------- 1 | ### Rxjava流向图 2 | - Observable -> map需求 -> map需求 -> ... -> Observer 3 | - RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。Observable 和 Observer 通过 subscribe() ⽅法实现订阅关系,从⽽ Observable 可以在需要的时候发出事件来通知 Observer。 4 | - RxJava 的事件回调⽅法除了普通事件 onNext() (相当于 onClick() / onEvent())之外,还定义了两个特殊的事件:onCompleted() 和 onError() 5 | - onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做⼀个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发onCompleted() ⽅法作为标志。 6 | - onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列⾃动终⽌,不允许再有事件发出。 7 | - 在⼀个正确运⾏的事件序列中, onCompleted() 和 onError() 有且只有⼀个,并且是事件序列中的最后⼀个。需要注意的是,onCompleted() 和 onError() ⼆者也是互斥的,即在队列中调⽤了其中⼀个,就不应该再调⽤另⼀个。 8 | 9 | ### Oberver观察者 10 | - 它决定事件触发的时候将有怎样的⾏为。 11 | - 除了 Observer 接⼝之外,RxJava 还内置了⼀个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接⼝进⾏了⼀些扩展,但他们的基本使⽤⽅式是完全⼀样的 12 | - 在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成⼀个 Subscriber 再使⽤。所以如果你只想使⽤基本功能,选择 Observer 和 Subscriber 是完全⼀样的 13 | ```java 14 | Subscriber subscriber = new Subscriber() { 15 | @Override 16 | public void onNext(String s) { 17 | Log.d(tag, "Item: " + s); 18 | } 19 | @Override 20 | public void onCompleted() { 21 | Log.d(tag, "Completed!"); 22 | } 23 | @Override 24 | public void onError(Throwable e){ 25 | Log.d(tag,"onError!"); 26 | } 27 | } 28 | ``` 29 | 30 | ### Observable被观察者 31 | - 它决定什么时候触发事件以及触发怎样的事件。 RxJava 使⽤ create() ⽅法来创建⼀个 Observable ,并为它定义事件触发规则: 32 | ```java 33 | //创建⼀个上游被观察者 Observable: 34 | Observable observable = Observable.create(new ObservableOnSubscribe() { 35 | @Override 36 | public void subscribe(ObservableEmitter emitter) throws Exception { 37 | emitter.onNext(1); 38 | emitter.onNext(2); 39 | emitter.onNext(3); 40 | } 41 | }); 42 | ``` 43 | 44 | ### Subscribe订阅 45 | - 创建了 Observable 和 Observer 之后,再⽤ subscribe() ⽅法将它们联结起来,整条链⼦就可以⼯作了 46 | ```java 47 | observable.subscribe(observer); 48 | ``` 49 | 50 | ### flatMap操作 51 | ```java 52 | Observable.just(f.getPath()) 53 | .flatMap((Function>) qnUploadPresenter::driverUploadPic) 54 | .flatMap((Function>) imagePath -> { 55 | String pImagePath = imagePath; 56 | if (null != imagePath && TextUtils.isEmpty(imagePath)) { 57 | pImagePath = ""; 58 | } 59 | 60 | return FoYoNet.builder().params("imageUrl", pImagePath) 61 | .service(DriverInfoService.class) 62 | .method((IMethod) DriverInfoService::userCheckCard) 63 | .build().request(); 64 | }).subscribeOn(Schedulers.io()) 65 | .observeOn(AndroidSchedulers.mainThread()) 66 | .subscribe(new FoYoObserver() { 67 | @Override 68 | public void onSuccess(CheckIdCardEntity entity) { 69 | if (null != entity && entity.data != null && entity.data.size() > 0 && 0 == entity.status.code) {//status 等于N 说明 数据正常返回 但数据有错误 70 | callback.onTaskLoaded(entity); 71 | } 72 | } 73 | 74 | @Override 75 | public void onFailure(int code, String desc) { 76 | callback.onDataNotAvailable(code, desc); 77 | } 78 | 79 | }); 80 | ``` 81 | 82 | ### subscribeOn()和observeOn()的区别 83 | - subscribeOn()改变调用它之前代码的线程 84 | ![img.png](resource/subscribeOn.png) 85 | - observeOn()改变调用它之后代码的线程 86 | ![img.png](resource/observeOn.png) 87 | -------------------------------------------------------------------------------- /android/框架/resource/BlockCannery核心流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/BlockCannery核心流程.png -------------------------------------------------------------------------------- /android/框架/resource/LeakCannery工作流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/LeakCannery工作流程.png -------------------------------------------------------------------------------- /android/框架/resource/LeakCannery工作流程2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/LeakCannery工作流程2.png -------------------------------------------------------------------------------- /android/框架/resource/LeakCannery工作流程3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/LeakCannery工作流程3.png -------------------------------------------------------------------------------- /android/框架/resource/MVC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/MVC.png -------------------------------------------------------------------------------- /android/框架/resource/MVP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/MVP.png -------------------------------------------------------------------------------- /android/框架/resource/MVVM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/MVVM.png -------------------------------------------------------------------------------- /android/框架/resource/Mvvm架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/Mvvm架构.png -------------------------------------------------------------------------------- /android/框架/resource/OkHttp流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/OkHttp流程.png -------------------------------------------------------------------------------- /android/框架/resource/ViewModel与Activity生命周期.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/ViewModel与Activity生命周期.png -------------------------------------------------------------------------------- /android/框架/resource/databinding布局.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/databinding布局.png -------------------------------------------------------------------------------- /android/框架/resource/observeOn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/observeOn.png -------------------------------------------------------------------------------- /android/框架/resource/retrofit处理流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/retrofit处理流程.png -------------------------------------------------------------------------------- /android/框架/resource/subscribeOn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/subscribeOn.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/观察者的Lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/android/框架/resource/观察者的Lifecycle.png -------------------------------------------------------------------------------- /android/设计模式/View绘制理解设计模式-桥接.md: -------------------------------------------------------------------------------- 1 | ### 为什么Activity 不直接把ui交给WMS呢?WindowManager中ViewRootImpl存在的价值是什么? 2 | ![img.png](../基础/resource/View与Window逻辑结构.png) 3 | - ViewRootImpl作为桥梁来连接 activity 和 WMS -------------------------------------------------------------------------------- /android/设计模式/代理模式.md: -------------------------------------------------------------------------------- 1 | ### 静态代理模式 2 | - 为其他对象提供一种代理,以控制这个对象的访问。 3 | - 例如秘书,明星经纪人。 4 | 5 | ```java 6 | // 抽象接口代理类 7 | public interface IMacSeller { 8 | void buy(); 9 | } 10 | 11 | public class USAMacSeller implements IMacSeller { 12 | @Override 13 | public void buy() { 14 | System.out.println("buy a mac book from USA"); 15 | } 16 | } 17 | // 香港卖家从美国带货,相当于中介 18 | public class HonkongMacSeller implements IMacSeller { 19 | IMacSeller seller = new USAMacSeller(); 20 | @Override 21 | public void buy() { 22 | seller.buy(); 23 | System.out.println("buy a mac book from HonKong"); 24 | } 25 | } 26 | 27 | public class Client { 28 | public static void main(String[] args) { 29 | System.out.println("静态代理模式"); 30 | IMacSeller seller = new HonkongMacSeller(); 31 | seller.buy(); 32 | } 33 | } 34 | ``` 35 | 36 | ### 动态代理 37 | - 静态代理:程序运行前就已经编写好了 38 | - 动态代理:程序运行前,并不存在,要在程序中动态生成。根据java的反射机制动态生成。 39 | - 动态代理:只支持对接口的代理,不支持对类的代理 40 | - 动态代理:当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用 41 | 42 | ### 动态代理原理 43 | - 在newProxyInstance方法中把接口复制出来,通过这些接口和类加载器,拿到这个代理类cl。 44 | - 然后通过反射的技术复制拿到代理类的构造函数,最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。 45 | 46 | ```java 47 | import java.lang.reflect.InvocationHandler; 48 | import java.lang.reflect.Method; 49 | import java.lang.reflect.Proxy; 50 | 51 | public interface ImiSeller { 52 | void buy(); 53 | } 54 | 55 | public interface IKFC { 56 | void eat(); 57 | } 58 | 59 | //实现类 60 | public class MiStore implements ImiSeller { 61 | @Override 62 | public void buy() { 63 | System.out.println("我们买了小米手机"); 64 | } 65 | } 66 | 67 | public class KFC implements IKFC { 68 | @Override 69 | public void eat() { 70 | System.out.println("吃KFC"); 71 | } 72 | } 73 | 74 | public class ProxyHandler implements InvocationHandler { 75 | // 负责连接代理类和委托类的中间类。 76 | Object realSubject; //真正被我们代理的对象 77 | 78 | public ProxyHandler(Object realSubject) { 79 | this.realSubject = realSubject; 80 | } 81 | 82 | /** 83 | * 84 | * @param proxy 需要代理的对象 85 | * @param method 需要代理的方法 86 | * @param args 传入的参数 87 | * @return 88 | * @throws Throwable 89 | */ 90 | @Override 91 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 92 | System.out.println("开始调用了代理方法"); 93 | Object ret = method.invoke(realSubject, args); 94 | System.out.println("结束调用了代理方法"); 95 | return ret; 96 | } 97 | } 98 | 99 | public class Client { 100 | public static void main(String[] args) { 101 | System.out.println("动态代理模式"); 102 | IKFC kfc = (IKFC)Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{IKFC.class},new ProxyHandler(new KFC())); 103 | kfc.eat(); 104 | 105 | ImiSeller imiSeller = (ImiSeller)Proxy.newProxyInstance(Client.class.getClassLoader(),MiStore.class.getInterfaces() ,new ProxyHandler(new MiStore())); 106 | imiSeller.buy(); 107 | } 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /android/设计模式/六大原则.md: -------------------------------------------------------------------------------- 1 | ### 单一职责 2 | - 一个类只负责一个功能领域中的相应职责,体现出高内聚的特性,降低复杂度,可维护性高,容易被复用。 3 | 4 | ### 开放封闭 5 | - 开放扩展,关闭修改,在不影响原有代码的的情况下进行扩展,这就要抽象化。 6 | 7 | ### 里氏替换 8 | - 自类型必须能够替代他们的基类,使用基类的类并不关心它的具体实现。一个软件的实体用的是它的基类,那么当把这个基类替换成继承它的子类,程序不会发生任何变化。 9 | ```java 10 | public abstract class WeaponGun{ 11 | abstract void shoot(); 12 | } 13 | 14 | public class Rifle extends WeaponGun{ 15 | void shoot(){ 16 | System.out.println("Rifle"); 17 | } 18 | } 19 | 20 | public class HandGun extends WeaponGun{ 21 | void shoot(){ 22 | System.out.println("HandGun"); 23 | } 24 | } 25 | 26 | public class Soldier{ 27 | public void killEnemy(WeaponGun gun){ 28 | gun.shoot(); 29 | System.out.println("soldier kill enemy"); 30 | } 31 | } 32 | public class Main{ 33 | public static void main(String[]args){ 34 | Soldier soldier = new Soldier(); 35 | soldier.killEnemy(new Rifle()); 36 | soldier.killEnemy(new HandGun()); 37 | } 38 | } 39 | 40 | ``` 41 | 42 | ### 依赖倒置 43 | - 依赖于抽象,不要依赖于具体。抽象不应依赖于细节,细节应依赖于抽象,面向接口编程。 44 | 45 | ```java 46 | public interface IReader{ 47 | void getContent(); 48 | } 49 | public class Newspaper implements IReader{ 50 | public void getContent(){ 51 | System.out.println("邓肯退役了"); 52 | } 53 | } 54 | 55 | public class Book implements IReader{ 56 | public void getContent(){ 57 | System.out.println("阿里巴巴与四十大盗"); 58 | } 59 | } 60 | public class Mother{ 61 | public void read(IReader iReader){ 62 | System.out.println("Mom is Reading"); 63 | iReader.getContent(); 64 | } 65 | } 66 | public class Main{ 67 | public static void main(String[]args){ 68 | Mother mother = new Mother(); 69 | mother.read(new Book()); 70 | mother.read(new Newspaper()); 71 | } 72 | } 73 | ``` 74 | ### 接口隔离 75 | - 使用一些小的接口,总比使用一些较大的接口要好 76 | ```java 77 | public interface IPrettyGirl{ 78 | //寻找美女的标准 79 | void goodLooking(); 80 | void greatTemprament(); 81 | } 82 | public interface INiceFingure{ 83 | void niceFigure(); 84 | } 85 | public abstract class BaePrettyGirl implements IPrettyGirl,INiceFingure{} 86 | //明星 87 | public class AngleBaby extends BaePrettyGirl { 88 | void goodLooking(){ 89 | System.out.println("angle is goodLooking"); 90 | } 91 | void niceFigure(){ 92 | System.out.println("angle is niceFigure"); 93 | } 94 | void greatTemprament(){ 95 | System.out.println("angle is greatTeprament"); 96 | } 97 | } 98 | //模特 99 | public class ModelA implements INiceFingure{ 100 | void niceFigure(){ 101 | System.out.println("modelA is niceFigure"); 102 | } 103 | } 104 | //星探1 105 | public class Searcher{ 106 | public void searchActress(BaePrettyGirl girl){ 107 | System.out.println("找女演员"); 108 | girl.goodLooking(); 109 | girl.niceFigure(); 110 | girl.greatTemprament(); 111 | } 112 | } 113 | //星探2 114 | public class SearchB{ 115 | public void searchSuperModel(INiceFingure girl){ 116 | System.out.println("找女模特"); 117 | girl.niceFigure(); 118 | } 119 | } 120 | 121 | public class Client{ 122 | public static void main(String[]args){ 123 | System.out.println("接口隔离"); 124 | //找明星 125 | Searcher searcher = new Searcher(); 126 | searcher.searchActress(new AngleBaby()); 127 | //找模特 128 | SearchB searchB = new SearchB(); 129 | searchB.searchSuperModel(new ModelA()); 130 | } 131 | } 132 | ``` 133 | 134 | ### 迪米特-最少知识原则 135 | - 对象与对象之间使用尽可能少的方法来关联,避免千丝万缕的联系。类和类的耦合性低 136 | ```java 137 | public class Beaf{ 138 | 139 | } 140 | public class Vagetable{ 141 | 142 | } 143 | public class Bread{ 144 | 145 | } 146 | public class Hamberger{ 147 | public Hamberger(Beaf beaf,Vagetable vagetable,Bread bread){ 148 | System.out.println("make a Hamberger "); 149 | } 150 | } 151 | public class KFC{ 152 | public Hamberger sell(){ 153 | Bread bread = new Bread(); 154 | Vagetable vagetable = new Vagetable(); 155 | Beaf beaf = new Beaf(); 156 | return new Hamberger(beaf,vagetable,bread); 157 | } 158 | } 159 | public class Lucy{ 160 | public void eat(){ 161 | Hamberger hamberger = new KFC().sell(); 162 | System.out.println("Lucy eat a Hamberger "); 163 | } 164 | } 165 | public class Lily{ 166 | public void eat(){ 167 | Hamberger hamberger = new KFC().sell(); 168 | System.out.println("Lily eat a Hamberger "); 169 | } 170 | } 171 | 172 | public class Client{ 173 | public static void main(String[]args){ 174 | System.out.println("迪米特"); 175 | Lily lily = new Lily(); 176 | lily.eat(); 177 | Lucy lucy = new Lucy(); 178 | lucy.eat(); 179 | } 180 | } 181 | ``` 182 | 183 | ### 设计模式三类 184 | - 创建型:单例,工厂 185 | - 结构型:代理,适配器 186 | - 行为型:观察者,策略,责任链 -------------------------------------------------------------------------------- /android/设计模式/单例模式.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Description: 4 | * Created by small small su 5 | * Date: 2022/3/17 6 | * Email: surao@foryou56.com 7 | */ 8 | public class KtBase21 { 9 | 10 | // TODO: 2022/4/7 单例公用:能否 懒加载,线程安全,通过反射破坏(思考 如何拒绝jvm读取类的私有方法:枚举类型单例 无法被反射获取) 11 | private KtBase21() { 12 | } 13 | 14 | // TODO: 2022/4/7 单例 饿汉式 很着急就把自己创建出来了 15 | private static KtBase21 instanceEH = new KtBase21(); 16 | 17 | // 安全性高 只会new 一次 18 | public static KtBase21 getInstanceEH() { 19 | return instanceEH; 20 | } 21 | 22 | // TODO: 2022/4/7 懒汉式 调用时在加载 23 | private static KtBase21 instanceLH; 24 | 25 | public static KtBase21 getInstanceLH() { 26 | if (instanceLH == null) { 27 | instanceLH = new KtBase21(); 28 | } 29 | return instanceLH; 30 | } 31 | 32 | // TODO: 2022/4/7 懒汉式+安全 33 | private static KtBase21 instanceLHAQ; 34 | 35 | public static synchronized KtBase21 getInstanceLHAQ() { 36 | if (instanceLHAQ == null) { 37 | instanceLHAQ = new KtBase21(); 38 | } 39 | return instanceLHAQ; 40 | } 41 | 42 | // TODO: 2022/4/7 双检索 写起来复杂 43 | private static volatile KtBase21 instanceDC = null; 44 | 45 | // volatile 防止指令重拍 46 | public static KtBase21 getInstanceDC() { 47 | if (instanceDC == null) { 48 | synchronized (KtBase21.class) { 49 | if (instanceDC == null) { 50 | instanceDC = new KtBase21(); 51 | } 52 | } 53 | } 54 | return instanceDC; 55 | } 56 | 57 | // TODO: 2022/4/7 静态内部类 :懒加载 + 线程安全 + 写起来方便 58 | //静态内部类 在程序启动的时候 不会加载;只有第一次调用的时候才加载,利用了jdk类加载机制的特性实现懒加载 59 | 60 | private static class SingleHolder { 61 | private static final KtBase21 INSTANCE = new KtBase21(); 62 | } 63 | 64 | public static final KtBase21 getInstance() { 65 | return SingleHolder.INSTANCE; 66 | } 67 | 68 | // TODO: 2022/4/7 枚举类型单例:并不能因此得出 此方法 比前面优秀,只不过不能被反射获取,jvm获取不到枚举类型的构造器 69 | // 枚举类型 没有 无参构造函数 70 | public enum SingleInstance { 71 | INSTANCE; 72 | } 73 | 74 | } 75 | ``` 76 | ### DCL双重检索 77 | #### 为什么要加两次判空,第一次判空能不能不加? 78 | - 假设第一次判空不加,那么每次进入这个方法,INSTANCE不论是不是null,都会执行下面的synchronized代码块,多线程下会出现锁的竞争,而除了第一次初始化,后面的都不会为null,判空的效率比加锁高。 79 | 80 | #### 为什么要进行第二次判空? 81 | - 防止多次初始化:多线程下,有可能会出现两个线程都经过了前面第一次检查,来到了下面的synchronized这里,如果不判空,就会出现一个线程new了一个Singleton出来,然后释放锁,第二个线程进来又会new一个Singleton出来。 82 | 83 | #### INSTANCE = new Singleton();当使用new关键字创建一个对象时,JVM需要做哪些事情 84 | - 1.为对象分配内存 85 | - 2.内存分配完毕,属性设置默认值 86 | - 3.执行构造函数,属性设置初始值。 87 | - 4.建立连接,引用指向对象内存地址。 88 | 89 | #### volatile作用 90 | - 1. 保持内存可见性,2.防止指令重排序 91 | - volatile这里的作用就是防止指令重排 92 | - 可以看到,new一个对象,虽然只有一行代码,实际上需要经过好几个过程,而且这些过程并非顺序执行。 有可能一个对象在未初始化时,就先建立连接了。 93 | - 一旦发生这种情况,使用DCL实现的单例模式,就会导致线程拿到的是一个未被初始化的对象。 94 | - volatile修饰的语句则禁止CPU这种乱序执行,保证指令执行的顺序性。 -------------------------------------------------------------------------------- /android/设计模式/工厂模式.md: -------------------------------------------------------------------------------- 1 | ### 简单工厂,静态工厂 2 | 3 | - 基于一个共同的抽象类和接口 4 | - 隐藏了工厂创建的过程,根据外界给定的信息,决定究竟应该创建那个具体的类,而无需了解这个工厂是如何创建的。 5 | - 集中了所有实例的创建逻辑,一旦工厂出了问题,所有客户端都受到牵连,违背单一职责 6 | - 如果新增任何一个产品,都要修改工厂类,违背了开闭原则 7 | - 一对多的关系 8 | 9 | ```java 10 | //抽象产品接口 11 | public interface IFood { 12 | void eat(); 13 | } 14 | 15 | public class Chips implements IFood { 16 | public void eat() { 17 | System.out.println("我们吃薯条"); 18 | } 19 | } 20 | 21 | public class Chicken implements IFood { 22 | public void eat() { 23 | System.out.println("我们吃鸡"); 24 | } 25 | } 26 | 27 | //麦当劳就是简单工厂,我们不用关注 薯条和 鸡怎么生产的 28 | public class Mcdonlad { 29 | public IFood getFood(String name) { 30 | switch (name) { 31 | case "chip: 32 | return new Chips(); 33 | case "chicken: 34 | return new Chicken(); 35 | break; 36 | } 37 | } 38 | } 39 | 40 | public class Client { 41 | public static void main(String[] args) { 42 | System.out.println("简单工厂"); 43 | Mcdonlad mcdonlad = new Mcdonlad(); 44 | IFood food = mcdonlad.getFood("chip"); 45 | if (food != null) { 46 | food.eat(); 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | ### 工厂方法模式 53 | 54 | - 一个工厂类,派生出多个具体工厂类,每个具体工厂类只能创建一个具体产品类的实例 55 | - 一对一的关系:一个工厂创建一个产品 56 | 57 | ```java 58 | // 抽象产品类 59 | public interface IChips { 60 | void eat(); 61 | } 62 | 63 | public class McChips implements IChips { 64 | public void eat() { 65 | System.out.println("麦当劳薯条"); 66 | } 67 | } 68 | 69 | public class KFCChips implements IChips { 70 | public void eat() { 71 | System.out.println("肯德基薯条"); 72 | } 73 | } 74 | 75 | public class DicsChips implements IChips { 76 | public void eat() { 77 | System.out.println("德克士薯条"); 78 | } 79 | } 80 | 81 | //工厂类 82 | public interface IFactory { 83 | IChips getChips(); 84 | } 85 | 86 | public class Mcdonald implements IFactory { 87 | public IChips getChips() { 88 | System.out.println("欢迎来到麦当劳"); 89 | return new McChips(); 90 | } 91 | } 92 | 93 | public class KFC implements IFactory { 94 | public IChips getChips() { 95 | System.out.println("欢迎来到KFC"); 96 | return new KFCChips(); 97 | } 98 | } 99 | 100 | // 后续有了德克士,只需要new一个德克士工厂就可以 101 | public class Dics implements IFactory { 102 | public IChips getChips() { 103 | System.out.println("欢迎来到德克士"); 104 | return new DicsChips(); 105 | } 106 | } 107 | 108 | public class Client { 109 | public static void main(String[] args) { 110 | System.out.println("工厂方法"); 111 | IFactory store = new Dics(); 112 | store.getChips().eat(); 113 | } 114 | } 115 | ``` 116 | 117 | ### 抽象工厂 118 | - 多个抽象产品类派生出多个具体产品类 119 | - 一个抽象工厂类派生出多个具体工厂类 120 | - 每个具体工厂类可以创建多个具体产品类的实例 121 | - 一对多的关系:一个工厂创建多个产品 122 | 123 | ```java 124 | // 抽象产品 125 | public interface IChicken { 126 | void eat(); 127 | } 128 | 129 | public interface IChips { 130 | void eat(); 131 | } 132 | 133 | //产品类 134 | 135 | 136 | public class KFCChips implements IChips { 137 | public void eat() { 138 | System.out.println("肯德基薯条"); 139 | } 140 | } 141 | 142 | public class KECChicken implements IChicken { 143 | public void eat() { 144 | System.out.println("我们吃到了 KECChicken"); 145 | } 146 | } 147 | 148 | public class McChips implements IChips { 149 | public void eat() { 150 | System.out.println("麦当劳薯条"); 151 | } 152 | } 153 | 154 | public class McChicken implements IChicken { 155 | public void eat() { 156 | System.out.println("我们吃到了 McChicken"); 157 | } 158 | } 159 | 160 | //抽象工厂类 161 | public interface IFactory { 162 | IChips getChips(); 163 | 164 | IChicken getChicken(); 165 | } 166 | 167 | // 工厂实现类 168 | public class KFC implements IFactory { 169 | public IChips getChips() { 170 | System.out.println("生产 肯德基薯条"); 171 | return new KFCChips(); 172 | } 173 | 174 | public IChicken getChicken() { 175 | System.out.println("生产 肯德基鸡"); 176 | return new KECChicken(); 177 | } 178 | } 179 | 180 | public class Maldond implements IFactory { 181 | public IChips getChips() { 182 | System.out.println("生产 麦当劳薯条"); 183 | return new McChips(); 184 | } 185 | 186 | public IChicken getChicken() { 187 | System.out.println("生产 麦当劳鸡"); 188 | return new McChicken(); 189 | } 190 | } 191 | 192 | public class Client { 193 | public static void main(String[] args) { 194 | System.out.println("抽象工厂"); 195 | IFactory store = new Maldond(); 196 | store.getChips().eat(); 197 | store.getChicken().eat(); 198 | } 199 | } 200 | ``` 201 | -------------------------------------------------------------------------------- /android/设计模式/策略模式.md: -------------------------------------------------------------------------------- 1 | ### 策略模式 2 | 3 | - 定义了一系列的算法,并将每一个算法作为一种策略封装起来。 4 | - 一个类定义了多种行为,用户在做选择时,使用策略 替换swich-case,复杂的if-else 5 | 6 | ```java 7 | 8 | public interface TravelStrategy { 9 | void travel(); 10 | } 11 | 12 | public class BikeTravel implements TravelStrategy { 13 | @Override 14 | public void travel() { 15 | System.out.println("自行车方式"); 16 | } 17 | } 18 | 19 | public class TrainTravel implements TravelStrategy { 20 | @Override 21 | public void travel() { 22 | System.out.println("火车方式"); 23 | } 24 | } 25 | 26 | public class FlyTravel implements TravelStrategy { 27 | @Override 28 | public void travel() { 29 | System.out.println("飞机方式"); 30 | } 31 | } 32 | 33 | public class Person { 34 | private TravelStrategy travelStrategy; 35 | 36 | public Person(TravelStrategy travelStrategy) { 37 | this.travelStrategy = travelStrategy; 38 | } 39 | 40 | public void takeHoliday() { 41 | travelStrategy.travel(); 42 | System.out.println("去度假了"); 43 | } 44 | } 45 | 46 | public class Client { 47 | public static void main(String[] args) { 48 | System.out.println("策略模式"); 49 | Person xiaoLi = new Person(new FlyTravel()); 50 | xiaoLi.takeHoliday(); 51 | } 52 | } 53 | ``` -------------------------------------------------------------------------------- /android/设计模式/观察者.md: -------------------------------------------------------------------------------- 1 | ### 观察者模式 2 | 3 | - 当被观察者状态发生改变时,会触发观察者发生改变 4 | - 解耦了观察者与被观察者的实现 5 | 6 | ```java 7 | import java.util.ArrayList; 8 | 9 | //以气象站为实例介绍观察者模式 10 | public interface WeatherSubject { 11 | void registerObserver(Observer o); 12 | 13 | void removeObserver(Observer o); 14 | 15 | void notifyObserver(); 16 | } 17 | 18 | public interface Observer { 19 | void update(); 20 | } 21 | 22 | public class WeatherStation implements WeatherSubject { 23 | List observes = new ArrayList<>(); 24 | private int temperature = 0; 25 | private int dampness = 0; 26 | 27 | @Override 28 | public void registerObserver(Observer o) { 29 | observes.add(o); 30 | } 31 | 32 | @Override 33 | public void removeObserver(Observer o) { 34 | observes.remove(o); 35 | } 36 | 37 | @Override 38 | public void notifyObserver() { 39 | for (Observer o : observes) { 40 | o.update(); 41 | } 42 | } 43 | 44 | 45 | public int getTemerature() { 46 | return temperature; 47 | } 48 | 49 | public void setTemperature(int temperature) { 50 | this.temperature = temperature; 51 | notifyObserver(); 52 | } 53 | 54 | public int getDampness() { 55 | return dampness; 56 | } 57 | 58 | public void setDampness(int dampness) { 59 | this.dampness = dampness; 60 | notifyObserver(); 61 | } 62 | } 63 | 64 | public class LaoWang implements Observer { 65 | WeatherStation weatherStation; 66 | 67 | LaoWang(WeatherStation weatherStation) { 68 | this.weatherStation = weatherStation; 69 | } 70 | 71 | @Override 72 | public void update() { 73 | if (weatherStation.getTemerature() < 0) { 74 | System.out.println("老王穿衣服"); 75 | } 76 | } 77 | } 78 | 79 | public class XiaoLi implements Observer { 80 | 81 | WeatherStation weatherStation; 82 | 83 | XiaoLi(WeatherStation weatherStation) { 84 | this.weatherStation = weatherStation; 85 | } 86 | 87 | @Override 88 | public void update() { 89 | if (weatherStation.getDampness() > 50) { 90 | System.out.println("小李开空调抽湿"); 91 | } 92 | } 93 | } 94 | 95 | public class Client { 96 | public static void main(String[] args) { 97 | System.out.println("观察者模式"); 98 | WeatherStation weatherStation = new WeatherStation(); 99 | LaoWang laoWang = new LaoWang(weatherStation); 100 | XiaoLi xiaoLi = new XiaoLi(weatherStation); 101 | weatherStation.registerObserver(laoWang); 102 | weatherStation.registerObserver(xiaoLi); 103 | 104 | // 老王和小李 没有轮询去问气象站,而是气象站发生变化后通知老王小李 105 | weatherStation.setTemperature(-10); 106 | weatherStation.setDampness(50); 107 | 108 | } 109 | } 110 | ``` 111 | -------------------------------------------------------------------------------- /android/设计模式/责任链模式.md: -------------------------------------------------------------------------------- 1 | ### 责任链模式 2 | 3 | - 他让多个处理器都有机会处理该请求,一个处理器处理完成(有可能不处理)之后就把请求推给下一个处理器 4 | - 处理器至少 有两个方法,一个是处理请求,一个是设置下一个链 5 | - 责任链模式把多个处理器串成链,让请求在链上传递 6 | 7 | ```java 8 | 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | 12 | public class ChainRespPattern { 13 | private List chainList; 14 | // 作为链上传递的请求 15 | private Integer integer= 10; 16 | ChainRespPattern() { 17 | chainList = new ArrayList<>(); 18 | } 19 | 20 | public void inputChain(Handler handler) { 21 | chainList.add(handler); 22 | } 23 | 24 | public void executeHandler() { 25 | for (int i = 0; i < chainList.size(); i++) { 26 | if (i + 1 < chainList.size()) { 27 | chainList.get(i).setNextHandler(chainList.get(i + 1)); 28 | } 29 | chainList.get(i).process(integer); 30 | } 31 | } 32 | 33 | } 34 | 35 | //处理器至少 有两个方法,一个是处理请求,一个是设置下一个链 36 | abstract class Handler { 37 | protected Handler nextHandler; 38 | 39 | public void setNextHandler(Handler nextHandler) { 40 | this.nextHandler = nextHandler; 41 | } 42 | 43 | public abstract void process(Integer info); 44 | } 45 | 46 | class Leader extends Handler { 47 | @Override 48 | public void process(Integer info) { 49 | if (info > 0 && info < 11) { 50 | // 只处理0-10的请求 51 | System.out.println("leader 处理"); 52 | } else { 53 | // 否则 下一个处理器处理 54 | nextHandler.process(info); 55 | } 56 | } 57 | } 58 | 59 | class Boss extends Handler { 60 | @Override 61 | public void process(Integer info) { 62 | System.out.println("boss 处理"); 63 | } 64 | } 65 | 66 | 67 | ``` -------------------------------------------------------------------------------- /android/设计模式/适配器.md: -------------------------------------------------------------------------------- 1 | ### 类适配器模式 2 | - 将一个类的接口转换成客户希望的另外一个接口 3 | - RecycleView的Adapter 4 | 5 | ```java 6 | public interface Power5V { 7 | void getPower5V(); 8 | } 9 | 10 | public class Power220 { 11 | public void getPower220() { 12 | System.out.println("当前是220V"); 13 | } 14 | } 15 | 16 | public class PowerAdapter extends Power220 implements Power5V { 17 | @Override 18 | public void getPower5V() { 19 | super.getPower220(); 20 | transform(); 21 | System.out.println("当前是5V "); 22 | } 23 | 24 | void transform(){ 25 | System.out.println("我们做了转压处理"); 26 | } 27 | } 28 | 29 | public class NoteBook { 30 | public void powerOn(Power5V power5V) { 31 | power5V.getPower5V(); 32 | System.out.println("获取到 5V"); 33 | } 34 | } 35 | 36 | public class Client { 37 | public static void main(String[] args) { 38 | System.out.println("类适配器模式"); 39 | NoteBook noteBook = new NoteBook(); 40 | noteBook.powerOn(new PowerAdapter()); 41 | } 42 | } 43 | ``` 44 | 45 | ### 对象适配器 46 | - 类适配器使用对象继承的方式,是静态的定义方式 47 | - 对象适配器使用对象组合的方式,是动态组合的方式 48 | ```java 49 | public interface Power12V { 50 | void getPower12V(); 51 | } 52 | public class PowerAdapter implements Power12V { 53 | //对象适配器才去的是组合的方式 54 | private Power220 power220; 55 | 56 | PowerAdapter(Power220 power220){ 57 | this.power220 = power220; 58 | } 59 | 60 | @Override 61 | public void getPower12V() { 62 | power220.getPower220(); 63 | transform(); 64 | System.out.println("当前是12V "); 65 | } 66 | 67 | void transform(){ 68 | System.out.println("我们做了转压处理"); 69 | } 70 | } 71 | public class Power220 { 72 | public void getPower220() { 73 | System.out.println("当前是220V"); 74 | } 75 | } 76 | 77 | public class NoteBook{ 78 | public void turnOn(Power12V power12V){ 79 | power12V.getPower12V(); 80 | System.out.println("获取到 12V "); 81 | } 82 | } 83 | public class Client { 84 | public static void main(String[] args) { 85 | System.out.println("对象适配器模式"); 86 | PowerAdapter powerAdapter = new PowerAdapter(new Power220()); 87 | NoteBook noteBook = new NoteBook(); 88 | noteBook.turnOn(powerAdapter.getPower12V()); 89 | } 90 | } 91 | ``` 92 | 93 | 94 | -------------------------------------------------------------------------------- /interview.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /java基础/JVM.md: -------------------------------------------------------------------------------- 1 | ## JVM 2 | - 编译流程:编译器 将java文件 编译成class文件,交给jvm运行 3 | ### 类加载机制 4 | - 虚拟机把class文件加载到内存中,并对数据进行校验,转换解析和初始化,形成可以虚拟机直接使用的java类型即.class 5 | ![img.png](resouse/类加载机制.png) 6 | ### 类加载过程 7 | #### 装载 8 | - 通过一个类的限定名获取这个类的二进制字节流; 9 | - 将这个字节流所代表的静态存储结构转换为方法区运行时数据结构; 10 | - 在java堆中生成一个代表这个类的class对象,这个对象作为我们访问方法区的数据访问入口。 11 | #### 链接 12 | - 1.验证:保证我们加载类的正确性(文件格式,源数据,字节码,符号引用); 13 | - 2.准备:为类的静态变量分配内存,并将其初始化为当前类型的默认值;static int a = 1;那么它在这个准备阶段 a=0; 14 | - 3.解析:把类中的符号引用转换成直接引用。 15 | #### 初始化 16 | - 为静态变量赋值,初始化静态代码块,初始化当前类的父类 17 | ### 类加载器层级 18 | ![img.png](resouse/类加载器.png) 19 | ### 双亲委派机制(父类委派机制) 20 | ![img.png](resouse/双亲委派机制.png) 21 | - 在源码中已经存在了java.lang.String;但是我有自己写了个java.lang.String,这是应该加载哪个?优先加载源码中的String 22 | ```java 23 | // ClassLoader中的loadClass方法 24 | protected Class loadClass(String name, boolean resolve)throws ClassNotFoundException{ 25 | synchronized (getClassLoadingLock(name)) { 26 | // 判断是否加载过,如果加载过,直接拿过来用;否则走下面 27 | Class c = findLoadedClass(name); //类加载机制中的缓存机制 28 | if (c == null) { 29 | long t0 = System.nanoTime(); 30 | try { 31 | if (parent != null) { // 递归调用父类的 loadClass。双亲委派 本质依赖于本层递归。 32 | c = parent.loadClass(name, false); 33 | } else { // 如果递归到 34 | c = findBootstrapClassOrNull(name); 35 | } 36 | } catch (ClassNotFoundException e) { 37 | // ClassNotFoundException thrown if class not found 38 | // from the non-null parent class loader 39 | } 40 | 41 | if (c == null) { 42 | // If still not found, then invoke findClass in order 43 | // to find the class. 44 | long t1 = System.nanoTime(); 45 | c = findClass(name); 46 | 47 | // this is the defining class loader; record the stats 48 | PerfCounter.getParentDelegationTime().addTime(t1 - t0); 49 | PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); 50 | PerfCounter.getFindClasses().increment(); 51 | } 52 | } 53 | if (resolve) { 54 | resolveClass(c); 55 | } 56 | return c; 57 | } 58 | } 59 | ``` 60 | ### 打破双亲委派机制 61 | - 继承classload类,重写loadclass方法,把 c = parent.loadClass(name, false) 去掉 62 | - SPI机制:Service Provider Interface 服务提供接口;通过接口,将这些装配的控制权移到外部,可以随时替换实现。 63 | - OSGI:它支持热部署,热更新的。 64 | 65 | ### 运行时数据区 66 | ![img.png](resouse/运行时数据区.png) 67 | - 运行时数据区是运行时的一块内存区域,划分为上图五块内容。 68 | 69 | #### 方法区 70 | - 虚拟机启动时创建,线程共享,逻辑上是堆的一部分,即非堆内存。如果内存不足,会抛出一个oom 71 | - 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 运行时常量池 是方法区的一部分,存放编译生成的各种字面量和符号引用,运行期间也可能将新的常量放入池中 72 | 73 | #### 堆 74 | - 线程共享。如果内存不足,会抛出一个oom 75 | - 存放对象实例,数组。 76 | 77 | #### Java虚拟机栈 78 | - 执行java方法,是线程私有的,栈深度不够,压不进这么多栈帧了,会StackOverFlow 79 | - 栈中单位是栈帧,一个栈帧代表一个方法的执行。 80 | 81 | #### 本地方法栈 82 | - 执行native方法,线程私有的,栈深度不够,压不进这么多栈帧了,会StackOverFlow 83 | 84 | #### 程序计数器 85 | - 记录程序执行的位置,是线程私有的。无法进入到本地方法栈。本地方法栈是隔离的。 86 | 87 | ### 栈帧结构 88 | ![img.png](resouse/栈帧结构.png) 89 | 90 | #### 局部变量表 91 | - 方法中定义的局部变量以及方法的参数 92 | 93 | #### 操作数栈 94 | - 以压栈 以及出栈的方式存储操作数 95 | 96 | ```java 97 | int a = 1; //把 a 从局部变量表中拿出来 放到操作数栈 98 | int b = 1; //把 b 从局部变量表中拿出来 放到操作数栈 99 | int c = a + b; //把 a 和 b 拿出来进行运算,完成之后把 c = 2 放到局部变量表 100 | ``` 101 | #### 方法的返回地址 102 | - 遇到方法返回的字节码指令return 103 | - 出现了异常,有异常处理器则交给一场处理器,没有则抛异常 104 | 105 | #### 动态链接 106 | - 运行时 将对方法的符号引用 链接成 对调用方法的直接引用。 107 | ```java 108 | void a(){ b(); } 109 | void b(){ c(); } 110 | void c(){} 111 | // 方法具体调用的哪一个,只有执行了之后我才能直到 112 | ``` 113 | ### java堆的分代设计:新生代,老年代,永久代 114 | ![img.png](resouse/堆分代设计.png) 115 | - 新老年代划分:新对象先进入新生代,经过几轮gc后,依然存活的进老年代。垃圾回收主要在新生代进行。 116 | - Eden区:经过几轮回收后,会产生空间碎片,再次new新对象时,明明有空间但是无法创建成功。于是将新生代再次划分空间Eden区。 117 | - 将Eden区一部分空间划分为大小相等的两块,s0和s1,每次使用其中一块,每次gc时我将这两块的内容 移动到另一块区域,以额外空间消耗来换取空间无碎片化。 118 | - Eden :s0 :s1大小 8:1:1; Eden区是存放新生对象的。Eden区越小 gc次数越多。 119 | - 当新生代内存吃紧的时候,新生代的对象会直接进入老年代,因为老年代有担保机制。 120 | - 永久代:在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。 121 | 122 | 123 | ### 对象创建过程 124 | ![img.png](resouse/对象创建过程.png) 125 | 126 | ### 什么时候进行垃圾回收 127 | #### 1.自动进行垃圾回收 128 | - 当Eden区 或者s区不够用了 129 | - 老年代空间不够用了 130 | - 方法区(非堆)空间不够用了 131 | #### 2.手动回收 132 | - 手动调用System.gc() 用的比较少,耗性能。时机不确定,只是发一个通知。一般让线程睡500毫秒来等待线程操作。 133 | 134 | ### 如何判定一个对象是垃圾 135 | - 通过引用计数法,引用为0的对象便是垃圾,引用数目>0则不是垃圾。效率高,但是如果AB相互持有引用,循环引用(容易造成内存泄漏——>堆积起来 容易导致内存溢出),导致永远不能被回收,该方法被虚拟机抛弃。 136 | - 可达性分析(根搜索),目前虚拟机使用的算法。 137 | 138 | #### GCRoot 根节点 139 | - 类加载器,Thread,虚拟机栈局部变量表,本地方法栈的JNI引用的对象,方法区中static成员,方法区中常量引用。 140 | 141 | #### 可达性 142 | - gc可达对象:从gcRoot开始往下遍历,形成一个引用链,能够遍历到的对象都成为可达对象。对于循环引用,它不会在引用链上,所以顺理成章的进行回收。 143 | ![img.png](resouse/gcRoot.png) 144 | 145 | ### 垃圾收集算法 146 | #### 1.标记-清除算法 147 | - 标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间 148 | - 缺点:容易产生内存碎片 149 | 150 | #### 2.复制算法 151 | - 它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。 152 | - 这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。 153 | 154 | #### 3.标记-整理算法 155 | - 标记阶段的任务是标记出所有需要被回收的对象,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存 156 | 157 | #### 4.分代回收算法 158 | - 分代收集算法是目前大部分JVM的垃圾收集器采用的算法 159 | - 它的核心思想是根据对象存活的生命周期将内存划分为老年代和新生代,老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。 160 | - 老年代的特点是每次回收都只回收少量对象,一般使用的是标记整理算法 161 | - 新生代采取复制算法,新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor(s0和s1)空间,每次使用Eden空间和其中的一块s0空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块s1空间中,然后清理掉Eden和刚才使用过的Survivor空间。 162 | 163 | #### 垃圾回收器划分 164 | - Minor GC、Major GC、Full GC 165 | - Minor GC用于清理年轻代区域 166 | - Major GC 用于清理老年区域 167 | - Full GC 用于清理年轻代、年老代区域。成本较高,会对系统产生影响。System.gc 会出发Full GC,代价高。 168 | 169 | ### 参考致谢 170 | - https://www.bilibili.com/video/BV1AY411H7DY?p=20 171 | -------------------------------------------------------------------------------- /java基础/StringBulider和StringBuffer.md: -------------------------------------------------------------------------------- 1 | ### String 2 | - String是final修饰的,不可变,每次操作都会产生新的String对象 3 | 4 | #### StringBuffer和StringBuilder 5 | - StringBuffer和StringBuilder都是在原对象上操作 6 | - StringBuffer是线程安全的,StringBuilder线程不安全的 7 | - StringBuffer方法都是synchronized修饰的 8 | - 性能:StringBuilder > StringBuffer > String 9 | - 在不考虑多线程的情况下,优先使用StringBuilder,多线程使用共享变量时使用StringBuffer -------------------------------------------------------------------------------- /java基础/equal与hashCode.md: -------------------------------------------------------------------------------- 1 | ### == 2 | - == 对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址。 3 | 4 | ### Object中equals()方法 5 | - equals:object类中默认也是采用==比较,通常会重写(为了比较成员变量中的值)。如果不作处理,跟 == 没什么差别,比较内存地址是否一样。 6 | 7 | ```java 8 | public boolean equals(Object obj) { 9 | return (this == obj); 10 | } 11 | ``` 12 | 13 | ### hashCode() 14 | - Object中的方法,将对象的内存地址转化成整数之后返回。 15 | - 约定:如果两个对象相等,必须具备相同的哈希码;如果两个对象哈希码想等,则两个对象可能相等,可能不想等。 16 | 17 | ### String 中重写了equals()方法 18 | - 字符串的equals()是对值进行对比,两个不同地址存放的相同的字符串,equals()方法返回true 19 | 20 | ### 为什么重写equals()的时候还要重写hashCode() 21 | - 由于 hashCode() 中的约定 22 | - 规定:如果两个对象调用equals方法返回true,那么两个对象的hashCode必须返回相同的整数。 23 | ```java 24 | String s1 = new String("hello"); 25 | String s2 = new String("hello"); 26 | s1.euqals(s2); //返回true 27 | //但是 s1.hashCode() 不一定等于 s2.hashCode() 就违背了规定。 28 | // 所以要对hashCode方法进行重写。因此String类中不仅重写了equals 还重写了 hashCode 29 | ``` -------------------------------------------------------------------------------- /java基础/final关键字.md: -------------------------------------------------------------------------------- 1 | ### final 2 | - final是用于修饰类、成员变量和成员方法,类不可被继承,成员变量不可变,成员方法不可被重写 3 | 4 | ### finally 5 | - finally与try…catch…共同使用,确保无论是否出现异常都能被调用到; 6 | 7 | ### finalize 8 | - finalize是类的方法,垃圾回收前会调用此方法,子类可以重写finalize方法实现对资源的回收 9 | 10 | ### Serlizable 11 | - 是java序列化接口,在硬盘上读写,读写的过程中有大量临时变量产生,内部执行大量的I/O操作,效率很低; 12 | 13 | ### Parcelable 14 | - 是Android序列化接口,效率高,在内存中读写,但使用麻烦,对象不能保存到磁盘中。 -------------------------------------------------------------------------------- /java基础/resouse/ConcurrentHashMap分段锁.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ConcurrentHashMap分段锁.png -------------------------------------------------------------------------------- /java基础/resouse/Executors的默认方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/Executors的默认方法.png -------------------------------------------------------------------------------- /java基础/resouse/ThradLocal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ThradLocal.png -------------------------------------------------------------------------------- /java基础/resouse/ThradLocal弱引用.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ThradLocal弱引用.png -------------------------------------------------------------------------------- /java基础/resouse/ThreadPoolExecutorExtend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ThreadPoolExecutorExtend.png -------------------------------------------------------------------------------- /java基础/resouse/ThreadPoolExecutor变量.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ThreadPoolExecutor变量.png -------------------------------------------------------------------------------- /java基础/resouse/ThreadPoolExecutor状态.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/ThreadPoolExecutor状态.png -------------------------------------------------------------------------------- /java基础/resouse/execute提交流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/execute提交流程.png -------------------------------------------------------------------------------- /java基础/resouse/gcRoot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/gcRoot.png -------------------------------------------------------------------------------- /java基础/resouse/hashmap容量.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/hashmap容量.png -------------------------------------------------------------------------------- /java基础/resouse/hashmap长度.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/hashmap长度.png -------------------------------------------------------------------------------- /java基础/resouse/hash运算.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/hash运算.png -------------------------------------------------------------------------------- /java基础/resouse/java注解.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/java注解.png -------------------------------------------------------------------------------- /java基础/resouse/jdk7中CHM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/jdk7中CHM.png -------------------------------------------------------------------------------- /java基础/resouse/jdk8中CHM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/jdk8中CHM.png -------------------------------------------------------------------------------- /java基础/resouse/不同jdk的区别.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/不同jdk的区别.png -------------------------------------------------------------------------------- /java基础/resouse/为什么不推荐使用Executors的默认方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/为什么不推荐使用Executors的默认方法.png -------------------------------------------------------------------------------- /java基础/resouse/双亲委派机制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/双亲委派机制.png -------------------------------------------------------------------------------- /java基础/resouse/可达性分析.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/可达性分析.png -------------------------------------------------------------------------------- /java基础/resouse/哈希表.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/哈希表.png -------------------------------------------------------------------------------- /java基础/resouse/堆分代设计.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/堆分代设计.png -------------------------------------------------------------------------------- /java基础/resouse/对象创建过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/对象创建过程.png -------------------------------------------------------------------------------- /java基础/resouse/对象头.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/对象头.png -------------------------------------------------------------------------------- /java基础/resouse/扩容要做的事情.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/扩容要做的事情.png -------------------------------------------------------------------------------- /java基础/resouse/栈帧结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/栈帧结构.png -------------------------------------------------------------------------------- /java基础/resouse/类加载器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/类加载器.png -------------------------------------------------------------------------------- /java基础/resouse/类加载机制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/类加载机制.png -------------------------------------------------------------------------------- /java基础/resouse/线程池工作流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/线程池工作流程.png -------------------------------------------------------------------------------- /java基础/resouse/线程池提交任务.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/线程池提交任务.png -------------------------------------------------------------------------------- /java基础/resouse/线程池线程的复用.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/线程池线程的复用.png -------------------------------------------------------------------------------- /java基础/resouse/运行时数据区.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/运行时数据区.png -------------------------------------------------------------------------------- /java基础/resouse/锁的升级.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/java基础/resouse/锁的升级.png -------------------------------------------------------------------------------- /java基础/并发/ThreadLocal.md: -------------------------------------------------------------------------------- 1 | ## ThreadLocal 2 | - ThreadLocal 提供了线程本地的实例。每个使用该变量的线程都会初始化一个完全独立的实例副本,它对其他线程而言是隔离的。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。 3 | - ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景 4 | - ThreadLocal 天然隔离线程的作用,一个线程往里放内容,但是其他线程是拿不到的。 5 | - ThreadLocal 应用与spring的transtion事物,mybatis关于分页处理 6 | ![img.png](../resouse/ThradLocal.png) 7 | - 方法一:把x一层一层往下传,如果中间有别人写好的类库,就无法传。 8 | - 方法二:x设置为static,多线程下不安全。 9 | - 方法三:放到ThreadLocal里面,必须保证每个方法里面用的对象都是同一个。又保证了安全。 10 | 11 | ![img.png](../resouse/ThradLocal弱引用.png) 12 | ### set方法 13 | - static ThreadLocal tl = new ThreadLocal<>(); 14 | - tl.set(new Person) // 拿到当前线程独有的map,然后把 tl 为key ,new Person 为 value 装到一个map里。 15 | ```java 16 | public void set(T value) { 17 | Thread t = Thread.currentThread(); //获取当前线程 18 | // 每个线程有独有的 ThreadLocalMap 。 Map的key可以有多个ThradLocal 19 | ThreadLocalMap map = getMap(t); // ThreadLocal 是个 map。 getMap 是当前线程的 threadLocals 。所以 线程之间不能互相串。 20 | if (map != null) { 21 | map.set(this, value); // 把 value 放到 map 里。this 是 new 出来的 ThreadLocal 对象 tl 22 | } else { 23 | createMap(t, value); // 如果map没有 ,则创建出来。 24 | } 25 | } 26 | ``` 27 | ```java 28 | private void set(ThreadLocal key, Object value) { 29 | 30 | Entry[] tab = table; 31 | int len = tab.length; 32 | int i = key.threadLocalHashCode & (len-1); 33 | 34 | for (Entry e = tab[i]; 35 | e != null; 36 | e = tab[i = nextIndex(i, len)]) { 37 | ThreadLocal k = e.get(); 38 | 39 | if (k == key) { 40 | e.value = value; 41 | return; 42 | } 43 | 44 | if (k == null) { 45 | replaceStaleEntry(key, value, i); 46 | return; 47 | } 48 | } 49 | // set的时候是new了一个 Entry 进行set的,Entry是若引用的子类,在new Entry的时候会调用父类构造方法,生成 WeakReference 弱引用,弱引用指向的是 k,即ThreadLocal 50 | tab[i] = new Entry(key, value); 51 | int sz = ++size; 52 | if (!cleanSomeSlots(i, sz) && sz >= threshold) 53 | rehash(); 54 | } 55 | ``` 56 | ```java 57 | static class Entry extends WeakReference> { 58 | /** Entry是若引用的子类 */ 59 | Object value; 60 | 61 | Entry(ThreadLocal k, Object v) { 62 | super(k); 63 | value = v; 64 | } 65 | } 66 | ``` 67 | ### 为什么要是用弱引用 68 | - 如果是强引用,只要线程不退,就回收不了。即使 tl = null ,但key的引用依然指向 ThreadLocal 对象,所以会造成内存泄漏。 69 | - 如果是弱引用,只要垃圾回收器监测到就会回收。回收后key是空值,value依然会存在,容易造成value的内存泄漏,所以一旦不用的时候,要及时remove掉。 70 | - 如果是在线程池中,任务完成后,首先要清理ThreadLocal,要不然下次用,Map里面有旧记录,有可能就会出现问题。 71 | 72 | -------------------------------------------------------------------------------- /java基础/并发/线程模型.md: -------------------------------------------------------------------------------- 1 | ## 线程 2 | ### 线程和进程的区别 3 | - 进程——资源分配的最小单位,线程——程序执行的最小单位 4 | - 进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 5 | - 进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。 6 | - 调度和切换:线程上下文切换,比进程上下文切换要快得多。 7 | ### 进程间通讯 8 | - 管道、消息队列、共享内存、信号量、网络、文件 9 | ### 线程间通讯 10 | - volatile 和 synchronized 关键字 、等待 / 通知机制 、管道输入/ 输出流 、Thread.join() 的使用 、ThreadLocal 11 | ### 操作系统的内核线程 12 | - Linux 进程实际上是 轻量级进程,进程有独立的地址空间,轻量级进程没有独立的地址空间,只能共享同一个轻量级进程组下的地址空间。进程和轻量级进程都是使用clone系统调用,区别是传的参数不同。 13 | ### 三种线程模型 14 | - 一对一 :一般直接使用API或系统调用创建的线程均为一对一的线程。简单好用但是 并发量有限。 15 | - 多对一 :多对一模型将多个用户线程映射到一个内核线程上,线程之间的切换由用户的代码来进行。提升并发量和性能,但是一个用户阻塞其他用户都无法使用。 16 | - 多对多 :结合了多对一模型和一对一模型的特点,将多个用户线程映射到少数但不止一个内核线程上。解决一对一和多对一的缺点,但是实现难度高。 17 | 18 | ### 创建线程的方式? 19 | - 1.继承Thread类。 该方法的好处是 this 代表的就是当前线程,无法规避java的单继承 20 | - 2.实现Runnable接口。 该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用 21 | - 3.实现Callable接口。该种方法可以创建一个FutureTask对象来接收返回值 22 | 23 | ### 线程状态 24 | - 线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。 25 | 26 | #### 线程的阻塞 27 | - 等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify或notifyAll方法才能被唤醒,wait是object类的方法 28 | - 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。 29 | - 其他阻塞:运行的线程执行sleep或join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。sleep是Thread类的方法。 30 | 31 | ### 锁池 32 | - 所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进行等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列进行等待cpu资源分配。 33 | 34 | ### 等待池 35 | - 当我们调用wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用了notify()或notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放到锁池,而notifyAll()是将等待池的所有线程放到锁池当中。 36 | 37 | ### sleep()、wait() 38 | - sleep就是把cpu的执行资格和执行权释放出去,不再运行此线程,当定时时间结束再取回cpu资源,参与cpu的调度,获取到cpu资源后就可以继续运行了。而如果sleep时该线程有锁,那么sleep不会释放这个锁,而是把锁带着进入了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这个锁。也就是说无法执行程序。如果在睡眠期间其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出interruptexception异常返回,这点和wait是一样的。 39 | - sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。 40 | - sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。 41 | - sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。 42 | - sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。 43 | - sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait 后可能还是有机会重新竞争到锁继续执行的。 44 | 45 | ### yield() 46 | - yield()执行后线程直接进入就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执行资格,所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行 47 | 48 | ### join() 49 | - join()执行后线程进入阻塞状态,例如在线程B中调用线程A的join(),那线程B会进入到阻塞队列,直到线程A结束或中断线程 50 | 51 | ### 参考致谢 52 | - https://blog.csdn.net/weixin_43947102/article/details/120649224 -------------------------------------------------------------------------------- /java基础/并发/锁.md: -------------------------------------------------------------------------------- 1 | ## 锁 2 | ### 什么是锁 3 | - 并发环境下,多个线程会对同一个资源进行争抢,会导致数据不一致的问题,为此编程语言引入锁机制。通过抽象的锁对资源进行锁定。在java中每个object都存在一把锁,锁存在与对象头中。java中每个对象包含三个部分,对象头,实例数据,填充字节。对齐填充字节是为了满足java对象的大小必须是8比特的倍数而设计的。实例数据包含属性,方法。对象头存放对象运行时信息包含两部分,mark word:当前对象运行时信息,如hashcode,锁标识位;class point :指针指向当前对象类型锁在方法区忠德对象类型 4 | - 轻量级锁:不会占用太多资源,性能高。 5 | - 重量级锁:耗费资源,性能低。synchronized 6 | 7 | ### 锁分类 8 | - 乐观锁CAS(无锁):JDK中提供乐观锁,例如原子操作类,Atomic系列。CAS响应速度快,使用cpu的自旋决定的,缺点是吃cpu。 9 | - Lock(显式锁):是一个接口,它的实现有 AbstractQueuedLongSynchronizer、ReentrantLock 10 | - Synchronized(内置锁): 11 | 12 | ### CAS(比较并交换) 13 | - CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 14 | - 如果当前内存位置的值等于预期原值A的话,就将B赋值。否则,处理器 不做任何操作。整个比较并替换的操作是一个原子操作。这样做就不用害怕其它线程同时修改变量。 15 | - compareAndSet,compareAndSwap 16 | - 可以称作:无锁,自旋锁,乐观锁,轻量级锁 17 | 18 | ### 原子操作代替锁,提高性能 19 | - AtomicInteger (CAS机制)代替锁;其内部是有个 volitale int value 进行操作的。 20 | - 先跟内存中的值进行比较,如果跟原有的值一样的话,就交换。 21 | 22 | ```java 23 | AtomicInteger ai = new AtomicInteger(); 24 | ai.incrementAndGet(); 25 | //incrementAndGet 内部源码为 26 | while(true){ 27 | //开启while循环 28 | int oldValue = ai.get(); 29 | int newValue = oldValue +1; 30 | if(ai.compareAndSet(oldValue,newValue)){ 31 | // CAS 操作成功后跳出循环。 32 | // 如果其中一个线程执行失败了,则继续循环执行此方法。 33 | brek; 34 | } 35 | } 36 | ``` 37 | ### Lock 显式锁运行机制,运用到了CAS机制 38 | #### ReentrantLock 39 | - lock.lock() 加锁,lock.unlock()解锁。 40 | - 常写的方式:在finally中写unlock解锁方法。 41 | #### 手写Lock:实现Lock接口,实现lock方法,实现unlock方法 42 | ```java 43 | import java.util.concurrent.LinkedBlockingDeque; 44 | import java.util.concurrent.atomic.AtomicReference; 45 | import java.util.concurrent.locks.LockSupport; 46 | 47 | public class MyLock implements Lock { 48 | AtomicReference owner = new AtomicReference<>(); 49 | // 阻塞队列用来存放没有抢到锁的线程,线程安全高效 50 | LinkedBlockingDeque waiters = new LinkedBlockingDeque(); 51 | 52 | // 实现 lock 与 unlock 53 | public void lock() { 54 | //期待值如果是null,则替换成当前线程。加锁用CAS 55 | while (!owner.compareAndSet(null, Thread.currentThread())) { 56 | // 如果false 抢不到 当前线程等待(其他线程拿到锁会阻塞) 57 | waiters.add(Thread.currentThread()); 58 | LockSupport.park();//让当前线程阻塞,相当于当前线程已经睡了,打了断点。当线程调用LockSupport.unpark之后 才会执行下面语句 59 | // 如果waiters 只有add,没有remove,会造成隐患内存泄漏 60 | waiters.remove(Thread.currentThread()); 61 | } 62 | } 63 | 64 | public void unlock() { 65 | //只有持有锁的线程才能解锁 66 | if(owner.compareAndSet(Thread.currentThread(),null)){ 67 | // 唤醒哪些等待的线程 68 | for(Object o :waiters.toArray()){ 69 | Thread new = (Thread)o; 70 | // 唤醒其他线程 71 | LockSupport.unpark(next); 72 | } 73 | }else{ 74 | //失败了 不做任何操作。 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | ### synchronized 81 | - synchronized 是内置锁,互斥锁,悲观锁,同步锁;等待的线程会进入到一个队列中。 82 | - 早期是重量级锁,- java 6 开始引入了 偏向锁,轻量级锁。所以markword 标识位锁总共分为4种状态:01无锁-标识位0,01偏向锁,00轻量级锁,10重量级锁,就对应了mark word中的四种状态。它会从某一状态升级到另一状态。锁只能升级不能降级。 83 | - 加了 synchronized之后 ,可以是轻量级锁(CAS 实现,响应速度快,利用cpu自旋,吃cpu性能);如果线程比较多,自旋就比较吃性能,所以进行升级成重量级锁(线程阻塞,相应速度慢,不吃cpu,吞吐量提高)。 84 | 85 | ### 锁的级别 86 | - ![img.png](../resouse/对象头.png) 87 | - 对象头中 会存储两部分内容 88 | - 对象⾃身运⾏时所需的数据,也被称为Mark Word,也就是⽤于轻量级锁和偏向锁的关键点。具体的内容包含对象的hashcode、分代年龄、轻量级锁指针、重量级锁指针、GC标记、偏向锁线程。 89 | - 存储类型指针,也就是指向类的元数据的指针,通过这个指针才能确定对象是属于哪个类的实例。如果是数组的话,则还包含了数组的⻓度 90 | 91 | #### 无锁 92 | 93 | #### 偏向锁 markwrod-01 94 | - 理想情况下 不通过mutex lock线程切换,也不通过cas获取锁。执行速度非常快。 95 | - 就算是CAS也有判断、 比较、 交换。而偏向锁 只有一个 if (线程第一次运行){ 在对象头中 写入线程id。}else (线程第二次运行){ 直接拿出对象头中的线程 } 96 | - 如果线程存在锁竞争,会带来额外的锁撤销的消耗。 97 | - 偏向锁往往适用与只有一个线程访问同步块场。 98 | 99 | 100 | #### 轻量级锁 101 | - 操作系统状态分为用户态,内核态。JVM 就跑在用户态。进行加锁或者解锁的时候,不需要向操作系统内核进行申请,对操作系统内核依赖性轻。 102 | - 使用CAS自旋,不会阻塞线程,得不到锁竞争的线程会使用自旋来消耗cpu性能。 103 | - 追求响应时间,同步速度快 104 | 105 | #### 重量级锁 106 | - 进行加锁或者解锁的时候,需要向操作系统内核进行申请,对操作系统内核依赖性重。 107 | - 线程竞争不用自旋,阻塞线程,响应时间缓慢,不消耗cpu。 108 | - 追求吞吐量,同步速度长 109 | 110 | ### synchronized锁的升级 111 | ![img.png](../resouse/锁的升级.png) 112 | - 1.当Thread-1访问对象的时候,首先通过cas操作去获取偏向锁并将锁的偏向位更改为1; 113 | - 2.当另一个线程(thread-2)到达的时候会比较自身线程id和对象头中id是否一致,发现不一致就会去检测对象头中的线程是否存活,如果Thread-1还是存活的就升级为轻量级锁; 114 | - 3.但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁。 115 | - 3.1 如果此时处在偏向锁,出现了重度竞争,会直接升级为重量级锁。 116 | 117 | ### synchronized锁定的到底是什么元素 118 | #### 1.修饰方法 119 | - 非静态方法,锁定的是方法调用者,同一个类的不同对象调用,不会锁。相当于锁的是上厕所的人。 120 | - 静态方法,锁定的是类,而不是方法调用者。相当于锁的是厕所。 121 | 122 | ```java 123 | import java.util.concurrent.TimeUnit; 124 | public class Test{ 125 | public static void main(String[] args){ 126 | Data data = new Data(); 127 | new Thread(()->{data.func1();},"A").start; 128 | TimeUnit.SECONDS.sleep(1); 129 | new Thread(()->{data.func2();},"B").start; 130 | /* 非静态方法 对象锁 */ 131 | // 情景一:若同一个类中func1、fun2 两个方法 都加了 synchronized 非静态方法锁,则锁定的是data;A与B同时执行的话,肯定是A能拿到data运行结果 3秒钟后,1与2 同时输出 132 | // 情景二: 只有 func1 加了synchronized,则先输出 2,后输出1。 133 | 134 | Data data1 = new Data(); 135 | Data data2 = new Data(); 136 | new Thread(()->{data1.func1();},"A").start; 137 | TimeUnit.SECONDS.sleep(1); 138 | new Thread(()->{data2.func2();},"B").start; 139 | // 情景一: 由于不同的对象调用的,所以不存在排队 140 | // 情景四: 由于锁定的是类,虽然有两个对象,但是只有一个类,所以排队 141 | // 情景三: 不会排队,func1,func2两个厕所,func1 锁了,func2没锁。 142 | 143 | 144 | } 145 | } 146 | 147 | class Data { 148 | // 情景一 149 | public synchronized void func1() { 150 | try { 151 | TimeUnit.SECONDS.sleep(3); 152 | } 153 | System.out.println("1..."); 154 | } 155 | 156 | public synchronized void fun2() { 157 | System.out.println("2..."); 158 | } 159 | 160 | // 情景二 161 | public synchronized void func1() { 162 | try { 163 | TimeUnit.SECONDS.sleep(3); 164 | } 165 | System.out.println("1..."); 166 | } 167 | 168 | public void fun2() { 169 | System.out.println("2..."); 170 | } 171 | 172 | // 情景三 173 | public static synchronized void func1() { 174 | try { 175 | TimeUnit.SECONDS.sleep(3); 176 | } 177 | System.out.println("1..."); 178 | } 179 | 180 | public static synchronized void fun2() { 181 | System.out.println("2..."); 182 | } 183 | // 情景四 184 | public static synchronized void func1() { 185 | try { 186 | TimeUnit.SECONDS.sleep(3); 187 | } 188 | System.out.println("1..."); 189 | } 190 | 191 | public static void fun2() { 192 | System.out.println("2..."); 193 | } 194 | } 195 | ``` 196 | #### 1.修饰代码块 197 | - this,num 锁定的是传入的对象,就看里面有一个还是多个,锁一个会同步,锁多个,不会同步 198 | - Data.class 锁的是类,类只有一个 199 | 200 | ```java 201 | import java.util.concurrent.TimeUnit; 202 | public class Test{ 203 | public static void main(String[] args){ 204 | Data2 data2 = new Data2(); 205 | for(int i = 0 ; i < 5 ; i++){ 206 | new Thread(()->{ 207 | data2.func(); 208 | }).start(); 209 | } 210 | //情景一: 同时输出5个start...,之后输出5个end... 211 | //情景二: start 完之后再 end,依次进行5个。5个线程 公用一个data2对象,而锁的是this 即data2对象。所以等第一个线程data2执行完释放后第二个线程的data2才能运行。 212 | 213 | 214 | for(int i = 0 ; i < 5 ; i++){ 215 | new Thread(()->{ 216 | Data2 data2 = new Data2(); 217 | data2.func(); 218 | }).start(); 219 | } 220 | //情景二: 同时开始,同时结束。因为5个线程用的不同的data2 221 | //情景三: 锁的是类, start 完之后再 end,依次进行5个。 222 | //情景四: num只有一个,所以还是排队的 223 | 224 | Data2 data2 = new Data2(); 225 | for(int i = 0 ; i < 5 ; i++){ 226 | new Thread(()->{ 227 | Integer num = 1; 228 | data2.func(num); 229 | //情景五: num内存中只有一个,所以还是排队的 230 | 231 | Integer num = new Integer(1); 232 | data2.func(num); 233 | //情景五: 不排队,因为new出来的在堆内存中,所以不会排队 234 | }).start(); 235 | } 236 | } 237 | } 238 | class Data2 { 239 | // 情景一 240 | public void func() { 241 | System.out.println("start..."); 242 | TimeUnit.SECONDS.sleep(1); 243 | System.out.println("end..."); 244 | } 245 | 246 | // 情景二 247 | public void func() { 248 | synchronized (this){ // this指的是当前的对象 249 | System.out.println("start..."); 250 | TimeUnit.SECONDS.sleep(1); 251 | System.out.println("end..."); 252 | } 253 | } 254 | 255 | // 情景三 256 | public void func() { 257 | synchronized (Data2.class){ // 锁的是这个类 258 | System.out.println("start..."); 259 | TimeUnit.SECONDS.sleep(1); 260 | System.out.println("end..."); 261 | } 262 | } 263 | 264 | // 情景四 265 | public void func() { 266 | Integer num = 1; 267 | String num = "sss"; //字符串常量池中 只有一个,会排队 268 | String num = new String("sss"); //堆内存中,new出来的,不同线程有不同个,不会排队 269 | synchronized (num){ // 锁的是这个类 270 | System.out.println("start..."); 271 | TimeUnit.SECONDS.sleep(1); 272 | System.out.println("end..."); 273 | } 274 | } 275 | 276 | // 情景五 277 | public void func(Integer num) { 278 | synchronized (num){ // 锁的是这个类 279 | System.out.println("start..."); 280 | TimeUnit.SECONDS.sleep(1); 281 | System.out.println("end..."); 282 | } 283 | } 284 | } 285 | ``` 286 | 287 | 288 | 289 | ### 怎样保证线程安全? 290 | - 使用锁保证线程安全,如sychronized、volite、ReentryLock。 291 | ### volite两个特性是什么? 292 | - volite的两个特性是可见性和禁止指令重排序。可见性是线程在获取volite属性时,需要去主内存刷新数据到线程内存中,写数据要去更新到主内存中。禁止指令重排序最经典的例子是单例的DCL,由于创建对象分为3个步骤,分配内存空间,初始化成员属性和构造方法,将引用指向内存空间,。由于Java是乱序执行,在多线程环境,创建对象的第二步最后执行,会出现对象已经不为空,但是调用使用它的属性去调用方法会崩溃,因为此时还没有初始化成员属性。 293 | ### sychronized和volite有什么区别? 294 | - sychronized仅有获得锁的当前线程访问,其他线程不能访问。volite所有线程都可以访问,需要从主内存读取值。 295 | - sychronized可以作用在类、方法,代码块。volite可以作用在变量。 296 | - sychronized保证原子性,volite不能保证原子性。 297 | - sychronized会造成线程阻塞,volite不会造成线程阻塞。 298 | - synchronized可以被编译器优化,volite不可以被编译器优化。 299 | -------------------------------------------------------------------------------- /java基础/引用类型.md: -------------------------------------------------------------------------------- 1 | ## 引用类型 2 | - java中的引用类型:强软弱虚 3 | 4 | ### java中创建对象的过程 5 | - 1.分配内存空间。2.执行构造器初始化对象。3.将创建的对象指向内存空间 6 | 7 | ### 强引用 8 | - 一般 new 出来的对象 叫强引用。 M m = new M(); 9 | - 只要有强引用存在,被引用的对象就不会被垃圾回收,宁可溢出。 10 | 11 | ### 软引用 12 | - SoftReference m = new SoftReference<>(new byte[1024]) 13 | - m 会指向堆内存中的 SoftReference ,SoftReference 会指向 具体new 出来的的内容 14 | - m.get() 得到软引用包裹的内容。 15 | - 内存空间不够时,被软引用修饰的对象会被回收,即 SoftReference 指向的空间会被回收。 16 | - 适合做缓存 17 | 18 | ### 弱引用 19 | - WeakReference m = new WeakReference<>(new byte[1024]) 20 | - m 会指向堆内存中的 WeakReference ,WeakReference 会指向 具体new 出来的的内容 21 | - m.get() 得到软引用包裹的内容。 22 | - 只要有垃圾会收,二话不说立马被干掉,不管内存空间够不够。 23 | - 应用于 ThreadLocal 里防止内存泄漏 24 | 25 | ### 虚引用 26 | - PhantomReference m = new PhantomReference<>(new byte[1024],QUEUE) 27 | - m 会指向堆内存中的 PhantomReference ,PhantomReference 会指向 具体new 出来的的内容。用于管理直接内存。 28 | - get都get不到,但是对象回收的时候会给一个回收信号。 29 | -------------------------------------------------------------------------------- /java基础/接口和抽象类.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### 抽象类 4 | - 抽象方法用abstract关键字修饰,只有声明没有具体的实现。有抽象方法的类就就是抽象类,抽象类用abstract关键字修饰。 5 | - 子类继承了抽象类必须实现父类的抽象方法,如果没有实现,那么子类也将会是抽象类。 6 | - 抽象类不能被实例化,即不能使用new关键字来实例化对象,只能被继承。 7 | - 抽象类中的抽象方法的修饰符只能为public或者protected和default,默认为public 8 | - 可以继承一个类和实现多个接口 9 | - 可以有构造器 10 | - 可以有main方法,可以运行 11 | 12 | ### 接口 13 | - 接口 interface 定义接口,一个类可以实现多个接口,并在实现类中要把接口中所有的方法均实现。 14 | - 接口可以包含变量、方法;变量被隐士指定为public static final 15 | - 接口中所有的方法不能有实现方法 16 | - 接口支持多继承,即一个接口可以extends多个接口 17 | - 不能有构造器 18 | - 不能有main方法,不能运行 19 | 20 | -------------------------------------------------------------------------------- /java基础/注解.md: -------------------------------------------------------------------------------- 1 | ## 开源框架通用技术-编译时注解 2 | ### 目前App架构,你是怎么做的 3 | - corelib,coreui 之上形成工具组件,之后功能组件 4 | 5 | ### 传统架构模式缺点 6 | - 编译耗时(gradle并发,增量) 7 | - 多人git冲突 8 | - 不易于维护,app没有层次感 9 | 10 | ### Router跳转可以用什么实现,路由结构 11 | - 用Intent实现 12 | - EventBus 13 | - 广播 14 | - 隐式意图 15 | - 类加载Style 16 | - 数据结构Map来保存路径 17 | 18 | ### 组件话解决什么问题? 19 | - 设置模块之间的依赖,且使得业务模块可以单独编译 20 | - 解耦,解决页面之间的依赖关系 21 | - 解决业务模块之间的页面跳转以及通信 22 | 23 | ### 编译时技术 24 | #### 使用APT(Annotation Processing Tool)的框架 25 | - ButterKnife 26 | - Dagger 27 | - EventBus 28 | - ARouter 29 | - Room 30 | - Retrofit 31 | 32 | ### 注解 33 | - Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。 34 | - 注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。 35 | - 我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。 36 | 37 | ![img.png](resouse/java注解.png) 38 | 39 | ### Retention生命周期 40 | - Runtime运行时能获取到注解,它会到JVM,到内存中 41 | - Class类注解,在编译期间能获取 42 | - Source注解,只在代码层面 43 | 44 | ### 如何获取注解 45 | - isAnnotationPresent(AptCreate.class) 拿到boolean类型的值,如果应用到了注解,就为true, 46 | - 然后可以通过GetAnnotation来获取到注解对象,通过注解对象获取注解值 47 | 48 | ### ServiceLoader 与 ClassLoader 49 | - JVM利用ClassLoader将类加载进内存,做java类生命周期的处理:加载,链接,初始化,使用,卸载 50 | - ServiceLoader服务加载。 服务提供者可以通过扩展的形式(插件plugin)安装在java平台的实现中,也就是将jar文件放入任意常用的扩展目标中。也可以通过将提供者加入应用程序类路径,或者通过其他某些特定子平台的方式使其可用。 51 | 52 | ### AutoService主要用于组件化开发 53 | - 当开发的时候,我们只能知道接口而不能知道实现类,所以无法访问,如何解决这个问题? 54 | - 遍历dex文件找到实现类并动态注册(ARouter会自己实现,知道原理)。反射性能的问题 55 | - 修改字节码动态插入(AutoRegister框架) 打包慢 56 | - AutoService+ServiceLoader 反射 57 | 58 | 59 | ### 参考致谢 60 | - https://www.bilibili.com/video/BV1hZ4y1C7Vd?p=3 -------------------------------------------------------------------------------- /java基础/集合/ArrayList与LinkedList.md: -------------------------------------------------------------------------------- 1 | ## ArrayList 与 LinkedList 2 | 3 | ### ArrayList 4 | 5 | - ArrayList 里面维护了一个Object[]数组 6 | 7 | #### 第i个位置插入元素E add(int index,E) 8 | 9 | - rangeCheckForAdd 检查下标是否越界 10 | - elementData 元素个数+1; 11 | - 然后通过 arrayCopy() 方法,把 i 位置之后的元素全部依次往后挪动一个位置; 12 | - 把元素E赋值到第i个位置 13 | 14 | ```java 15 | public static void arraycopy( 16 | Object src, //源数组 17 | int srcPos, //源数组中的起始位置 18 | Object dest, //目标数组 19 | int destPos, //目标数组中的起始位置 20 | int length) //要复制的数组元素数量 21 | 22 | public void add(int index,E element){ 23 | rangeCheckForAdd(index); 24 | modCount++; 25 | final int s; 26 | Object[]elementData; 27 | if((s=size)==(elementData=this.elementData).length) 28 | elementData=grow(); 29 | System.arraycopy(elementData,index,elementData,index+1,s-index); 30 | elementData[index]=element; 31 | size=s+1; 32 | } 33 | ``` 34 | 35 | #### 删除元素remove(int index) 36 | 37 | - 数组个数-1 38 | - 通过arraycopy()将数组从index位置向前移动 39 | 40 | ```java 41 | public E remove(int index){ 42 | Objects.checkIndex(index,size); //检查下标是否越界 43 | final Object[]es=elementData; 44 | @SuppressWarnings("unchecked") 45 | E oldValue=(E)es[index]; 46 | fastRemove(es,index); 47 | return oldValue; 48 | } 49 | ``` 50 | ```java 51 | private void fastRemove(Object[]es,int i){ 52 | modCount++; 53 | final int newSize; 54 | if((newSize=size-1)>i) 55 | System.arraycopy(es,i+1,es,i,newSize-i); 56 | es[size=newSize]=null; 57 | } 58 | ``` 59 | 60 | ### LinkedList 61 | 62 | - 里面维护了一个 Node 双向链表 63 | 64 | ```java 65 | //LinkedList 66 | public class LinkedList 67 | extends AbstractSequentialList 68 | implements List, Deque, Cloneable, java.io.Serializable { 69 | transient int size = 0; 70 | //表头 71 | transient Node first; 72 | //表尾 73 | transient Node last; 74 | //... 75 | } 76 | ``` 77 | 78 | ```java 79 | // 每个结点 80 | private static class Node { 81 | E item; 82 | Node next; //后继 83 | Node prev; //前驱 84 | 85 | Node(Node prev, E element, Node next) { 86 | this.item = element; 87 | this.next = next; 88 | this.prev = prev; 89 | } 90 | } 91 | ``` 92 | 93 | #### add(E) 94 | 95 | ```java 96 | public boolean add(E e){ 97 | linkLast(e); 98 | return true; 99 | } 100 | ``` 101 | 102 | ```java 103 | void linkLast(E e){ 104 | final Node l=last; //找到最后一个位置 105 | final Node newNode=new Node<>(l,e,null); 106 | last=newNode; //将新元素作插入到最后 107 | if(l==null) 108 | first=newNode; //如果链表为空 表头为 newNode 109 | else 110 | l.next=newNode; //否则 将最后一个结点指向newNode 111 | size++; 112 | modCount++; 113 | } 114 | ``` 115 | 116 | ### 一个数组插入删除查找和链表的插入删除的效率对比(腾讯) 117 | - 插入:LinkedList 移动指针,ArrayList要复制移动元素 118 | - 查找:ArrayList 可以随机查找,LinkedList 只能顺序遍历查找 119 | - 删除:LinkedList 移动指针,ArrayList要复制移动元素 120 | 121 | ### 如果一个数组反复插入删除,怎么降低时间复杂度(腾讯) 122 | - 对数组反复插入删除,需要不断移动,会造成内存损耗 123 | - 思路借鉴一下JVM里面标记回收算法 124 | - 先把需要删除的结点标记为null,我不会真正的删除。我只是标记你删除了。如果内存不够了,把所有标记为null删除。 125 | - 如果这时候插入到标记为null时,直接把null修改 126 | - SparseArray 127 | 128 | #### 垃圾回收算法中 标记回收算法 129 | - 通过JVM中 可达性分析标记出不可达的内存块 130 | ![可达性分析](../resouse/可达性分析.png) 131 | - 对不可达的内存快进行回收,但是会产生内存碎片(现在用标记整理) 132 | 133 | #### SparseArray 134 | -------------------------------------------------------------------------------- /java基础/静态类、静态方法、成员内部类、静态内部类、局部内部类、和匿名内部类的理解.md: -------------------------------------------------------------------------------- 1 | ### 静态属性和静态方法是否可以被继承?是否可以被重写 2 | - 可以继承,但不可以被重写,而是被隐藏,如果子类里面定义了静态方法或属性,那么这时候父类的静态方法或属性称之为隐藏 3 | 4 | ### 成员内部类、静态内部类、局部内部类、和匿名内部类的理解 5 | - Java中内部类主要分为成员内部类、局部内部类(嵌套在方法和作用域中)、匿名内部类(无构造方法)、静态内部类(由static修饰的类、不能使用任何外围类的非static成员变量和方法、不依赖外围类),每个内部类都能独立的继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类均无影响,因为Java支持实现多个接口,而不支持多继承,我们可以使用内部类提供的、可以继承多个具体的或抽象的类的能力来解决使用接口难以解决的问题,接口只是解决了部分问题,而内部类使得多继承的解决方案变得更加完整。 -------------------------------------------------------------------------------- /java基础/面向对象.md: -------------------------------------------------------------------------------- 1 | ### 继承 2 | ### 封装 3 | ### 多态 4 | - 前提条件:继承、方法重写、父类引用指向子类对象 5 | - 多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。 6 | - 父类类型 变量名 = new 子类对象 ;变量名.方法名(); 7 | 8 | ### 重载和重写的区别 9 | #### 重载 10 | - 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,也可以相同,发生在编译时。 11 | #### 重写 12 | - 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类(如果父类的方法是基本数据类型或void,子类是需要一致的),抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。 -------------------------------------------------------------------------------- /kotlin/kt进阶.md: -------------------------------------------------------------------------------- 1 | ### 空检查机制 2 | ```kotlin 3 | class BaseFun{ 4 | var name:String 5 | } 6 | fun main(args:Array){ 7 | // TODO: 2022/6/10 空检查机制 8 | var name:String = null 9 | } 10 | ``` -------------------------------------------------------------------------------- /kotlin/协程.md: -------------------------------------------------------------------------------- 1 | https://www.bilibili.com/video/BV1Yu411X73j?spm_id_from=333.337.search-card.all.click&vd_source=a77b2d1e92c4bda82707549b246c89fe -------------------------------------------------------------------------------- /算法/resource/最大子数组.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/算法/resource/最大子数组.png -------------------------------------------------------------------------------- /算法/算法.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### 1.求最大子数组 4 | ![img.png](resource/最大子数组.png) 5 | * 动态规划算法 6 | * [代码戳这里](https://github.com/chsring/MergeSort/blob/master/src/MaxArray.java) 7 | * 状态定义:dp[i]表示以nums[i]结尾的 连续子数组的最大和 8 | * 如果 dp[i-1] > 0 , dp[i] = dp[i-1] + nums[i] 9 | * 如果 dp[i-1] <= 0 , dp[i] = nums[i] 10 | * 初始化 dp[0] = nums[0] 11 | * 最后输出max(dp) 12 | * 时间复杂度 O(N),空间复杂度 O(N) 13 | ```java 14 | public int maxSubArray(int[]nums){ 15 | int len = nums.length; 16 | if(len == 0){ 17 | return 0; 18 | } 19 | // dp[i]:以nums[i]结尾的 和最大的 连续子数组的和 20 | int []dp = new int[len]; 21 | dp[0]=nums[0]; 22 | int res = nums[0]; 23 | for(int i=0;inext = L->next; 39 | L->next = p; 40 | ``` 41 | ```java 42 | //链表逆置 43 | 44 | ``` 45 | ### 3.顺序表逆置 46 | ```java 47 | 48 | ``` 49 | ### 4.判断链表是否有环 50 | 51 | 52 | ### 5.求链表倒数第n个元素 53 | 54 | 55 | ### 合并k个排序链表 56 | 57 | 58 | - https://space.bilibili.com/390775036/channel/seriesdetail?sid=1868869 59 | ### 验证二叉搜索树 60 | ### 二叉树中最大路径 61 | ### 最小二叉搜索树 62 | ### 二叉树最近的公共祖先 63 | 64 | 65 | ### 最长回文串 66 | ### String to interger 67 | 68 | 69 | 70 | 71 | ### 参考致谢 72 | - https://blog.csdn.net/kongmin_123/article/details/82430985 -------------------------------------------------------------------------------- /网络/HTTP协议.md: -------------------------------------------------------------------------------- 1 | ### HTTP发展 2 | - 0.9 他只允许发送GET请求 3 | - 1.0 增加了HEAD,POST等新方法,引入了HTTP Header(头部的概念),让HTTP请求和响应更加灵活,发一次请求后就断开 4 | - 1.1 增加了PUT,DELETE等新方法,明确了连接管理,允许持久连接,允许响应数据分块,利于传输大文件 5 | - 2.0 OKHttp(底部封装了socket)支持2.0协议。二进制协议,不再是纯文本,可发起多个请求,废弃了1.1里面的管道,使用专有算,法压缩头部,减少数据传输量,允许服务器主动向客户端推送数据,增强了安全性,"事实上"要求加密通信 6 | 7 | ### HTTP报文格式 8 | ![img.png](resource/报文格式.png) 9 | - 请求头实际上是 key-value 的格式;请求头有多个(Host,Content-Length,Content-Type 等) 10 | - POST请求还有请求体,参数都在请求体里面。 11 | 12 | #### 响应体中如果没有Content-Length,那么怎么知道这个响应体有多长,怎么知道这个响应体解析完了 13 | - 在请求头中,有个 Transfer-Encoding:chunked,表示该响应体采取的分块编码,当数据量比较大的时候就会采取分块编码。每一行数据读取完后都会加/r/n 14 | - 最后一个数据解析完会0/r/n,表示后面有没有数据要解析。 15 | - OKHttp中解析方式分两种,如果响应头中包含了 Transfer-Encoding,就按照数据块的方式去解析。如果包含了 Content-Length,就可以直接获取数据长度。 16 | ![img_1.png](resource/OKHttp.png) 17 | 18 | ### HTTPS与SSL 19 | - HTTP在传输数据的时候全都是明文,无法保证数据的安全性。 20 | - HTTPS 运行在TLS协议上的安全的HTTP协议,TLS是SSL的升级版。当客户端发送http请求时,先对http请求内容在SSL层进行加密,然后通过TCP/IP传输,Client收到http请求后,先通过SSL层解密,就变成了HTTP抱文数据 21 | - 1.客户端要向服务器发送信息,客户端和服务器都要产生一对用于加密和解密的公钥和私钥。 22 | - 2.客户端的私钥保密,客户端的公钥告诉服务器;服务器的私钥保密,服务器的公钥告诉客户端。 23 | - 3.客户端 用对称加密报文,并产生一个密钥A,用服务器发送的公钥 使用非对称加密 A 后,与报文一起发送给服务器。 24 | - 4.服务器收到这个消息后,服务器用自己的私钥解密客户端的消息。 25 | 26 | ### 非对称加密 27 | - 如果用公钥对数据进行加密,只有用对应的私钥才能解密。 28 | - 优点:安全性较高,比对称密钥安全性高很多。 29 | - 缺点:由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。 30 | - 常见非对称加密算法RSA,DSA 31 | 32 | ### 对称加密 33 | - 加密和解密使用相同密钥的加密算法。 34 | - 优点:算法公开、计算量小、加密速度快、加密效率高。 35 | - 缺点:密钥管理的安全性很低,因为加密和解密都使用同一个密钥,在密钥的发送过程中,密钥可能被第三方截取 36 | - 常见对称加密算法DES,AES 37 | 38 | ### HTTP加密过程:SSL握手 39 | - SSL握手的过程是明文的,握手之后才是加密的,因此SSL握手的重点在于-在不安全的网络中进行密钥的交换 40 | - 在握手阶段使用非对称加密确定握手的密钥, 41 | 42 | ### RSA交换密钥,一般情况下会使用 ECDH+RSA 算法交换密钥,保证在不安全的网络中交换密钥 43 | - 1.客户端给出协议版本号、一个随机数R1、以及客户端支持的加密方法 44 | - 2.服务端确认使用的加密方法,给出数字证书,以及随机数R2 45 | - 3.客户端确认数字证书有效,生成一个新的随机数 ps,并使用数字证书的 公钥,利用RSA加密这个随机数,发给服务端。 46 | - 4.客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成对话密钥AES密钥,用来对称加密对话整个过程。 47 | ![img.png](resource/SSL加密过程.png) 48 | 49 | ### 数字证书 50 | - 证书相当于身份证,由CA机构颁发 51 | - 验证某个网站,还可以用来验证某个文件是否被篡改。 52 | - 有了证书之后,当你的浏览器在访问某个 HTTPS 网站时,会验证该站点上的 CA 证书。如果浏览器发现该证书没有问题,那么页面就直接打开;否则的话,浏览器会给出一个警告,告诉你该网站的证书存在某某问 53 | 54 | ### cookie 55 | - cookie的执行原理:就是当客户端访问服务器的时候(服务器运用了cookie),服务器会生成一份cookie传输给客户端,客户端会自动把cookie保存起来,以后客户端每次访问服务器,都会自动的携带着这份cookie。 56 | 57 | ### session 58 | - 当客户端第一次请求服务器的时候,服务器生成一份session保存在服务端,将该数据(session)的id以cookie的形式传递给客户端;以后的每次请求,浏览器都会自动的携带cookie来访问服务器(session数据id)。 59 | 60 | ### cookie和session共同点 61 | - cookie和session都是用来跟踪浏览器用户身份的会话方式。 62 | 63 | ### cookie和session区别 64 | - cookie是保存在客户端的,cookie有大小限制, 65 | - session是保存在服务器端,session更加安全,session会比较占用服务器性能,当访问增多时应用cookie 66 | 67 | 68 | ### 参考致谢 69 | - https://www.bilibili.com/video/BV1WV411t7pY?p=3 -------------------------------------------------------------------------------- /网络/TCP协议.md: -------------------------------------------------------------------------------- 1 | ### OSI七层模型 2 | ![img.png](resource/七层模型.png) 3 | - 物理层:用于建立、维护和拆除物理链路连接。数据的单位是比特,主要设备有中继器、集线器、适配器。 4 | - 链路层:在物理层基础上,建立相邻结点之间的数据链路,提供数据帧在信道上无差错的传输,本层在不可靠的物理介质上提供可靠的传输,其作用有物理地址寻址、数据的成帧、流量控制、数据的检验、重发等,本层的单位为帧,主要设备是二层交换机、网桥。 5 | - 网络层:其就是选择合适的网间路由和交换结点,确保数据及时传达。网络层将数据链路层提供的帧组成数据包,其中含有逻辑地址信息(源站点和目的站点地址的网络地址)。IP是第三层的一部分,此外还有一些路由协议和地址解析协议。地址解析和路由是第三层的重要目的。本层的单位是数据包,典型的协议包括IP,IPX,RIP,ARP,RARP,OSPF等。主要设备是路由器。 6 | - 传输层:为上层提供端到端(最终用户到最终用户) 的透明的、可靠的数据传输服务。具体的协议包括有TCP,UDP.单位是数据段。 7 | - 会话层:在会话层及以上的层次,数据传送单元统称为报文。它提供包括访问验证和会话管理在内的建立和维护应用之间的通信机制。如服务器验证用户登录便是在会话层完成的。 8 | - 表示层:解决用户信息的语法表示问题。提供格式化的表示和转换数据服务。数据的压缩和解压缩,加密和解密等工作都是由表示层负责的。 9 | - 应用层:为操作系统或网络应用程序提供访问网络服务的接口。这层的协议包括有IPSP,FTP,HTTP,SNMP等。 10 | 11 | ### socket 12 | - Socket是对TCP/IP协议的封装,是⼀个调⽤接⼝API。在TCP/IP协议中主要有Socket类型的流套接字StreamSocket和数据报套接 13 | 字DatagramSocket。 14 | - 流套接字将TCP作为其端到端的协议,提供⼀个可信赖的字节流服务。 15 | - 数据报套接字使⽤UDP协议,提供数据打包发送服务。 16 | ![img.png](resource/socket.png) 17 | 18 | ### TCP 19 | - TCP 是一种面向有连接的传输层协议,能够对自己提供的连接实施控制。适用于要求可靠传输的应用,例如文件传输。面向字节流,传输慢 20 | 21 | ### UDP 22 | - UDP 是一种面向无连接的传输层协议,不会对自己提供的连接实施控制。适用于实时应用,例如:IP电话、视频会议、直播等,以报文的方式传输,效率高 23 | 24 | ### TCP和UDP区别 25 | ![img.png](resource/TCP和UDP.png) 26 | 27 | #### TCP 三次握手 28 | ![img.png](resource/三次握手.png) 29 | - 第一次:Client将同步标志位 SYN (同步包)置为1,设置请求序号 seq 假设为 x,将该数据包发送给Server,Client进入 SYN_SENT 状态,等待Server确认。 30 | - 第二次:Server收到数据包,同步标志位 SYN=1 ,确认标志位 ACK 置为1,确认序号 ack=x+1,seq=y(自己的序号)发送给Client以确认连接请求,Server进入SYN_RCVD状态。 31 | - 第三次:Client 收到后,ACK=1,确认号 ack=y+1 ,自己的序号 seq=x+1 发送给Server,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。 32 | 33 | #### TCP 四次挥手 34 | ![img.png](resource/四次挥手.png) 35 | - 第一次:Client发送一个 FIN=1 (finish包),以及选择号 seq=u,用来关闭Client到Server的数据传送。Client进入FIN_WAIT_1状态。 36 | - 第二次:Server收到FIN后,发送一个 ACK=1,一个请求号 seq=v 和确认序号 ack=u+1 给Client。Server进入CLOSE_WAIT状态,Client收到后,进入FIN_WAIT_2状态。 37 | - 注:第二次之后,服务器可能还有数据要往客户端发送,所以第二次只是一发一个ACK确认包。 38 | - 第三次:Server发送一个FIN,请求号为最新的 seq=w 和确认序号 ack=u+1,用来关闭Server到Client的数据传送。Server进入LAST_ACK状态。 39 | - 第四次:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个 ACK=1 给Server,序号为 seq=u+1,确认序号为 ack=w+1。Server进入CLOSED状态,完成四次挥手。 40 | 41 | #### 为什么建立链接要三次握手? 42 | - 第一次保证Server发送能力没问题,第二次保证Server接受能力没问题,发送能力没问题;第三次保证Client接受能没问题 43 | 44 | ### 参考致谢 45 | - https://blog.csdn.net/qq_44647809/article/details/115143100 46 | - https://blog.csdn.net/MHSMIE/article/details/50992971 47 | - https://www.bilibili.com/video/BV1WV411t7pY?spm_id_from=333.999.0.0 -------------------------------------------------------------------------------- /网络/get和post.md: -------------------------------------------------------------------------------- 1 | - POST和GET都是HTTP请求的基本方法 2 | ### 主要区别 3 | #### 功能不同 4 | - get是从服务器上获取数据。 5 | - post是向服务器传送数据。 6 | #### 过程不同 7 | - get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。 8 | - post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。 9 | #### 获取值不同 10 | - 对于get方式,服务器端用Request.QueryString获取变量的值。 11 | - 对于post方式,服务器端用Request.Form获取提交的数据。 12 | #### 传送数据量不同 13 | - get传送的数据量较小,不能大于2KB。 14 | - post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。 15 | #### 安全性不同 16 | - get安全性非常低 17 | - post安全性较高 18 | 19 | ### URL解析过程 20 | #### 1、用户输入网址,浏览器发起DNS查询请求 21 | - 用户访问网页,DNS服务器(域名解析系统)会根据用户提供的域名查找对应的IP地址 22 | #### 2、建立TCP连接 23 | - 浏览器通过DNS查询到web服务器的真实IP地址后,便向服务器发起TCP连接请求,通过TCP的三次握手建立连接后,浏览器便可以将数据通过http请求发送给服务器了 24 | #### 3、浏览器向web服务器发送一个http请求 25 | - http请求是基于TCP协议之上的应用层协议--超文本传输协议。 26 | - http请求包括: 27 | - 请求行:请求方法 url 协议/版本 28 | - 请求头:数据以键值对的形式存放,其中包括Host、Connection、Accept、Accept-Encoding等 29 | - 请求空行 30 | - 请求体:一般包含请求的数据 31 | #### 4、发送响应数据给客户端 32 | - web服务器通过监听80端口来获取客户端的http请求。 33 | - 与客户端建立连接后,web服务器开始接受客户端发来的数据,并通过http解码,从接收到的网络数据中解析出请求的url信息,如Accept-Encoding等。web服务器将根据http请求的信息,响应相应的数据给客户端,典型的http响应数据如下: 34 | - 状态行:协议/版本 状态码 状态描述 35 | - 响应头:数据以键值对的形式存放,其中包括Location、Server、Content-Type、Content-Length等 36 | - 响应空行 37 | - 响应体 38 | - 至此,一个HTTP通信完成。服务器会根据http请求头中的Connection字段值决定是否关闭TCP连接通道,若其值为keep-alive时,web服务器不会立即关闭连接 39 | #### 5、浏览器解析http响应 40 | - html文档解析(DOM Tree) 41 | - 浏览器发送获取嵌入到html中的对象 42 | - css解析 、js解析 43 | -------------------------------------------------------------------------------- /网络/resource/OKHttp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/OKHttp.png -------------------------------------------------------------------------------- /网络/resource/SSL加密过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/SSL加密过程.png -------------------------------------------------------------------------------- /网络/resource/TCP和UDP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/TCP和UDP.png -------------------------------------------------------------------------------- /网络/resource/socket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/socket.png -------------------------------------------------------------------------------- /网络/resource/七层模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/七层模型.png -------------------------------------------------------------------------------- /网络/resource/三次握手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/三次握手.png -------------------------------------------------------------------------------- /网络/resource/四次挥手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/四次挥手.png -------------------------------------------------------------------------------- /网络/resource/报文格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chsring/interview/112cb887185b07e0ceee45e9e94d0641465f1cb0/网络/resource/报文格式.png --------------------------------------------------------------------------------