├── Android ├── 五.ⅳ、电量优化.md ├── 九、经典案例.md ├── 四.ⅳ、Android四大组件.md ├── PackageManagerService.md ├── 五.ⅲ、网络优化.md ├── 四.ⅲ、Android系统启动流程.md ├── 六、常用技术.md ├── 八、架构设计.md ├── ActivityManagerService.md ├── 四.ⅴ、Android图形界面.md ├── 四.ⅲ、Android系统整体流程.md ├── 一、Android基础.md ├── 五.ⅴ、其它.md ├── 四.ⅵ、other.md ├── 四.ⅱ、资源管理框架.md ├── 五.ⅰ、渲染优化.md ├── 四.ⅰ、Binder.md ├── 五.ⅱ、内存优化.md ├── 二、四大组件相关.md ├── 三、View剖析.md └── 七、常用框架.md ├── note ├── Linux操作系统 │ ├── 系统初始化.md │ └── 目录.md ├── Gradle实战 │ ├── Gradle实战第五章.md │ ├── 《Gradle实战》.md │ └── Gradle实战第四章.md ├── Web性能权威指南 │ └── Web性能权威指南.md └── 计算机网络 │ ├── 《计算机网络》(第7版 谢希仁).md │ ├── 计算机网络第六章.md │ ├── 计算机网络第一章.md │ ├── 计算机网络第四章.md │ └── 计算机网络第五章.md ├── 练习题.docx ├── image ├── Main.jpg ├── dpi.png ├── wqiruy.png ├── 类加载过程.webp ├── qewrtsagb.png ├── 1543826555.png ├── 1543831272.png ├── weurudsoifj.png ├── 1536215413(1).png ├── 1537194464(1).png ├── 1537194978(1).png ├── 1537195015(1).png ├── 1537195039(1).png ├── 1539659587(1).png ├── 1540114802(1).png ├── 1541426783(1).png ├── 1541818566(1).png ├── 1541818620(1).png ├── 1544338055(1).png ├── 20181206225009.png ├── 4564789168761.jpg ├── android-boot.jpg ├── handler_java.jpg ├── 061046391107893.jpg ├── 1354020417_5176.jpg ├── 1364831271_1701.jpg ├── 16367d673b0e268d.jpg ├── 1645b73891f5ff8c.png ├── 1645b738c9d09633.png ├── 1645b738e643cfc6.png ├── 2018040923163567.jpg ├── 5454871315648648.jpg ├── android-booting.jpg ├── b2cc0c785131b7b7.png ├── sdgwerjktrreyjk.png ├── 20160220005120410.png ├── 20160808154319878.png ├── 20170311114110621.jpg ├── 20170406230435886.jpg ├── 20180325101500413.jpg ├── 20180325121814300.jpg ├── 20180401225108270.jpg ├── 20180408221356219.jpg ├── 20180409230329278.jpg ├── 20180412224905920.jpg ├── 20180430170416746.jpg ├── 20180430191550814.jpg ├── activity_lifecycle.png ├── oqwjoifoqmgowigwq.png ├── 84734854472343465496.png ├── db150002205d9bc30b8f.jpg ├── 2243690-9cd9c896e0d512ed.jpg ├── 3985563-6fdf680296f3acd3.png ├── 3985563-cd5a153e44696643.png ├── 3985563-d7da4f5ba49f6887.png ├── 8855bb645d8ecc35c80aa89cde5d16e5.jpg ├── bf0bcbea6a24bc5084bc0d4ffca7c502.jpeg ├── ffb6847b94cb0fd086095ac263ac4ff0.jpg └── v2-a821a76fb84ce6223598c89ae8ebe7b0_hd.jpg ├── other ├── 数据结构与算法.md ├── JNI.md ├── kotlin │ └── 泛型.md ├── Gradle.md ├── kotlin.md ├── 计算机网络.md └── 设计模式.md ├── Java ├── 六、other.md ├── 三、IO-NIO.md ├── 二、Java集合框架.md ├── 一、Java基础.md └── 四、Java并发(Concurrent).md ├── README.md └── 常用框架.md /Android/五.ⅳ、电量优化.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /note/Linux操作系统/系统初始化.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /note/Gradle实战/Gradle实战第五章.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /note/Web性能权威指南/Web性能权威指南.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /练习题.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/练习题.docx -------------------------------------------------------------------------------- /image/Main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/Main.jpg -------------------------------------------------------------------------------- /image/dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/dpi.png -------------------------------------------------------------------------------- /image/wqiruy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/wqiruy.png -------------------------------------------------------------------------------- /image/类加载过程.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/类加载过程.webp -------------------------------------------------------------------------------- /image/qewrtsagb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/qewrtsagb.png -------------------------------------------------------------------------------- /image/1543826555.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1543826555.png -------------------------------------------------------------------------------- /image/1543831272.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1543831272.png -------------------------------------------------------------------------------- /image/weurudsoifj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/weurudsoifj.png -------------------------------------------------------------------------------- /other/数据结构与算法.md: -------------------------------------------------------------------------------- 1 | 1、数学归纳法。 2 | 3 | 1. 证明n=1时,结论成立; 4 | 2. 若n=k是结论成立,证明了n=k+1也成立; 5 | 则n为全体自然数时,结论成立。 6 | -------------------------------------------------------------------------------- /Android/九、经典案例.md: -------------------------------------------------------------------------------- 1 | #### [1、RecyclerView图片加载错位和闪烁问题。](http://www.codexiu.cn/android/blog/24953/) 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /image/1536215413(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1536215413(1).png -------------------------------------------------------------------------------- /image/1537194464(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1537194464(1).png -------------------------------------------------------------------------------- /image/1537194978(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1537194978(1).png -------------------------------------------------------------------------------- /image/1537195015(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1537195015(1).png -------------------------------------------------------------------------------- /image/1537195039(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1537195039(1).png -------------------------------------------------------------------------------- /image/1539659587(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1539659587(1).png -------------------------------------------------------------------------------- /image/1540114802(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1540114802(1).png -------------------------------------------------------------------------------- /image/1541426783(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1541426783(1).png -------------------------------------------------------------------------------- /image/1541818566(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1541818566(1).png -------------------------------------------------------------------------------- /image/1541818620(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1541818620(1).png -------------------------------------------------------------------------------- /image/1544338055(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1544338055(1).png -------------------------------------------------------------------------------- /image/20181206225009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20181206225009.png -------------------------------------------------------------------------------- /image/4564789168761.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/4564789168761.jpg -------------------------------------------------------------------------------- /image/android-boot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/android-boot.jpg -------------------------------------------------------------------------------- /image/handler_java.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/handler_java.jpg -------------------------------------------------------------------------------- /image/061046391107893.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/061046391107893.jpg -------------------------------------------------------------------------------- /image/1354020417_5176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1354020417_5176.jpg -------------------------------------------------------------------------------- /image/1364831271_1701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1364831271_1701.jpg -------------------------------------------------------------------------------- /image/16367d673b0e268d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/16367d673b0e268d.jpg -------------------------------------------------------------------------------- /image/1645b73891f5ff8c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1645b73891f5ff8c.png -------------------------------------------------------------------------------- /image/1645b738c9d09633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1645b738c9d09633.png -------------------------------------------------------------------------------- /image/1645b738e643cfc6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/1645b738e643cfc6.png -------------------------------------------------------------------------------- /image/2018040923163567.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/2018040923163567.jpg -------------------------------------------------------------------------------- /image/5454871315648648.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/5454871315648648.jpg -------------------------------------------------------------------------------- /image/android-booting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/android-booting.jpg -------------------------------------------------------------------------------- /image/b2cc0c785131b7b7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/b2cc0c785131b7b7.png -------------------------------------------------------------------------------- /image/sdgwerjktrreyjk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/sdgwerjktrreyjk.png -------------------------------------------------------------------------------- /image/20160220005120410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20160220005120410.png -------------------------------------------------------------------------------- /image/20160808154319878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20160808154319878.png -------------------------------------------------------------------------------- /image/20170311114110621.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20170311114110621.jpg -------------------------------------------------------------------------------- /image/20170406230435886.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20170406230435886.jpg -------------------------------------------------------------------------------- /image/20180325101500413.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180325101500413.jpg -------------------------------------------------------------------------------- /image/20180325121814300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180325121814300.jpg -------------------------------------------------------------------------------- /image/20180401225108270.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180401225108270.jpg -------------------------------------------------------------------------------- /image/20180408221356219.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180408221356219.jpg -------------------------------------------------------------------------------- /image/20180409230329278.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180409230329278.jpg -------------------------------------------------------------------------------- /image/20180412224905920.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180412224905920.jpg -------------------------------------------------------------------------------- /image/20180430170416746.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180430170416746.jpg -------------------------------------------------------------------------------- /image/20180430191550814.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/20180430191550814.jpg -------------------------------------------------------------------------------- /image/activity_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/activity_lifecycle.png -------------------------------------------------------------------------------- /image/oqwjoifoqmgowigwq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/oqwjoifoqmgowigwq.png -------------------------------------------------------------------------------- /image/84734854472343465496.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/84734854472343465496.png -------------------------------------------------------------------------------- /image/db150002205d9bc30b8f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/db150002205d9bc30b8f.jpg -------------------------------------------------------------------------------- /other/JNI.md: -------------------------------------------------------------------------------- 1 | #### 1、JNIEnv类型和jobject类型。 2 | 3 | - JNIEnv:代表了Java环境 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /image/2243690-9cd9c896e0d512ed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/2243690-9cd9c896e0d512ed.jpg -------------------------------------------------------------------------------- /image/3985563-6fdf680296f3acd3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/3985563-6fdf680296f3acd3.png -------------------------------------------------------------------------------- /image/3985563-cd5a153e44696643.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/3985563-cd5a153e44696643.png -------------------------------------------------------------------------------- /image/3985563-d7da4f5ba49f6887.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/3985563-d7da4f5ba49f6887.png -------------------------------------------------------------------------------- /image/8855bb645d8ecc35c80aa89cde5d16e5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/8855bb645d8ecc35c80aa89cde5d16e5.jpg -------------------------------------------------------------------------------- /image/bf0bcbea6a24bc5084bc0d4ffca7c502.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/bf0bcbea6a24bc5084bc0d4ffca7c502.jpeg -------------------------------------------------------------------------------- /image/ffb6847b94cb0fd086095ac263ac4ff0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/ffb6847b94cb0fd086095ac263ac4ff0.jpg -------------------------------------------------------------------------------- /image/v2-a821a76fb84ce6223598c89ae8ebe7b0_hd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-eugene/Android-Interview/HEAD/image/v2-a821a76fb84ce6223598c89ae8ebe7b0_hd.jpg -------------------------------------------------------------------------------- /note/Gradle实战/《Gradle实战》.md: -------------------------------------------------------------------------------- 1 | 2 | [Gradle实战第四章](https://github.com/chen-eugene/Interview/blob/master/note/Gradle%E5%AE%9E%E6%88%98/Gradle%E5%AE%9E%E6%88%98%E7%AC%AC%E5%9B%9B%E7%AB%A0.md) 3 | 4 | 5 | -------------------------------------------------------------------------------- /Android/四.ⅳ、Android四大组件.md: -------------------------------------------------------------------------------- 1 | 2 | #### [1、谈谈对Context的理解。](https://duanqz.github.io/2017-12-25-Android-Context) 3 | 4 | #### [2、Android中的token到底是什么?](https://blog.csdn.net/u012702547/article/details/53179957) 5 | 6 | #### 3、Activity启动过程分析。 7 | 8 | 9 | #### 4、[startService启动过程分析。](http://gityuan.com/2016/03/06/start-service/) 10 | 11 | 12 | #### 5、bindService过程分析。 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Java/六、other.md: -------------------------------------------------------------------------------- 1 | #### 1、LruChace实现原理。 2 | 3 | 主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。 4 | 5 | LinkedHashMap是由数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的对按照一定顺序排列起来。 6 | 7 | 其中accessOrder设置为true则为访问顺序,为false,则为插入顺序。 8 | 9 | **由此可见LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。** 10 | -------------------------------------------------------------------------------- /note/Linux操作系统/目录.md: -------------------------------------------------------------------------------- 1 | #### [系统初始化](https://github.com/chen-eugene/Android-Interview/blob/master/note/Linux%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96.md) 2 |
3 | 4 | ![常用命令](https://github.com/chen-eugene/Android-Interview/blob/master/image/8855bb645d8ecc35c80aa89cde5d16e5.jpg) 5 | 6 | ![Linux调用](https://github.com/chen-eugene/Android-Interview/blob/master/image/ffb6847b94cb0fd086095ac263ac4ff0.jpg) 7 | 8 | ![Linux架构](https://github.com/chen-eugene/Android-Interview/blob/master/image/bf0bcbea6a24bc5084bc0d4ffca7c502.jpeg) 9 | -------------------------------------------------------------------------------- /note/计算机网络/《计算机网络》(第7版 谢希仁).md: -------------------------------------------------------------------------------- 1 | [第一章:概述](https://github.com/chen-eugene/Interview/blob/master/note/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%AC%E4%B8%80%E7%AB%A0.md) 2 | 3 | [第四章:网络层](https://github.com/chen-eugene/Interview/blob/master/note/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%AC%E5%9B%9B%E7%AB%A0.md) 4 | 5 | [第五章:运输层](https://github.com/chen-eugene/Interview/blob/master/note/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%AC%E4%BA%94%E7%AB%A0.md) 6 | 7 | [第六章:应用层](https://github.com/chen-eugene/Interview/blob/master/note/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%AC%E5%85%AD%E7%AB%A0.md) 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /other/kotlin/泛型.md: -------------------------------------------------------------------------------- 1 | #### [1、理解形参和实参](https://juejin.im/post/5bc5f41f6fb9a05d1a1305e1) 2 | 3 |   **定义在外面的就是实参;定义在里面的就是形参。**(形参和实参是一个相对概念,与作用域有关;这里的外面不单单指函数的参数列表,还指泛型的定义) 4 | 5 | - 形参未被调用时,不占内存单元。形参只有在调用过程中才会占内存单元。在调用结束时, 即刻释放所分配的内存单元。 6 | - 实参(常量、变量、表达式):要有确定的值,占用存储单元,在调用过程中,实参将赋值给形参。 7 | - 数据的流向都是从实参流向形参,并且是单向的 8 | 9 | - 函数中的形参与实参: 10 | ``` 11 | //number:形参,作用域为函数内部,除了函数就不能使用 12 | fun square(number: Int): Int { 13 | return number * number 14 | } 15 | 16 | //radius、area都是实参,定义在square函数的外面, 17 | val radius = 5 18 | val area = Math.PI * square(radius) 19 | 20 | ``` 21 | 22 | - 泛型中的形参与实参 23 | ``` 24 | //T是一个类型形参 25 | class Box(var item:T) 26 | 27 | //String就是一个类型实参 28 | val box = Box("hello") 29 | 30 | //List 中的T是一个类型实参,这里的T是List使用的类型,定义在List类外边 31 | //ArrayList 中的T是一个类型形参 32 | class ArrayList: List{ 33 | ... 34 | } 35 | 36 | ``` 37 |   **在Kotlin的泛型中有这样规定: 如果一个类继承泛型类(或者实现了泛型接口),就必须为基础类型的泛型形参指定一个泛型实参。它可以是具体的类型或者另一个类型形参** 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Java/三、IO-NIO.md: -------------------------------------------------------------------------------- 1 | ![Java IO](https://github.com/chen-eugene/Android-Interview/blob/master/image/16367d673b0e268d.jpg) 2 | 3 | #### 1、什么是IO流? 4 | 5 | 它是一种数据的流从源头流到目的地。比如文件拷贝,输入流和输出流都包括了。输入流从文件中读取数据存储到进程(process)中,输出流从进程中读取数据然后写入到目标文件。 6 | 7 | 8 | #### [2、说说RandomAccessFile?](https://blog.csdn.net/qq496013218/article/details/69397380) 9 | 10 | 它在java.io包中是一个特殊的类,既不是输入流也不是输出流,可以随机访问文件(包括读/写)。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。 11 | 12 | - "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 13 | - "rw" 打开以便读取和写入。 14 | - "rws" 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。 15 | - "rwd" 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。 16 | 17 | **"rw", "rws", "rwd" 的区别。** 18 | - 当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),"rws" 或 "rwd", "rw" 才有区别。 19 | - 当模式是 "rws" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。 20 | - 当模式是 "rwd" 并且操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。 21 | - 当模式是 "rw" 并且操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。 22 | -------------------------------------------------------------------------------- /Android/PackageManagerService.md: -------------------------------------------------------------------------------- 1 | #### [1、apk的安装流程。](https://blog.csdn.net/luoshengyang/article/details/6766010) 2 | 3 | Android系统在启动的过程中,会启动一个应用程序管理服务PackageManagerService,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,即以Apk为后缀的文件,然后对这些文件进解析,得到应用程序的相关信息,完成应用程序的安装过程. 4 | 5 | 应用程序管理服务PackageManagerService安装应用程序的过程,其实就是解析析应用程序配置文件AndroidManifest.xml的过程,并从里面得到得到应用程序的相关信息,例如得到应用程序的组件Activity、Service、Broadcast Receiver和Content Provider等信息,有了这些信息后,通过ActivityManagerService这个服务,我们就可以在系统中正常地使用这些应用程序了。 6 | 7 | - ① SystemServer组件是由Zygote进程负责启动的,启动的时候就会调用它的main函数,系统服务的启动都是从main函数开始的。 8 | - ② 在这个调用链中会去创建一个ServerThread线程,PackageManagerService服务就是在这个线程中启动的。 9 | - ③ 在ServerThread的run方法中会去调用PackageManagerService的main函数,创建了一个PackageManagerService实例,并把它添加到ServiceManager中去,ServiceManager是Android系统Binder进程间通信机制的守护进程,负责管理系统中的Binder对象。 10 | - ④ PackageManagerService会去扫描移动设备上的五个目录(/system/framework、/system/app、/vendor/app、/data/app、/data/app-private)中的Apk文件,对每一个Apk文件进行解析和安装,并且把解析后的应用程序信息保存在PackageManagerService中。 11 | - ⑤ 这样就把前面解析应用程序得到的package、provider、service、receiver和activity等信息保存在了PackageManagerService服务中了。但是,这些应用程序只是相当于在PackageManagerService服务注册好了,如果我们想要在Android桌面上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Android/五.ⅲ、网络优化.md: -------------------------------------------------------------------------------- 1 | 2 | #### [1、网络优化的方向](https://time.geekbang.org/column/article/78585#previewimg) 3 | 4 | - 速度:在网络正常或者良好的时候,怎样更好地利用带宽,进一步提升网络请求速度。 5 | - 弱网络:移动端网络复杂多变,在出现网络连接不稳定的时候,怎么最大程度保证网络的连通性。 6 | - 安全:网络安全不容忽视,怎么有效防止被第三方劫持、窃听甚至篡改。 7 | 8 | 9 | #### [2、网络优化的方案](https://www.jianshu.com/p/d5a843cb7ab1) 10 | 11 | - ① 使用IP直连与HttpDNS 12 | 运营商LocalDNS存在的问题: 13 | - DNS解析默认使用运营商的LocalDNS服务,这块耗时在 3G 网络下可能是 200~300ms,4G 网络也需要 100ms。 14 | - 稳定性:UDP 协议,无状态,容易域名劫持(难复现、难定位、难解决),每天至少几百万个域名被劫持,一年至少十次大规模事件。 15 | - 准确性:LocalDNS 调度经常出现不准确,比如北京的用户调度到广东IP,移动的运营商调度到电信的IP,跨运营商调度会导致访问慢,甚至访问不了。 16 | - 及时性:运营商可能会修改DNS的TTL,导致DNS修改生效延迟。不同运营商的服务实现不一致,我们也很难保证DNS解析的耗时。 17 | 18 | - ② 连接复用(参考OkHttp连接复用): 19 | 创建连接要经过TCP三次握手、TLS密钥协商,建立连接的代价是非常大的。这里我们主要的优化思路是复用连接,这样就不用每次请求都重复建立连接。 20 | 21 | 利用Http协议的keep-alive机制:当一个Http请求的数据传输结束后,TCP连接不立即释放,如果此时有新的Http请求,且请求的Host同上次的请求相同,则可以直接复用未释放的TCP连接,从而省去了TCP的释放和再次创建的开销,减小了网络延时。 22 | 23 | - ③ 压缩 24 | - 使用gzip压缩算法,Http协议上的gzip编码是一种用来改进web应用程序性能的技术,减少传输数据量大小。 25 | - 图片使用webp压缩格式,图片使用缩略图预览。 26 | - 大文件使用断点传输的方式。 27 | 28 | - ② 文件传输 29 | - 使用缩略图。 30 | - 使用webp格式,图片压缩减小图片的大小,节省流量和传输时间。 31 | 32 | - ④ 安全 33 | - 使用Https代替Http 34 | - 对传输数据进行加密处理,对称与非对称加密 35 | - 添加token验证 36 | 37 | - ⑤ 其它 38 | - 使用网络缓存 39 | - 添加失败重试策略 40 | - 合并网络请求,减少请求次数 41 | 如统计信息,无需实时上报,可以将缓存到本地,然后同意上传,这样只需要上传一次头信息,节约了流量。 42 | - 对网络状态进行监听 43 | 44 | -------------------------------------------------------------------------------- /Android/四.ⅲ、Android系统启动流程.md: -------------------------------------------------------------------------------- 1 | ![系统进程](https://github.com/chen-eugene/Android-Interview/blob/master/image/android-booting.jpg) 2 | 3 | - init进程:是Android Kernel层在启动过程中创建的,**是所有用户进程的鼻祖。** 4 | - init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程; 5 | - init进程还启动servicemanager(binder服务管家)、bootanim(开机动画)等重要服务 6 | - init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程),Zygote是所有Java进程的父进程,Zygote进程本身是由init进程孵化而来的。 7 | 8 | - **Zygote进程:由init进程创建,是所有Java进程的鼻祖。** 9 | - 加载ZygoteInit类,注册Zygote Socket服务端套接字; 10 | - 加载虚拟机; 11 | - preloadClasses; 12 | - preloadResouces。 13 | 14 | - System Server:由Zygote进程创建,是Zygote孵化的第一个进程。 15 | - 负责启动和管理整个Java framework,包含ActivityManager,PowerManager等服务。 16 | 17 | ************************** 18 | 19 | #### [1、init进程启动。](http://gityuan.com/2016/02/05/android-init/) 20 | 21 | init进程是所有用户进程的父进程,Linux系统中用户空间的第一个进程,进程号为1.Kernel层启动后,在用户空间启动init进程,并调用init中的main()方法。 22 | 23 | 这部分内容涉及到linux内核,暂时先不管了。 24 | 25 | #### [2、Zygote进程启动。](http://gityuan.com/2016/02/13/android-zygote/) 26 | 27 | **Zygote进程是所有Java进程进程的父进程。** 28 | 29 | 做了几件事: 30 | - 通过JNI的方式调用ZygoteInit.main(),进入到Java层 31 | - 调用registerZygoteSocket函数创建了一个socket接口,用来和ActivityManagerService通讯。 32 | - 进行预加载资源。 33 | - 调用startSystemServer函数来启动system_server进程。 34 | - 调用runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程。 35 | 36 | #### [3、system_server进程的创建](http://gityuan.com/2016/02/14/android-system-server/) 37 | 38 | system_server进程由Zygote进行启动,负责启动和管理整个Java framework,包括ActivityManagerServiec等。 39 | 40 | #### [4、ServiceManager进程的启动](http://gityuan.com/2015/11/07/binder-start-sm/) 41 | 42 | ServiceManager进程由init进程启动,是守护进程,提供注册和查询两个核心功能。 43 | 44 | #### [5、App进程的启动](http://gityuan.com/android/) 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /other/Gradle.md: -------------------------------------------------------------------------------- 1 | #### 1、Gradle构建块。 2 | 每个Gradle构建都包含三个基本构建快: 3 | - project: 4 | 5 | 一个project代表一个正在构建的组件(比如一个JAR文件),或一个想要完成的目标,如部署应用程序。Gradle的build.gradle文件相当于Maven的pom.xml,每个Gradle构建脚本至少定义一个project。 6 | 7 | 当构建进程启动后,Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,并且能够通过priject变量使其隐式可用。 8 | 9 | - task: 10 | 11 | 一个project包含一个或多个task,task包含两个重要功能:任务动作(task action)、任务依赖(task dependecy)。 12 | 13 | 任务动作定义了一个任务执行时的最小工作单元。 14 | 15 | - property:每个project和task实例都提供了可以通过getter和setter方法访问的属性。 16 | 17 | #### 2、Gradle申明属性的方式 18 | 19 | - 扩展属性:使用ext命名空间 20 | 21 | 为了添加属性,需要使用ext命名空间 22 | ``` 23 | 扩展属性申明: 24 | project.ext.myProp = 'myValue' 只有在初始申明扩展属性时需要使用ext命名空间 25 | ext { 26 | someOtherProp = 123 27 | } 28 | 29 | assert myProp == 'myValue' 使用ext命名空间访问属性是可选的 30 | println project.someOtherProp 31 | ext.someOtherProp = 567 32 | ``` 33 | - 在gradle.property文件中申明 34 | 35 | Gradle属性可以通过gradle.properties文件中申明直接添加到项目中,这个文件位于/.gradle目录或项目的根目录下。这些这些属性可通过项目实例访问。记住,即使你有多个项目,每个用户也只能有一个Gradle属性文件/.gradle目录下。这是Gradle对它的限制。 36 | 37 | ``` 38 | 在gradle.properties文件中申明: 39 | exampleProp = myValue 40 | someOtherProp = 455 41 | 42 | 在项目中访问这两个变量: 43 | assert proect.exampleProp == 'myValue' 44 | 45 | task printGradleProperty << { 46 | println "Second property:$someOtherProp" 47 | } 48 | ``` 49 | 50 | #### 3、Gradle包装器:能够让机器在没有安装Gradle运行时的情况下运行Gradle构建,能够解决运行时版本不兼容问题。 51 | 52 | #### 3、gradle脚本的执行时序。 53 | Gradle脚本执行分为三个过程: 54 | 55 | - 初始化:分析有哪些module将要被构建,为每个module创建对应的 project实例。这个时候settings.gradle文件会被解析。 56 | 57 | - 配置:处理所有的模块的 build 脚本,处理依赖,属性等。这个时候每个模块的build.gradle文件会被解析并配置,这个时候会构建整个task的链表。 58 | 59 | - 执行:根据task链表来执行某一个特定的task,这个task所依赖的其他task都将会被提前执行。 60 | 61 | project.afterEvaluate,它表示所有的模块都已经配置完了,可以准备执行task了; 如果注册了多个project.afterEvaluate回调,那么执行顺序等同于注册顺序。 62 | -------------------------------------------------------------------------------- /Android/六、常用技术.md: -------------------------------------------------------------------------------- 1 | #### 1、插件化开发流程,插件化优势,插件化开发中遇到的问题以及如何解决的。 2 | 3 | **插件化优势:** 4 | 5 | - 将某些功能模块以插件化的形式进行拆分,可以减小主项目的体积。 6 | - 可以动态的更新功能模块以及修复一些紧急的Bug,避免主项目App的频繁更新。 7 | 8 | **插件化的缺陷:** 9 | 10 | - 开发方式相比较于常规app的开发比较复杂,由于插件项目是独立开发的,当主项目加载插件运行时,插件的运行环境已经完全不同,代码逻辑容易出现BUG,而且在主项目中调试插件十分繁琐。 11 | - 兼容性问题,有些插件化框架使用了反射的方式调用部分Android系统Framework层的API,部分的定制的Android Rom可能已经改动了这些api,这会导致在不同的手机上出现兼容性问题。 12 | 13 | **实现插件化的三种基本思路:** 14 | 15 | - 简单的动态加载模式: 16 | 17 | Android应用在运行时使用ClassLoader动态加载外部的dex文件非常简单,不用覆盖安装新的APK,就可以更改APP的代码逻辑。但是Android却很难使用插件APK里的res资源,这意味着无法使用新的XML布局等资源,同时由于无法更改本地的Manifest清单文件,所以无法启动新的Activity等组件。 18 | 19 | 不过可以先把要用到的全部res资源都放到主APK里面,同时把所有需要的Activity先全部写进Manifest里,只通过动态加载更新代码,不更新res资源,如果需要改动UI界面,可以通过使用纯Java代码创建布局的方式绕开XML布局。同时也可以使用Fragment代替Activity,这样可以最大限度得避开“无法注册新组件的限制”。 20 | 21 | 这种模式的框架比较适用一些UI变化比较少的项目,比如游戏SDK,基本就只有登陆、注册界面,而且基本不会变动,更新的往往只有代码逻辑。 22 | 23 | - 代理Activity模式: 24 | 25 | 我们可以通过动态加载,让现在的Android应用启动一些“新”的Activity,甚至不用安装就启动一个“新”的APK。宿主APK需要先注册一个空壳的Activity用于代理执行插件APK的Activity的生命周期。 26 | 27 | - 宿主APK可以启动未安装的插件APK。 28 | - 插件APK也可以作为一个普通APK安装并且启动。 29 | - 插件APK可以调用宿主APK里的一些功能。 30 | - 宿主APK和插件APK都要接入一套指定的接口框架才能实现以上功能。 31 | 32 | 同时也主要有一下几点限制: 33 | 34 | - 需要在Manifest注册的功能都无法在插件实现,比如应用权限、LaunchMode、静态广播等。 35 | - 宿主一个代理用的Activity难以满足插件一些特殊的Activity的需求,插件Activity的开发受限于代理Activity。 36 | - 宿主项目和插件项目的开发都要接入共同的框架,大多时候,插件需要依附宿主才能运行,无法独立运行。 37 | 38 | 代理Activity模式的核心在于“使用宿主的一个代理Activity为插件所有的Activity提供组件工作需要的环境”,随着代理模式的逐渐成熟,现在还出现了“使用Hack手段给插件的Activity注入环境”的模式。 39 | 40 | - 动态创建Activity模式 : 41 | 42 | 动态创建Activity模式的核心是“运行时字节码操作”,现在宿主注册一个不存在的Activity,启动插件的某个Activity时都把想要启动的Activity替换成前面注册的Activity,从而是后者能正常启动。 43 | 44 | - 主APK可以启动一个未安装的插件APK。 45 | - 插件APK可以是任意第三方APK,无需接入指定的接口,理所当然也可以独立运行。 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /note/计算机网络/计算机网络第六章.md: -------------------------------------------------------------------------------- 1 | ### 第六章:应用层 2 | 3 | **要点:** 4 | 5 | - 域名系统DNS —— 从域名解析出IP笛子。 6 | 7 | - 万维网和HTTP协议,以及万维网的两种不同的信息搜索引擎。 8 | 9 | - 电子邮件的传送过程,SMTP协议和POP3协议、IMAP协议使用场合。 10 | 11 | - 动态主机配置协议DHCP的特点。 12 | 13 | - 系统调用和应用编程接口的基本概念。 14 | 15 | - P2P文件系统。 16 | 17 | #### 1、域名系统DNS(Domain Name System):用于把便于人们使用的机器名字转换为IP地址。 18 | 19 | #### 2、文件传送协议FTP(File Transfer Protocol) 20 | 21 | #### 3、万维网WWW(World Wide Web)并非某种特殊的计算机网络。万维网是一个大规模的、联机式的信息储藏所,英文简称Web。 22 | 23 | #### 4、统一资源定位符URL:是用来表示从互联网上的到的资源位置和访问这些资源的方法。 24 | 25 | URL的一般形式由以下四个部分组成: 26 | <协议>://<主机>:<端口>/<路径> 27 | 28 | #### 5、超文本传送协议HTTP:定义了浏览器(即万维网客户进程)怎么向万维网服务器请求万维网文档,以及服务器怎么把文档传送给浏览器。HTTP是面向事务(transaction-oriented)的应用层协议,它是万维网上能够可靠地交换文件(包括文本、声音、图像等各种多媒体文件)的重要基础。 29 | 30 | HTTP规定在HTTP客户与HTTP服务器之间的每次交互,都由一个ASCII码串构成的请求和一个类似的通用互联网扩充,即“类MIME(MIME-like)”的响应组成。 31 | 32 | - HTTP使用了面向连接的TCP作为运输层协议,保证了数据的可靠性。但是,HTTP协议**本身是无连接的**。也就是说,虽然HTTP使用了TCP连接,但通信的双方在交换HTTP报文之前不需要先建立HTTP连接。 33 | 34 | - HTTP协议是**无状态的**:也就是说,同一个客户第二次访问同一个服务器上的页面时,服务器的响应与第一次被访问时的相同,因为服务器并不记得曾经访问过的这个客户,也不记得为该客户曾经服务过多少次。HTTP协议的无状态特性简化了服务器的设计,是服务器更容易支持大量并发的HTTP请求。 35 | 36 | #### 6、HTTP的报文结构 37 | 38 | HTTP有两类报文: 39 | 1. 请求报文 —— 从客户向服务器发送的请求报文 40 | 2. 响应报文 —— 从服务器到客户的回答 41 | 42 | ![HTTP报文结构](https://github.com/chen-eugene/Interview/blob/master/image/qewrtsagb.png) 43 | 44 | HTTP的请求报文和响应报文都是由三个部分组成的: 45 | 46 | - 开始行:用于区分是请求报文还是响应报文。 47 | 48 | - 首部行:用来说明浏览器、服务器或报文主体的一些信息。 49 | 50 | - 实体主体:在请求报文中一般都不用这个字段,而在响应报文中也有可能没有这个字段。 51 | 52 | ![请求方法](https://github.com/chen-eugene/Interview/blob/master/image/oqwjoifoqmgowigwq.png) 53 | 54 | 响应报文的第一行就是状态行,状态行包括三项内容,即HTTP的版本,状态码,以及解释状态码的简单短语。 55 | 56 | - 1xx:表示通知信息,如请求收到了或正在进行处理。 57 | 58 | - 2xx:表示成功,如接受或知道了。 59 | 60 | - 3xx:表示重定向,如要完成请求还必须采取进一步行动。 61 | 62 | - 4xx:表示客户的差错,如请求中有错误的语法或不能完成。 63 | 64 | - 5xx:表示服务器的差错,如服务器失效无法完成请求。 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /other/kotlin.md: -------------------------------------------------------------------------------- 1 | #### 1、标准函数。 2 | - run:作用域中的接受者为this,可以省略,返回作用域中的最后一条代码的执行结果。 3 | ``` 4 | stringVariable?.run{ 5 | println("字符串的长度为$length") 6 | } 7 | ``` 8 | 9 | - let:作用域的接受者为it,返回值为作用域中的最后一条代码的执行结果。 10 | ``` 11 | stringVariable?.let{ 12 | println("字符串长度为${it.length}") 13 | } 14 | ``` 15 | 16 | - alse:作用域中的接受者为it,返回值为当前代用的对象,方便实现链式调用。 17 | ``` 18 | "abc".also{ 19 | println("The oringle String is $it") //"abc" 20 | }.also{ 21 | println("The reverse String is ${it.reversed()}") //"cba" 22 | }.also{ 23 | println("The length of the String is ${it.length}") //3 24 | } 25 | ``` 26 | 27 | - apply:作用域中的接受者为this,返回值为当前调用的对象。 28 | ``` 29 | val intent = Intent().apply{ 30 | data = Uri.parse("") 31 | } 32 | ``` 33 | 34 | - with:不是扩展函数,是正常函数,作用域中的接受者为this,返回值为作用域中的最后一条代码的执行结果。 35 | ``` 36 | with(webview.setting){ 37 | this?.javaScriptEnable = true 38 | this?.databaseEnable = true 39 | } 40 | ``` 41 | 42 | - use:Closeable的扩展函数,用于IO操作,在use实现代码里面对代码块lambda代码块进行了try catch操作,并且在finally代码块中调用了close()方法, 43 | 在进行IO操作的时候可以省去对于IOException的捕捉和释放。 44 | ``` 45 | val stream = Files.newInputStream(Paths.get("/file.txt")) 46 | stream.buffered().reader().use{reader -> 47 | println(reader.readText()) 48 | } 49 | ``` 50 | 51 | 52 | #### [2、内联函数inline。](https://www.jianshu.com/p/be78824ce1c2) 53 | 54 | 在kotlin中,函数也是对象,可以把它当做参数传递给函数。 55 | ``` 56 | inline fun call(a:Int,b:Int,c:(Int,Int)->Int){ 57 | var x = c(a,b) 58 | print(x) 59 | } 60 | ``` 61 | 当申明inline的时候,这个lambda也默认为内联函数。 62 | 63 | 在kotlin中,return只可以用在有名字的函数,或者匿名函数中,是的函数执行完毕。针对lambda表达式,不能直接使用return。可以使用return+label。 64 | ``` 65 | fun main(){ 66 | println("start") 67 | go die@{if(it == 1) return@die else println} 68 | println("end") 69 | } 70 | ``` 71 | 但是如果lambda应用在一个内联函数的时候,这时候可以直接return。 72 | ``` 73 | fun main(){ 74 | println("start") 75 | sayHello{ return } 76 | println("end") 77 | } 78 | ``` 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Android/八、架构设计.md: -------------------------------------------------------------------------------- 1 | #### [1、MCV、MVP、MVVM比较。](https://tech.meituan.com/android_mvvm.html) 2 | 3 | **MVC:** 4 | ![mvc](https://github.com/chen-eugene/Interview/blob/master/image/1645b73891f5ff8c.png) 5 | 6 | - Model(模型):用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。 7 | - View(视图):渲染页面。 8 | - Controller(控制器):M和V之间的连接器,用于控制应用程序的流程,及页面的业务逻辑。 9 | 10 | **优点:** 11 | 12 | - 易于维护,MVC使开发和维护用户接口的技术含量降低。 13 | - 业务逻辑被放置在model层,能够更好的复用和修改增加业务。 14 | 15 | **缺点:** 16 | 17 | - Controller和View在android中无法做到彻底分离,耦合严重。 18 | 19 | 20 | **MVP:** 21 | ![mvp](https://github.com/chen-eugene/Interview/blob/master/image/1645b738c9d09633.png) 22 | 23 | - View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。 24 | - View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。 25 | - Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,这样就可以重用。 26 | 27 | **优点:** 28 | 29 | - Model和View完全分离,MVP彻底解决了MVC中View和Controller傻傻分不清楚的问题。 30 | 31 | **缺点:** 32 | 33 | - 视图和Presenter的交互会过于频繁,使得他们的联系过于紧密。也就是说,一旦视图变更了,presenter也要变更。 34 | - MVP把Activity相当的一部分责任放到了Presenter来处理,复杂的业务同时也可能会导致P层太大。 35 | - 一旦业务逻辑越来越多,View定义的方法越来越多,会造成Activity和Fragment实现的方法越来越多,依然臃肿。 36 | 37 | **MVVM:** 38 | 39 | ![mvvm](https://github.com/chen-eugene/Interview/blob/master/image/1645b738e643cfc6.png) 40 | 41 | - MVP中我们说过随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的case,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题。 42 | - 区别于MVP,View和ViewModel行双向绑定,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新。实现View和ViewModel双向绑定的工具是DataBinding。 43 | 44 | **优点:** 45 | 46 | - View和ViewModel双向绑定,不需要通过接口来通信,避免了MVP中过多的接口。 47 | 48 | **缺点:** 49 | 50 | - 由于View和ViewModel的双向绑定,导致出现问题时不好定位。 51 | 52 | **对于偏向展示型的app,绝大多数业务逻辑都在后端,app主要功能就是展示数据,交互等,建议使用mvvm。** 53 | **对于工具类或者需要写很多业务逻辑app,使用mvp或者mvvm都可。** 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /note/计算机网络/计算机网络第一章.md: -------------------------------------------------------------------------------- 1 | ### 第一章:概述 2 | 3 | - 1、**计算机网络(简称网络)**:有若干**结点(node)** 和连接这些结点的**链路(link)** 组成。 4 | 5 | - 2、网络之间还可以通过路由器连接起来,这就构成了一个覆盖范围更大的计算机网路,这样的网络称为**互联网(internetwork或internet)**,因此互联网是“网络的网络”(networks of networks)。 6 | 7 | - 3、互联网服务提供商ISP(Internet Service Provider)。 8 | 9 | - 4、电路交换:必须通过**“建立连接(占用通信资源)→通过(一直占用通信资源)→释放资源(归还通信资源)”**三个步骤的交换方式称为电路交换。 10 | 11 | - 5、通常我们把要发送的整块数据称为一个报文。在发送报文之前,先把较长的报文划分成一个个更小的等长数据段,在每一个数据段前面,加上一些由必要的控制信息组成的首部(header)后,就构成了一个分组(packet)。分组又称为**“包”**,而分组的首部也可以称为**“包头”**。 12 | 13 | **主机是为用户进行信息处理的,路由器是用来转发分组的,即进行分组交换。** 14 | 15 | - 6、网络按照作用范围进行分类: 16 | 17 | - 广域网WAN(Wide Area Network):广域网的作用范围通常为几十到几千公里,因为有时也称为 **远程网(long haul network)** 。 18 | 19 | - 城域网MAN(Metropolitan Area Network):城域网的作用范围一般是一个城市。 20 | 21 | - 局域网LAN(Local Area Metwork):局域网一般用微型计算机或工作站通过告诉通信线路相连,地理上局限在较小的范围。 22 | 23 | - 个人区域网PAN(Personal Area Network):就是在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络,因此也常称为**无线个人区域网WPAN(Wireless PAN)** 。 24 | 25 | - 7、**带宽**:带宽用来表示网络中某通道传送数据的能力,因此网络带宽表示在单位时间内网络中的某信道所能通过的**最高数据率,单位是bit/s**。 26 | 27 | - 8、**吞吐量**:表示单位时间内通过某哥网络的**实际数据量**。 28 | 29 | - 9、**国际标准化组织ISO**提出了一个视图使各种计算机在世界范围内互联成网的标准框架,即著名的**开放系统互连基本参考模型OSI/RM(Open System Interconnection Reference Model),简称OSI**。 30 | 31 | - 10、**ISO七层模型、TCP/IP四层模型和五层模型:** 32 | ![协议分层模型](https://github.com/chen-eugene/Interview/blob/master/image/1540114802(1).png) 33 | 34 | - 应用层:应用层的任务就是**通过应用进程间的交互来完成特定网络应用**。应用层协议定义的是**应用进程间通信和交互的规则**。如域名系统DNS,支持万维网应用的HTTP协议,支持邮件的SMTP协议等,我们把应用层交互的数据单元称为**报文**。 35 | 36 | - 传输层:传输层的任务就是负责向**两台主机中进程之间的通信**提供**通用的数据传输**服务。应用进程利用该服务传送应用层报文。 37 | 38 | 传输层主要使用一下两种协议: 39 | **传输控制协议TCP(Transmission Control Protocol)** —— 提供面向连接的、可靠的数据传输服务,其数据传输的单位是**报文段**。 40 | 41 | **用户数据协议(User Datagram Protocol)** —— 提供无连接的、尽最大努力的数据传输服务(不保证数据传输的可靠性),其数据传输的单位是**用户数据报**。 42 | 43 | - 网络层:网络层负责为分组交换网上的不同主机提供通信服务。在TCP/IP体系中,由于网络层使用IP协议,因此分组也叫做**IP数据报**,或者简称为**数据报**。 44 | 45 | - 数据链路层:在两个结点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻节点间的链路上传送**帧(frame)**。每一帧包括数据和必要的**控制信息**(如同步信息、地址信息、差错信息等)。 46 | 47 | 在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。这样,数据链路层在接收到一个帧后,就可以从中提取出数据部分,上交给网络层。 48 | 49 | - 物理层:在物理层上所传输数据的单位是比特。传递信息所利用到的一些屋里媒介。 50 | 51 | -------------------------------------------------------------------------------- /Android/ActivityManagerService.md: -------------------------------------------------------------------------------- 1 | AMS是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。 2 | 3 | - 四大组件管理(activity,service,content provider,boardcast recever)。 4 | - 主要工作就是管理,记录,查询。 5 | - 四大组件进程通信的server端 四大组件属于client。 6 | - 属于系统进程的一部分。 7 | 8 | 相关类: 9 | - ActivityStack.java:其实是个管理类,管理activity的各种状态 10 | - ActivityRecord.java:ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像 11 | - ActivityThread.java:主线程,类中有main方法。 12 | - H.java:Handler子类。 13 | - Instrumentation.java:可以理解为ActivityThread的一个工具类,用来监控应用程序和系统的交互,对于生命周期的所有操作例如onCreate最终都是直接由它来执行的。对于hook和测试会用到这个类。 14 | - ApplicationThread.java:用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。 15 | 16 | #### [1、Android应用程序的启动过程](https://blog.csdn.net/luoshengyang/article/details/6689748) 17 | 18 | - ① 论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity方法来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity方法。 19 | - ② 通过桌面快捷方式获取Intent,向Intent中添加`Intent.FLAG_ACATIVITY_NEW_TASK`,最终调用到ActivityManagerService.startActivity方法。 20 | - ③ 解析Intent内容,包括类名、启动模式等。 21 | - 如果启动模式为singleTask,就回去查找是否存在Activity相关联的Task没有就从新创建; 22 | - 如果启动模式为singleInstance,就会去查找是否存在要启动的Activity实例,如果没有,就创建一个新的Task。 23 | - 新创建的Task被添加到了ActivityManagerService中。 24 | - ④ 获取栈顶的Activity是否为即将激动的Activity,如果是,就不会在重新创建一个Activity。否则就将要启动的Activity的ActivityRecord存入栈中。 25 | - ⑤ 继续判断要启动的Activity是否为Resumed状态,如果是的话就不做任何操作。 26 | - ⑥ 通过当前状态为Resumed的Activity所在的进程的ApplicationThread对象,来通知Resumed状态的Activity(即Launcher)进入Paused状态。 27 | - ⑦ 根据清单文件的Application的process属性,系统默认使用package的名称,去查找进程是否存在,由于是第一次启动程序,所以会创建一个新的进程。并且执行到ActivityThread的mian函数,创建一个ActivityThread实例,每一个应用程序都有一个ActivityThread实例与之对应。 28 | - ⑧ 通知ActivityManagerService来正真启动一个Activity,并且回调对应的生命周期函数,在客户端创建一个正真的Activity实例(前面栈中保存的都是ActivityRecord)。 29 | 30 | 31 | 32 | #### [2、Android应用进程的启动过程](https://blog.csdn.net/luoshengyang/article/details/6747696) 33 | 34 | - Android应用程序进程的入口函数是`ActivityThread.main`,即进程创建完成之后,Android应用程序框架层就会在这个进程中将ActivityThread类加载进来,然后执行它的main函数,这个main函数就是进程执行消息循环的地方了。 35 | 36 | - Android应用进程天然支持Binder进程间通信。当我们在Android应用程序中实现Server组件的时候,我们并没有让进程进入一个循环中去等待Client组件的请求,然而,当Client组件得到这个Server组件的远程接口时,却可以顺利地和Server组件进行进程间通信,这就是因为Android应用程序进程在创建的时候就已经启动了一个线程池来支持Server组件和Binder驱动程序之间的交互了,这样,极大地方便了在Android应用程序中创建Server组件。 37 | 38 | #### [3、Launcher启动过程](https://blog.csdn.net/luoshengyang/article/details/6767736) 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /note/计算机网络/计算机网络第四章.md: -------------------------------------------------------------------------------- 1 | ### 第四章:网络层 2 | 3 | #### 1、网络层设计思路:网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务。 4 | 5 | 好处就是网络造价大大降低,运行方式灵活,能够适应多种应用。 6 | 7 | #### 2、将网络互相连接起来要使用一些中间设备: 8 | 9 | - 物理层使用的中间设备叫**转发器**。 10 | - 数据链路层使用的中间设备叫**网桥**或**桥接器**。 11 | - 网络层使用的中间设备叫**路由器**。 12 | - 在网络层以上使用的中间设备叫**网关**。用网关连接两个不兼容的系统需要在高层进行协议转换。 13 | 14 | #### 3、 虚拟互联网络 15 | 16 | 图4-3(a)表示有许多计算机网络通过一些路由器进行互连。由于参加互连的计算机网络都使用相同的**网络协议IP(International Protocol)**,因此可以把互连以后的计算机网络看成如图4-3(b)所示的一个**虚拟互连网络(internet)**。所谓虚拟互连网络也就是逻辑互连网络,它的意思就是互连起来的各种物理网络的异构性本来是客观存在的,但是我们利用IP协议就可以使这些性能各异的网络**在网络层上看起来好像是一个统一的网络**。这种使用IP协议的虚拟互连网络可简称为**IP网**(IP网是虚拟的,但平常不必每次都强调“虚拟”二字)。 17 | 18 | 使用IP网的好处是:当IP网上的主机进行通信时,就好像在一个单个网络上通信一样,它们看不见互连的各网络的具体异构细节(如具体的编址方案、路由器选择协议,等等)。如果在这种覆盖全球的IP网的上层使用TCP协议,那么就是现在的互联网(Internet)。 19 | 20 | ![虚拟互联网络](https://github.com/chen-eugene/Interview/blob/master/image/84734854472343465496.png) 21 | 22 | 23 | #### 4、分类IP地址: 24 | 25 | 所谓“分类的IP地址”就是将IP地址划分成若干个固定类,每一个地址由两个固定长度的字段组成,其中第一个字段是**网络号(net-id)** ,它标志主机(或路由器)所连接到的网络。一个网络号在整个互联网范围内必须是唯一的。第二个字段是**主机号(host-id)** ,它标志该主机(或路由器)。一台主机在它前面的网络号所指明的网络范围内必须是唯一的。由此可见,**一个IP地址在整个互联网范围内是唯一的**。 26 | 27 | IP地址空间共有2^32个地址,A类地址空间共有2^31个,占50%;B类地址空间共有2^30个,占25%;C类地址空间共有2^29个,占12.5%。 28 | 29 | ![IP地址](https://github.com/chen-eugene/Interview/blob/master/image/weurudsoifj.png) 30 | 31 | #### 5、子网掩码:从IP数据包的首部无法看出源主机或目的主机所连接的网络是否进行了子网的划分。这是因为32位的IP地址本身以及数据报的首部都没有包含任何有关子网划分的信息。子网掩码的作用就是不管网络有没有划分子网,只要把子网掩码和IP地址进行逐位的 “与”运算(AND),就能立即得出网络地址来。 32 | 33 | ![子网掩码](https://github.com/chen-eugene/Interview/blob/master/image/1541426783(1).png) 34 | 35 | #### 6、分层次的路由选择协议 36 | 37 | 由于① 互联网的规模非常大。 ② 许多单位不愿意外界了解自己单位网络的布局细节和本部门所采用的路由选择协议,但同时还希望连接到互联网上。 为此可以把整个互联网划分为许多较小的自治系统(autonomous system)AS。 38 | 39 | 互联网把路由选择协议划分为两大类: 40 | 41 | - 内部网关协议IGP(Interior Gateway Protocol) 即在一个自治系统内部使用的路由选择协议,而这与互联网中的其他自治系统选用的什么路由选择协议无关。目前这类路由选择协议使用的最懂,如RIP和OSPF协议。 42 | 43 | - 外部网关协议EGP(External Gateway Protocol) 若源主机和目的主机处在不同的资质系统中(这两个自治系统可能使用不同的内部网关协议),但数据报传到一个自治系统的边界时,就需由于要使用一种协议将路由选择信息传递到另一个自治系统中。这样的协议就是外部网关协议EGP。目前使用最多的外部网关协议是BGP的版本4(BGP-4)。 44 | 45 | #### 7、路由器的构成 46 | 47 | 路由器是一种具有多个输入端口和多个输出端口的专用计算机,其任务就是转发分组。从路由器某个输入端口受到分组,按照分组要去的目的地(即目标网络),吧该分组从路由器的某个合适的输出端口转发给下一跳路由器。下一跳路由器也按照这种方法处理分组,直到该分组达到终点为止。路由器的转发分组正式网络层的主要工作。 48 | 49 | 50 | #### 8、IPv6 51 | 52 | IPv6地址为128位,采用冒号十六进制记法。IPv4地址为32位,采用点分十进制计数法。 53 | 54 | ``` 55 | 68E6:8C64:0000:0000:0000:0000:960A:FFFF 56 | 可记为 68E6:8C64:0:0:0:0:960A:FFFF 57 | 也可记为 68E6:8C64::960A:FFFF 58 | 59 | 为了保证零压缩有一个不含混的解释,规定在任一地址中只能使用一次零压缩。 60 | ``` 61 | 62 | #### 9、从IPv4向IPv6过渡 63 | 64 | 由于互联网规模太大,因此不可能短时间内一律都改用IPv6,因此IPv6系统必须能够接收和转发IPv4分组。IPv4向IPv6过度策略。 65 | 66 | - 双协议栈:指在完全过渡到IPv6之前,使一部分主机(或路由器)装有双协议栈:一个IPv4和一个IPv6。因此双协议栈主机(或路由器)能够和IPv6的系统通信,又能够和IPv4系统通信。 67 | 68 | - 隧道技术:在IPv6数据报要进入IPv4网络时,把IPv6数据报封装成为IPv4数据报。当IPv4数据报离开IPv4网络中隧道时,在保数据部分(既原来的IPv6数据报)交给主机的IPv6协议栈。 69 | 70 | #### 10、IP多播(组播):一对多的通信 71 | 72 | 与单播相比,在一对多的通信中,多播可大大节约网络资源。在互联网范围的多播要靠路由器来实现,这些路由器必须增加一些能够识别多播数据报的软件。能够运行多播协议的路由器称为**多播路由器**。多播路由器当然也可以转发普通的单播IP数据报。 73 | 74 | #### 11、虚拟专用网VPN(Virtual Private Network):利用公用的互联网作为本机构个专用网之间的通信载体,这样的专用网又称为虚拟专用网VPN。 75 | 76 | 假定某个机构在两个相隔比较远的场所建立了专用网A和B,其网络地址分别为专用地址10.1.0.0和10.2.0.0 。现在这两个场所需要通过公用的互联网构成一个VPN。翻墙估计就是这么来的。 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /note/Gradle实战/Gradle实战第四章.md: -------------------------------------------------------------------------------- 1 | ### 第四章 2 | 3 | #### 1、Gradle构建块。 4 | 每个Gradle构建都包含三个基本构建快: 5 | - project: 6 | 7 | 一个project代表一个正在构建的组件(比如一个JAR文件),或一个想要完成的目标,如部署应用程序。Gradle的build.gradle文件相当于Maven的pom.xml,每个Gradle构建脚本至少定义一个project。 8 | 9 | 当构建进程启动后,Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,并且能够通过priject变量使其隐式可用。 10 | 11 | - task: 12 | 13 | 一个project包含一个或多个task,task包含两个重要功能:任务动作(task action)、任务依赖(task dependecy)。 14 | 15 | 任务动作定义了一个任务执行时的最小工作单元。 16 | 17 | - property:每个project和task实例都提供了可以通过getter和setter方法访问的属性。 18 | 19 | #### 2、Gradle申明属性的方式 20 | 21 | - 扩展属性:使用ext命名空间 22 | 23 | 为了添加属性,需要使用ext命名空间 24 | ``` 25 | 扩展属性申明: 26 | project.ext.myProp = 'myValue' 只有在初始申明扩展属性时需要使用ext命名空间 27 | ext { 28 | someOtherProp = 123 29 | } 30 | 31 | assert myProp == 'myValue' 使用ext命名空间访问属性是可选的 32 | println project.someOtherProp 33 | ext.someOtherProp = 567 34 | ``` 35 | - 在gradle.property文件中申明 36 | 37 | Gradle属性可以通过gradle.properties文件中申明直接添加到项目中,这个文件位于/.gradle目录或项目的根目录下。这些这些属性可通过项目实例访问。记住,即使你有多个项目,每个用户也只能有一个Gradle属性文件/.gradle目录下。这是Gradle对它的限制。 38 | 39 | ``` 40 | 在gradle.properties文件中申明: 41 | exampleProp = myValue 42 | someOtherProp = 455 43 | 44 | 在项目中访问这两个变量: 45 | assert proect.exampleProp == 'myValue' 46 | 47 | task printGradleProperty << { 48 | println "Second property:$someOtherProp" 49 | } 50 | ``` 51 | 52 | #### 3、Gradle包装器:能够让机器在没有安装Gradle运行时的情况下运行Gradle构建,能够解决运行时版本不兼容问题。 53 | 54 | #### 4、申明task动作:动作(action)就是在task中合适的地方防止构建逻辑。Task接口提供了两个相关的方法来声明task动作:doFirst(相当于>>)和doLast(相当于<<),当task被执行的时候,动作逻辑被定义为闭包参数被依次执行。 55 | 56 | #### 5、访问默认 57 | ``` 58 | task printVersion << { 59 | logger.quiet "Version:$version" 60 | } 61 | ``` 62 | 63 | #### 6、定义task依赖 64 | ``` 65 | task first << { println "first" } 66 | task second << { println "second" } 67 | 68 | task printVersion(dependsOn:[second,first]) << { 69 | logger.quiet "Version:$version" 70 | } 71 | 72 | task third << { println "third" } 73 | third.dependsOn('printVersion') 74 | ``` 75 | Gradle不能保证task依赖的执行顺序,dependsOn方法只是定义了所依赖的task需要先执行。 76 | 77 | 78 | #### 7、Gradle构建生命周期阶段 79 | 80 | Gradle脚本执行分为三个过程: 81 | 82 | - 初始化:分析有哪些module将要被构建,为每个module创建对应的 project实例。这个时候settings.gradle文件会被解析。在这个阶段当前已有的构建脚本代码都不会被执行。 83 | 84 | - 配置:处理所有的模块的 build 脚本,处理依赖,属性等。这个时候每个模块的build.gradle文件会被解析并配置,这个时候会构建整个task的链表,task配置块永远在task动作执行之前被执行。 85 | 86 | - 执行:根据task链表来执行某一个特定的task,这个task所依赖的其他task都将会被提前执行。 87 | 88 | project.afterEvaluate,它表示所有的模块都已经配置完了,可以准备执行task了; 如果注册了多个project.afterEvaluate回调,那么执行顺序等同于注册顺序。 89 | 90 | #### 8、Gradle的内置task类型 91 | 92 | Gradle的内置task类型都是DefaultTask的派生类。增强task(自定义task)可以直接使用。 93 | ``` 94 | task createDistribution(type:Zip,dependsOn:makeReleaseVersion){ 95 | from war.outputs.files //隐式饮用War task 96 | 97 | from(sourceSets*.allSource){ //把所有源文件都放到ZIP文件的src目录下 98 | into 'src' 99 | } 100 | 101 | from(rootDir){ //为ZIP文件添加版本文件 102 | include versionFile.name 103 | } 104 | } 105 | 106 | task backupReleaseDistribution(type:Copy){ 107 | from createDistribution.outputs.files //隐式饮用createDistribution的输出 108 | into "$buildDir/backup" 109 | } 110 | 111 | task release(dependsOn:backupReleaseDistribution) << { 112 | logger.quiet 'Releasing the project...' 113 | } 114 | ``` 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Java/二、Java集合框架.md: -------------------------------------------------------------------------------- 1 | #### [1、Java集合框架的结构。](https://www.jianshu.com/p/63e76826e852) 2 | 3 | ![Java集合框架](https://github.com/chen-eugene/Android-Interview/blob/master/image/2243690-9cd9c896e0d512ed.jpg) 4 | 5 | #### 2、ArrayList实现原理。 6 | [ArrayList工作原理](https://yikun.github.io/2015/04/04/Java-ArrayList%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/) 7 | [ArrayList源码](https://blog.csdn.net/ns_code/article/details/35568011) 8 | - 无参构造方法构造的ArrayList的容量默认为10。 9 | - 当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1。当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。 10 | - ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。 11 | - 在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理,ArrayList中允许元素为null。 12 | #### 3、LinkedList实现原理。 13 | [LinkedList工作原理](https://yikun.github.io/2015/04/05/Java-LinkedList%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/) 14 | [LinkedList源码](https://blog.csdn.net/ns_code/article/details/35787253) 15 | - LinkedList的实现是基于双向循环链表的,因此不存在容量不足的问题,且头结点中不存放数据。 16 | - LinkedList是基于链表实现的,因此插入删除效率高,查找效率低。 17 | #### 4、HashMap实现原理。 18 | [HashMap工作原理](https://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/) 19 | [HashMap源码](https://blog.csdn.net/ns_code/article/details/36034955) 20 | - 在Java 8之前的实现中是用链表解决冲突的,在产生碰撞的情况下,进行get时,两步的时间复杂度O(1)+O(n)。因此,当碰撞很厉害的时候n很大,O(n)的速度显然是影响速度的。因此在Java 8中,利用红黑树替换链表,这样复杂度就变成了O(1)+O(logn)了,这样在n很大的时候,能够比较理想的解决这个问题。 21 | - 当put时,如果发现目前的bucket占用程度已经超过了Load Factor所希望的比例,那么就会发生resize。在resize的过程,简单的说就是把bucket扩充为2倍,之后重新计算index,把节点再放到新的bucket中。 22 | #### 5、LinkedHashMap实现原理。 23 | [LinkedHashMap工作原理](https://yikun.github.io/2015/04/02/Java-LinkedHashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/) 24 | [LinkedHashMap源码](https://blog.csdn.net/ns_code/article/details/37867985) 25 | ![LinkedHashMap结构图](https://github.com/chen-eugene/Interview/blob/master/image/v2-a821a76fb84ce6223598c89ae8ebe7b0_hd.jpg) 26 | 27 | 28 | #### [6、ConcurrentHashMap的原理。](https://www.cnblogs.com/dolphin0520/p/3932905.html) 29 | - get方法没有加锁。 30 | - 默认情况下分为16个segment,segment之间不存在竞争关系,只要线程不是访问的同一个segment,那么就没有锁竞争,默认情况下允许16条线程并发的访问不会回发生竞争。 31 | - 访问ConcurrentHashMap会进程两次的hash计算,第一次确定是在哪一个segment中,第二次是确定key的索引位置。 32 | 33 | 34 | #### [7、什么是fast-fail机制。](https://blog.csdn.net/javazejian/article/details/53073995#%E7%90%86%E8%A7%A3iterator%E6%8E%A5%E5%8F%A3) 35 | 36 | **先说说为什么需要迭代器(Iterator)?** 37 | 38 | 试想一下,我们在遍历ArrayList等集合类的时候通常可以采用for循环,但是如果随便的一个集合类,在不知道其api或者内部结构的情况下如何进行遍历呢?这时就需要迭代器。 39 | 40 | 迭代器是一种标准化遍历容器类的工具类。用于遍历各种集合类。它可以避免暴露集合的内部结构,只要该集合内部实现了迭代器,那么就可以在不知道API或者集合类内部结构的情况下通过迭代器遍历该集合的所有元素。 41 | 42 | **fast-fail机制:** 43 | 44 | 迭代器和集合类之间还存在着一个严重的问题,那就是怎么保证数据的一致性,比如说当我们通过迭代器遍历ArraryList的时候,已经通过hashNext方法判断了,正在调用next方法,但是我们有调用了ArrayList本身的remove方法,这样就会造成迭代器获取的数据和ArrayList的数据不一致的情况。 45 | 46 | fast-fail机制是java集合保证迭代器数据和集合本身数据是否一致的一种机制,当多个线程直接修改集合的结构而没有通知迭代器的时候就有可能触发fast-fail机制,这里是有可能触发,而不是一定触发。 47 | 48 | 49 | #### [8、SparseArray的原理,与HashMap的区别。](https://extremej.itscoder.com/sparsearray_source_analyse/) 50 | 51 | **原理:** 52 | - 在Android中,SparseArray是用来取代HashMap的,它的key默认是int类型。 53 | - 在它的内部使用的int[]数组来存放key,使用Object[]数组来存放value。 54 | - 存放key的数组是有序的,采用二分查找法计算key应该插入的位置。 55 | - 如果有冲突,就直接覆盖掉原来的值,并不会返回原值(HashMap会返回原值)。 56 | - 如果当前要插入的 key 的索引上的值为DELETE,直接覆盖。 57 | - 前几步都失败了,检查是否需要gc()并且在该索引上插入数据。 58 | 59 | **与HashMap的区别:** 60 | - 使用int[]数组存放key,避免了HashMap中基本数据类型装箱的消耗。 61 | - 没有使用HashMap中的结构体Entry,节省了内存。 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Android/四.ⅴ、Android图形界面.md: -------------------------------------------------------------------------------- 1 | #### [1、WMS的启动流程。](http://gityuan.com/2017/01/08/windowmanger/) 2 | 3 | 4 | #### 2、窗口(Window)的启动 5 | 6 | - [startingWindow(预览窗口)的启动](http://gityuan.com/2017/01/15/wms_starting_window/) 7 | 8 | - [Activity窗口的启动](http://gityuan.com/2017/01/22/start-activity-wms/) 9 | 10 | #### [3、Choreographer和Vsync信号原理](http://gityuan.com/2017/02/25/choreographer/) 11 | 12 | #### [4、View的绘制流程](https://blog.csdn.net/yanbober/article/details/46128379/) 13 | 14 | #### [5、Android中有哪些窗口](https://blog.csdn.net/u012439416/article/details/54564330) 15 | 16 | Android窗口类型主要分了三大类: 17 | - 应用程序窗口:比如我们应用程序的Activity的窗口。 18 | - 子窗口:一般在Activity里面的窗口,比如各种菜单等。 19 | - 系统窗口:系统的窗口,比如输入法,Toast,墙纸等。 20 | 21 | 系统对TYPE_APPLICATION类型的窗口,要求必需是Activity的Token,不是的话系统会抛出BadTokenException异常。Dialog 是应用窗口类型,Token必须是Activity的Token。 22 | 23 | #### [6、Dialog的context能传ApplicationContext吗,为什么?Dialog和Toast有什么不同。](https://www.jianshu.com/p/628ac6b68c15) 24 | 25 | ``` 26 | Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { 27 | ... 28 | 29 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 30 | 31 | final Window w = new PhoneWindow(mContext); 32 | mWindow = w; 33 | w.setWindowManager(mWindowManager, null, null); 34 | 35 | ... 36 | } 37 | ``` 38 | 跟Activity对应的窗口一样,Dialog有一个PhoneWindow的实例。在Dialog构造方法中,通过`w.setWindowManager(mWindowManager, null, null);`将appToken设为了null,所以Dialog的token为null。 39 | 40 | ``` 41 | public void show() { 42 | ... 43 | 44 | WindowManager.LayoutParams l = mWindow.getAttributes(); 45 | 46 | mWindowManager.addView(mDecor, l); 47 | ... 48 | } 49 | 50 | public final WindowManager.LayoutParams getAttributes() { 51 | return mWindowAttributes; 52 | } 53 | 54 | public LayoutParams() { 55 | super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 56 | type = TYPE_APPLICATION; 57 | format = PixelFormat.OPAQUE; 58 | } 59 | ``` 60 | 在show方法中获取了WindowManager.LayoutParams,Dialog的窗口类型为TYPE_APPLICATIOIN,并通过mWindowManager添加了视图。 61 | 62 | 在Dialog的构造方法中通过getSystemService来获取mWindowManager,如果传入的不是Activity,那么在Dialog中获取到一个WindowManagerImpl实例,并且其中关联的parentWindow为null,所以Dialog的token也为null,在WindowManagerService添加视图的时候会报BadTokenException异常。 63 | 如果传入的是Activity,Activity中重写了getSystemService方法,Dialog中获取到的就是Activity中创建的本地WindoeManagerImpl实例,其中所关联的parentWindow就是Activity的Window实例,所以Dialog中使用的就是Activity的AppToken。 64 | ``` 65 | @Override 66 | public Object getSystemService(@ServiceName @NonNull String name) { 67 | if (getBaseContext() == null) { 68 | throw new IllegalStateException( 69 | "System services not available to Activities before onCreate()"); 70 | } 71 | 72 | if (WINDOW_SERVICE.equals(name)) { 73 | return mWindowManager; 74 | } else if (SEARCH_SERVICE.equals(name)) { 75 | ensureSearchManager(); 76 | return mSearchManager; 77 | } 78 | return super.getSystemService(name); 79 | } 80 | ``` 81 | **那为什么一定要是Activity的Token呢?** 82 | 83 | 我想使用Token应该是为了安全问题,通过Token来验证WindowManager服务请求方是否是合法的。如果我们可以使用Application的Context,或者说Token可以不是Activity的Token,那么用户可能已经跳转到别的应用的Activity界面了,但我们却可以在别人的界面上弹出我们的Dialog,想想就觉得很危险。 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Android/四.ⅲ、Android系统整体流程.md: -------------------------------------------------------------------------------- 1 | AMS是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。 2 | 3 | - 四大组件管理(activity,service,content provider,boardcast recever)。 4 | - 主要工作就是管理,记录,查询。 5 | - 四大组件进程通信的server端 四大组件属于client。 6 | - 属于系统进程的一部分。 7 | 8 | 相关类: 9 | - ActivityStack.java:其实是个管理类,管理activity的各种状态 10 | - ActivityRecord.java:ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像 11 | - ActivityThread.java:主线程,类中有main方法。 12 | - H.java:Handler子类。 13 | - Instrumentation.java:可以理解为ActivityThread的一个工具类,用来监控应用程序和系统的交互,对于生命周期的所有操作例如onCreate最终都是直接由它来执行的。对于hook和测试会用到这个类。 14 | - ApplicationThread.java:用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。 15 | 16 | *************************************** 17 | 18 | #### [1、apk的安装流程。](https://blog.csdn.net/luoshengyang/article/details/6766010) 19 | 20 | Android系统在启动的过程中,会启动一个应用程序管理服务PackageManagerService,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,即以Apk为后缀的文件,然后对这些文件进解析,得到应用程序的相关信息,完成应用程序的安装过程. 21 | 22 | 应用程序管理服务PackageManagerService安装应用程序的过程,其实就是解析析应用程序配置文件AndroidManifest.xml的过程,并从里面得到得到应用程序的相关信息,例如得到应用程序的组件Activity、Service、Broadcast Receiver和Content Provider等信息,有了这些信息后,通过ActivityManagerService这个服务,我们就可以在系统中正常地使用这些应用程序了。 23 | 24 | - ① SystemServer组件是由Zygote进程负责启动的,启动的时候就会调用它的main函数,系统服务的启动都是从main函数开始的。 25 | - ② 在这个调用链中会去创建一个ServerThread线程,PackageManagerService服务就是在这个线程中启动的。 26 | - ③ 在ServerThread的run方法中会去调用PackageManagerService的main函数,创建了一个PackageManagerService实例,并把它添加到ServiceManager中去,ServiceManager是Android系统Binder进程间通信机制的守护进程,负责管理系统中的Binder对象。 27 | - ④ PackageManagerService会去扫描移动设备上的五个目录(/system/framework、/system/app、/vendor/app、/data/app、/data/app-private)中的Apk文件,对每一个Apk文件进行解析和安装,并且把解析后的应用程序信息保存在PackageManagerService中。 28 | - ⑤ 这样就把前面解析应用程序得到的package、provider、service、receiver和activity等信息保存在了PackageManagerService服务中了。但是,这些应用程序只是相当于在PackageManagerService服务注册好了,如果我们想要在Android桌面上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。 29 | 30 | 31 | #### [2、Launcher启动过程](https://blog.csdn.net/luoshengyang/article/details/6767736) 32 | 33 | 34 | #### [3、Android应用进程的启动过程](https://blog.csdn.net/luoshengyang/article/details/6747696) 35 | 36 | - Android应用程序进程的入口函数是`ActivityThread.main`,即进程创建完成之后,Android应用程序框架层就会在这个进程中将ActivityThread类加载进来,然后执行它的main函数,这个main函数就是进程执行消息循环的地方了。 37 | 38 | - Android应用进程天然支持Binder进程间通信。当我们在Android应用程序中实现Server组件的时候,我们并没有让进程进入一个循环中去等待Client组件的请求,然而,当Client组件得到这个Server组件的远程接口时,却可以顺利地和Server组件进行进程间通信,这就是因为Android应用程序进程在创建的时候就已经启动了一个线程池来支持Server组件和Binder驱动程序之间的交互了,这样,极大地方便了在Android应用程序中创建Server组件。 39 | 40 | 41 | #### [4、Android应用程序的启动过程](https://blog.csdn.net/luoshengyang/article/details/6689748) 42 | 43 | - ① 论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity方法来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity方法。 44 | - ② 通过桌面快捷方式获取Intent,向Intent中添加`Intent.FLAG_ACATIVITY_NEW_TASK`,最终调用到ActivityManagerService.startActivity方法。 45 | - ③ 解析Intent内容,包括类名、启动模式等。 46 | - 如果启动模式为singleTask,就回去查找是否存在Activity相关联的Task没有就从新创建; 47 | - 如果启动模式为singleInstance,就会去查找是否存在要启动的Activity实例,如果没有,就创建一个新的Task。 48 | - 新创建的Task被添加到了ActivityManagerService中。 49 | - ④ 获取栈顶的Activity是否为即将激动的Activity,如果是,就不会在重新创建一个Activity。否则就将要启动的Activity的ActivityRecord存入栈中。 50 | - ⑤ 继续判断要启动的Activity是否为Resumed状态,如果是的话就不做任何操作。 51 | - ⑥ 通过当前状态为Resumed的Activity所在的进程的ApplicationThread对象,来通知Resumed状态的Activity(即Launcher)进入Paused状态。 52 | - ⑦ 根据清单文件的Application的process属性,系统默认使用package的名称,去查找进程是否存在,由于是第一次启动程序,所以会创建一个新的进程。并且执行到ActivityThread的mian函数,创建一个ActivityThread实例,每一个应用程序都有一个ActivityThread实例与之对应。 53 | - ⑧ 通知ActivityManagerService来正真启动一个Activity,并且回调对应的生命周期函数,在客户端创建一个正真的Activity实例(前面栈中保存的都是ActivityRecord),MainActivity启动起来了就表示整个应用程序也启动起来了。 54 | 55 | 56 | #### [5、Service的启动流程。](https://blog.csdn.net/luoshengyang/article/details/6677029) 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /other/计算机网络.md: -------------------------------------------------------------------------------- 1 | #### 1、TCP三次握手过程,为什么需要三次握手。 2 | TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。 3 | ![三次握手](https://github.com/chen-eugene/Interview/blob/master/image/3985563-cd5a153e44696643.png) 4 | - 第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认。 5 | 6 | - 第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时还要向客户端发送一个SYN包(syn=k),服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态。 7 | 8 | - 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 9 | 10 | **为什么需要三次握手:** 11 | 12 | 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。 13 | 14 | client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。 15 | 16 | #### 2、TCP四次挥手过程,为什么需要四次挥手。 17 | ![四次挥手](https://github.com/chen-eugene/Interview/blob/master/image/3985563-6fdf680296f3acd3.png) 18 | - 第一次挥手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了。 19 | 20 | - 第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求; 21 | 22 | - 第三次挥手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态; 23 | 24 | - 第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL(MSL:Maximum Segment Lifetime,报文段最大生存时间,详情请见下文介绍)后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。 25 | 26 | **为什么需要四次挥手:** 27 | 28 | TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。 29 | 30 | #### 3、TCP四次挥手为什么需要等待2MSL。 31 | MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。 32 | 33 | 原因有二: 34 | - 保证TCP协议的全双工连接能够可靠关闭。 35 | 36 | 如果主机1直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致主机2没有收到主机1最后回复的ACK。那么主机2就会在超时之后继续发送FIN,此时由于主机1已经CLOSED了,就找不到与重发的FIN对应的连接。所以,主机1不是直接进入CLOSED,而是要保持TIME_WAIT状态。当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。 37 | 38 | - 保证这次连接的重复数据段从网络中消失。 39 | 40 | 如果主机1直接CLOSED,然后又再向主机2发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中(称为Lost Duplicate),这些延迟数据在建立新连接之后才到达主机2,由于新连接和老连接的端口号是一样的,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接要在TIME_WAIT状态等待2倍MSL,保证本次连接的所有数据都从网络中消失。 41 | 42 | #### 4、什么是DNS。 43 | DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。 44 | 45 | #### 5、Http 的session和cookie的区别。 46 | - Session:是在服务端保存用户信息的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。 47 | - Cookie:是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。 48 | 49 | #### 6、Https工作原理。 50 | [Https工作原理](http://www.cnblogs.com/binyue/p/4500578.html) 51 | 52 | Https是一种基于SSL/TLS的Http协议,所有的http数据都是在SSL/TLS协议封装之上传输的,也属于应用层协议。 53 | 54 | TLS/SSL中使用了非对称加密,对称加密以及HASH算法。 55 | 56 | - HTTP协议运行在TCP之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。 57 | 58 | - HTTPS是运行在SSL/TLS之上的HTTP协议,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。 59 | 60 | **Https握手过程:** 61 | - 浏览器将自己支持的一套加密规则发送给网站。 62 | 63 | - 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。 64 | 65 | - 获得网站证书之后浏览器要做以下工作: 66 | 67 | - 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。 68 | - 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。 69 | - 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。 70 | 71 | - 网站接收浏览器发来的数据之后要做以下的操作: 72 | 73 | - 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。 74 | - 使用密码加密一段握手消息,发送给浏览器。 75 | 76 | - 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。 77 | 78 | #### 7、get和post请求的区别。 79 | 80 | - ① get请求用来从服务器上获得资源,而post是用来向服务器提交数据; 81 | - ② get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL; 82 | - ③ get传输的数据要受到URL长度限制(1024字节即256个字符);而post可以传输大量的数据,上传文件通常要使用post方式; 83 | - ④ 使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post; 84 | - ⑤ get使用MIME类型application/x-www-form-urlencoded的URL编码(也叫百分号编码)文本的格式传递参数,保证被传送的参数由遵循规范的文本组成,例如一个空格的编码是"%20"。 85 | 86 | [扩展阅读1](https://www.zhihu.com/question/28586791) 87 | [扩展阅读2](https://mp.weixin.qq.com/s?__biz=MzI3NzIzMzg3Mw==&mid=100000054&idx=1&sn=71f6c214f3833d9ca20b9f7dcd9d33e4#rd) 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Java部分 2 | ### [一、Java基础](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E4%B8%80%E3%80%81Java%E5%9F%BA%E7%A1%80.md) 3 | ### [二、Java集合框架](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E4%BA%8C%E3%80%81Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6.md) 4 | ### [三、IO/NIO](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E4%B8%89%E3%80%81IO-NIO.md) 5 | ### [四、Java并发(Concurrent)](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E5%9B%9B%E3%80%81Java%E5%B9%B6%E5%8F%91(Concurrent).md) 6 | ### [五、JVM](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E4%BA%94%E3%80%81JVM.md) 7 | ### [六、other](https://github.com/chen-eugene/Android-Interview/blob/master/Java/%E5%85%AD%E3%80%81other.md) 8 | 9 | ## Android部分 10 | ### [一、Android基础](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%B8%80%E3%80%81Android%E5%9F%BA%E7%A1%80.md) 11 | ### [二、四大组件相关](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%8C%E3%80%81%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E7%9B%B8%E5%85%B3.md) 12 | ### [三、View剖析](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%B8%89%E3%80%81View%E5%89%96%E6%9E%90.md) 13 | 14 | ### 四、Framework 15 | - [ⅰ Binder](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B0%E3%80%81Binder.md) 16 | - [ⅱ 资源管理框架](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B1%E3%80%81%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E6%A1%86%E6%9E%B6.md) 17 | - [ⅲ Android系统启动流程](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B2%E3%80%81Android%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B.md) 18 | - [ⅳ Android四大组件](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B3%E3%80%81Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6.md) 19 | - [ⅴ Android图形界面](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B4%E3%80%81Android%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2.md) 20 | - [ⅵ other](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%9B%9B.%E2%85%B5%E3%80%81other.md) 21 | 22 | ### 五、性能优化 23 | - [ⅰ 渲染优化](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%94.%E2%85%B0%E3%80%81%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96.md) 24 | - [ⅱ 内存优化](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%94.%E2%85%B1%E3%80%81%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96.md) 25 | - [ⅲ 网络优化](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%94.%E2%85%B2%E3%80%81%E7%BD%91%E7%BB%9C%E4%BC%98%E5%8C%96.md) 26 | - [ⅳ 电量优化](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%94.%E2%85%B3%E3%80%81%E7%94%B5%E9%87%8F%E4%BC%98%E5%8C%96.md) 27 | - [ⅴ 其它](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%BA%94.%E2%85%B4%E3%80%81%E5%85%B6%E5%AE%83.md) 28 | 29 | ### [六、常用技术](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%85%AD%E3%80%81%E5%B8%B8%E7%94%A8%E6%8A%80%E6%9C%AF.md) 30 | ### [七、常用框架](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%B8%83%E3%80%81%E5%B8%B8%E7%94%A8%E6%A1%86%E6%9E%B6.md) 31 | ### [八、架构设计](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E5%85%AB%E3%80%81%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md) 32 | ### [九、经典案例实现](https://github.com/chen-eugene/Android-Interview/blob/master/Android/%E4%B9%9D%E3%80%81%E7%BB%8F%E5%85%B8%E6%A1%88%E4%BE%8B.md) 33 | 34 | ## Other 35 | ### [设计模式](https://github.com/chen-eugene/Interview/blob/master/other/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md) 36 | ### [计算机网络](https://github.com/chen-eugene/Interview/blob/master/other/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md) 37 | ### [数据结构与算法分析](https://github.com/chen-eugene/Interview/blob/master/other/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md) 38 | ### [Gradle](https://github.com/chen-eugene/Interview/blob/master/other/Gradle.md) 39 | ### [Kotlin](https://github.com/chen-eugene/Android-Interview/blob/master/other/kotlin.md) 40 | ### [JNI](https://github.com/chen-eugene/Android-Interview/blob/master/other/JNI.md) 41 | 42 | ## 读书笔记 43 | ### [《计算机网络》(第7版 谢希仁)](https://github.com/chen-eugene/Interview/blob/master/note/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E3%80%8A%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E3%80%8B(%E7%AC%AC7%E7%89%88%20%E8%B0%A2%E5%B8%8C%E4%BB%81).md) 44 | ### [《实战Gradle》(第三版)](https://github.com/chen-eugene/Interview/blob/master/note/Gradle%E5%AE%9E%E6%88%98/%E3%80%8AGradle%E5%AE%9E%E6%88%98%E3%80%8B.md) 45 | ### [《Web性能权威指南》](https://github.com/chen-eugene/Android-Interview/tree/master/note/Web%E6%80%A7%E8%83%BD%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97) 46 | ### [《趣谈Linux操作系统》](https://github.com/chen-eugene/Android-Interview/blob/master/note/Linux%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E7%9B%AE%E5%BD%95.md) 47 | 48 | -------------------------------------------------------------------------------- /Android/一、Android基础.md: -------------------------------------------------------------------------------- 1 | #### 1、Serializable和Parcalable的区别。 2 | 3 | **序列化的三种场景:** 4 | - 永久性保存对象,保存对象的字节序列到本地文件中; 5 | - 对象在网络中传递; 6 | - 对象在IPC间传递。 7 | 8 | Parcelable的设计初衷是因为Serializable效率过慢(使用反射),为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在。Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。 9 | - Parcelable的性能比Serializable好,因为后者在反射过程频繁GC,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据。 10 | 11 | Serializable在进行序列化的同时会产生大量的临时对象,比如说Constructor、Method对象,更加消耗性能。 12 | 13 | - 而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化。 14 | 15 | - Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。 16 | 17 | #### 2、Bundle传递数据为什么需要序列化 18 | 19 | 想想Bundle的使用场景,在Intent传递数据的时候会用到Bundle,使用Intent启动一个Activity,那么必然会去通知AMS,这里和AMS的交互涉及到了多进程的交互,进程间传递数据必须进行序列化。 20 | 21 | 22 | #### 3、dpi、ppi、px、pt、dp、sp的区别。 23 | - px(pixel):像素,电子屏幕上组成一幅图画或照片的最基本单元 24 | - pt(point):点,印刷行业常用单位,等于1/72英寸 25 | - ppi(pixel per inch):每英寸像素数,ppi是指屏幕上的像素密度,该值越高,则屏幕越细腻。 26 | 27 | dpi最初用于衡量打印物上每英寸的点数密度。dpi值越小图片越不精细。当dpi的概念用在计算机屏幕上时,就应称之为ppi。同理: PPI就是计算机屏幕上每英寸可以显示的像素点的数量。因此,在电子屏幕显示中提到的ppi和dpi是一样的 28 | 29 | `ppi= 屏幕对角线上的像素点数/对角线长度 = √ (屏幕横向像素点^2 + 屏幕纵向像素点^2)/对角线长度` 30 | 31 | - dpi(dot per inch):每英寸多少点,该值越高,则图片越细腻 32 | - density:屏幕密度,每平方英寸上所包含的像素点。 33 | - dp(dip):Density-independent pixel, 是安卓开发用的长度单位,1dp表示在屏幕像素点密度为160ppi时1px长度 34 | - sp:(scale-independent pixel):安卓开发用的字体大小单位。 35 | ``` 36 | 例如:4.95英寸 1920*1080的手机屏幕 37 | 计算公式: 38 | dpi = √(1920^2 + 1080^2) / 4.95 = 445 39 | density = dpi^2 = 445^2 = 198025 40 | 1dp = (dp * dpi) / 160 = (1 * 445) / 160 = 2.78px 41 | ``` 42 | **Android系统中定义的dpi:** 43 | ![dpi](https://github.com/chen-eugene/Interview/blob/master/image/1539659587(1).png) 44 | 45 | 意思就是:在 1920*1080 分辨率的手机上默认就使用 480 的 dpi ,不管的你的尺寸是多大都是这样,除非厂家手动修改了配置文件 46 | 47 | **在系统中使用的全部都是系统 dpi,没有使用物理 dpi,也获取不到物理 dpi。物理 dpi 主要用于厂家对于手机的参数描述,所以dp能够适配大多数手机,但不能适配所有手机。** 48 | 49 | 50 | 51 | #### 4、应用最多占多少内存。 52 | ``` 53 | 获取app的分配的最大内存 54 | Runtime rt = Runtime.getRuntime(); 55 | long maxMemory = rt.getMaxMemory(); 56 | 57 | ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 58 | activityManager.getMemoryClass(); 59 | activityManager.getLargeMemoryClass(); 60 | ``` 61 | 在设置android:largeheap = "false"时和android:largeheap = "true"是获取到的最大内存存在明显的差异。 62 | 63 | 原则上Google原始OS的默认值是16m,但是各个厂商的OS会对这个值进行修改。 64 | 65 | 可以在/system/build.prop文件中查看内存的配置参数, 66 | ``` 67 | shell@NX510J:/ $ cat /system/build.prop | grep heap 68 | dalvik.vm.heapsize=36m 69 | 70 | dalvik.vm.heapstartsize=8m ----起始分配内存 71 | dalvik.vm.heapgrowthlimit=192m ---- 一般情况app申请的最大内存 dalvik.vm.heapsize=512m ---- 设置largeheap时,App可用的最大内存dalvik.vm.heaptargetutilization=0.75 ---- GC相关 72 | dalvik.vm.heapminfree=512k 73 | dalvik.vm.heapmaxfree=8m ----- GC机制相关 74 | ``` 75 | getMemoryClass()和getLargeMemoryClass()方法最终读取的仍然是dalvik.vm.heapgrowthlimit和dalvik.vm.heapsize的值。 76 | 77 | **设置largeHeap的确可以申请到更多的内存,但是收到dalvik.vm.heapsize的限制,当内存很大时,每次gc将花费更多的时间,性能也会下降。** 78 | 79 | #### [5、了解sp的apply和commit的区别吗,sp具体是怎么实现的,sp什么时候会将内存中的数据写入到文件中。](http://weishu.me/2016/10/13/sharedpreference-advices/) 80 | Android 中的 SharedPreference 是轻量级的数据存储方式,能够保存简单的数据类型,比如 String、int、boolean 值等。其内部是以 XML 结构保存在 /data/data/包名/shared_prefs 文件夹下,数据以键值对的形式保存。 81 | 82 | - 初始化:首次使用SP的时候会通过IO操作把xml文件读取并存入一个map对象中,所以get操作实际上是直接访问的map集合,提高了效率,如果xml不存在就重新创建一个。 83 | 84 | SP 在第一次创建后会维持一个SingleTon,以后都会返回这个实例,更新app时,xml文件并不会被删除。 85 | 86 | - 读数据:首先取得 SharedPreferencesImpl 对象锁,然后同步等待从磁盘加载数据完成,最后返回数据。如果单个 SP 存储的内容过多,将会导致 get 方法的时候阻塞过长,特别是在主线程调用的时候,所以建议在单个SP中尽量少地保存数据。 87 | - 写数据:SP 写入数据的操作是通过 Editor 完成的,一是将数据先写入内存中,即map集合;二是把数据写入到磁盘中。 88 | - commit:同步执行,会阻塞当前线程,有返回值。 89 | - apply:异步执行,没有返回值,推荐使用apply。 90 | 91 |
92 | 93 | #### [6、compileSdkVersion,minSdkVersion,targetSdkVersion的区别](https://www.jianshu.com/p/358a3d42196e) 94 | 95 | - compileSdkVersion:compileSdkVersion是我们告诉Gradle,我们是用哪一版本的Android Sdk去编译程序的,可以使用这个版本的API,比如我们使用的是7.0的版本,compileSdkVersion=24,那么我们对于拍照裁剪图片等功能的操作,就可以使用FileProvider了。 96 | 97 | 我们改变compileSdkVersion的版本号,本质上改变不了我们程序的运行,虽然可能会报错误❌或者警告⚠️,但compileSdkVersion 只会在编译期间起作用,因为环境是compileSdkVersion这个版本的SDK,所以你可以用一些这个版本的API,但是只是IDE给你的便利性帮助而已,帮助你检测代码,避免使用一些弃用的API。 98 | 99 | - minSdkVersion:就是我们的app能够运行的最小版本,如果选择16,那么就是Android 4.1 以及以上的设备才能运行我们app,如果小于这个版本,那么抱歉运行不了,我们不支持。 100 | 101 | 102 | - targetSdkVersion:在 Android 4.4 (API 19)以后,AlarmManager 的 set() 和 setRepeat() 这两个 API 的行为发生了变化。 103 | 104 | 系统通过targetSdkVersion来保证Android的向前兼容性,在Android4.4之后的设备上,系统会判断你的targetSdkVersion是否小于19,如果小于的话,那就按照19之前的api方法,如果大于等于19,那么就按照之后的api方法来走,保证了程序运行的一致性。也就是向前兼容性。 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Android/五.ⅴ、其它.md: -------------------------------------------------------------------------------- 1 | 目录: 2 | #### [1. RecyclerView优化](#RecyclerView优化) 3 | 4 | #### [2. WebView优化](#WebVew优化) 5 | 6 | #### [3. Other优化](#other) 7 | 8 | 9 | ### RecyclerView优化 10 | 11 | #### [1、RecyclerView图片加载错位和闪烁问题。](http://www.codexiu.cn/android/blog/24953/) 12 | 13 | 由于RecycerView的复用机制,导致图片异步加载完成之后,原来的item可能已经被重用,导致item的图片设置错误,一会之后,正确的图片加载完成,这是会显示正确的图片,出现图片闪烁的情况。由于网络的原因,先显示正确的图片,然后原来的图片才加载完成,这时候会出现图片错乱的情况。 14 | 15 | 解决方法: 16 | - 当item还在加载图片的过程中,被移出屏幕可视范围,不需要继续加载这张图片了,可以在onRecycled中取消图片的加载。这样就不会造成图片加载完成设置到其他item的ImageView中了。 17 | - 每一个经过屏幕可视区域的item,加载的图片都要放进缓存中,即使item离开了可视区域,也要加载完毕并放入缓存中,方便下次浏览时能快速加载。每次onBind时对ImageView设置Tag标记,如果Tag标记已经被更改,旧线程加载好的图片不再设置到ImageView中。 18 | 19 | 20 | #### [2、RecyclerView的卡顿优化、资讯流懒加载如何实现。](https://blog.csdn.net/likuan0214/article/details/51911873) 21 | 22 | RecyclerView卡顿的原因: 23 | - item设计复杂,导致inflate时间较长,造成滑动卡顿。 24 | - 图片的加载,图片一般都是用三级缓存,创建图片的IO也会造成滑动的卡顿 25 | 26 | 优化方案: 27 | - 对于复杂的布局可以使用自定义布局来降低复杂度。 28 | - 滑动过程中的图片加载可以使用懒加载的方式。监测reyclerView的滑动状态,在滑动过程中不去加载复杂的布局或者图片,使用占位图,当静止的时候才去加载图片。 29 | 30 | 考虑一种情况,如果只要滚动就去停止加载图片,不管是否快速滚动还是缓慢的滑动,那么只要用户已停止就会去刷新,这样会存在体验上的问题。 31 | 所以优化思路是,通过GestureDetector.OnGetureListener的onFling方法监听RecyclerView的滑动速度。只有在快速滑动的时候才停止加载图片和复杂布局。 32 | 33 | 34 | ### WebVew优化 35 | 36 | #### [1、Native和js交互流程。](https://blog.csdn.net/carson_ho/article/details/64904691) 37 | 38 | - Native调用js: 39 | - 通过WebView的loadUrl() 40 | `mWebView.loadUrl("javascript:callJS()");` 41 | - JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。 42 | - 效率低,会使页面重新刷新,不能获取返回值 43 | - 通过WebView的evaluateJavascript() 44 | ``` 45 | webView.evaluateJavascript("javascript:callJS()", new ValueCallback() { 46 | @Override 47 | public void onReceiveValue(String value) { 48 | //TODO 处理返回结果 49 | } 50 | }); 51 | ``` 52 | - 效率高,但只支持4.4以上版本 53 | 54 | - js调用Native: 55 | - 通过WebView的addJavascriptInterface()进行对象映射 56 | ``` 57 | webView.addJavascriptInterface(new CallNative(), "native"); 58 | 59 | class CallNative { 60 | 61 | @JavascriptInterface 62 | public void jsCallNative() { 63 | 64 | } 65 | 66 | } 67 | ``` 68 | - [在Android4.2以下,存在安全漏洞](https://www.jianshu.com/p/3a345d27cd42) 69 | - 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url 70 | - 操作麻烦,不方便获取返回值。 71 | - 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息 72 | - 使用复杂 73 | 74 | 75 | #### [2、WebView调用原生方法:js调用和原生拦截,你更倾向于哪一个?]() 76 | 77 | 78 | #### 3、如果涉及到多个原生方法,所有方法统一封装进一个WebView和分成多个WebView对应多种方法,你更倾向于哪一个? 79 | 80 | 81 | #### 4、h5设计多层跳转,一个WebView内部实现跳转和每次跳转打开新的WebView,你更倾向于哪一个? 82 | 83 | #### [5、WebView的优化。](https://tech.meituan.com/2017/06/09/webviewperf.html) 84 | [优化方案](https://tech.meituan.com/2017/06/09/webviewperf.html) 85 | 86 | 87 | #### [2、WebView的泄露如何解决。](https://lipeng1667.github.io/2016/08/06/memory-optimisation-for-webview-in-android/) 88 | 89 | 很少发现WebView出现内存泄露的情况,在Android5.1系统中会出现,WebView没有得到及时的释放。 90 | 91 | - 不在xml中使用WebView,而是在Activity中代码创建,并且Context传入ApplicationContext。 92 | 93 | 并不能解决WebView的泄露问题,因为WebView持有Context而造成的Context泄露,而是WebView本身的泄露。 94 | 95 | - 销毁WebView前,先将WebView从View树中移除,然后在销毁。 96 | 97 | ``` 98 | if(mWebView != null){ 99 | ((ViewGroup)mWebView.getParent()).removeView(mWebView); 100 | mWebView.destroy(); 101 | mWebView = null; 102 | } 103 | ``` 104 | 105 | - 单独启动一个进程来进行WebView的相关操作,在结束的时候直接kill掉WebView所在的进程。(没有试过) 106 | 107 | 108 | 109 | ### other 110 | 111 | #### 1、常用的性能优化工具总结。 112 | 113 | - UI分析工具: 114 | - HierarchiViewer 115 | - LayoutInspect 116 | - Android开发者模式GPU过度绘制(Debug GPU Overdraw) 117 | - GPU呈现模式分析(Profile GPU Rendering) 118 | 119 | - CPU分析工具 120 | - TraceView 121 | - Systrace 122 | [手把手教你使用Systrace(一)](https://zhuanlan.zhihu.com/p/27331842) 123 | [手把手教你使用Systrace(二)——锁优化](https://zhuanlan.zhihu.com/p/27535205) 124 | 125 | - 耗电分析 126 | - Battery Historian:耗电分析工具 127 | 128 | - 三方框架 129 | - Leakcanary:内存泄漏监测 130 | - BlockCanary:卡顿监测 131 | 132 | #### 2、 如何进行屏幕适配。 133 | 134 | - [常见屏幕适配方案](https://www.jianshu.com/p/55e0fca23b4f) 135 | - [今日头条屏幕适配方案](https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA) 136 | 137 | #### [3、怎么减小APK大小。](http://wuxiaolong.me/2017/03/19/ReduceAPKSize/) 138 | 139 | - 开启minifyEnable混淆代码。 140 | - 开启shrinkResources去除无用资源。 141 | - 删除未使用的xml和图片:使用Lint分析工具。 142 | - 删除未使用的代码:使用Lint分析工具。 143 | - 使用vector矢量图。 144 | - 使用shape作为背景。 145 | - 使用Tiny压缩图片。 146 | - 配置resConfigs:如果APP支持中文`resConfigs "zh"`。 147 | - 使用微信Android资源混淆工具。 148 | 149 | #### 4、 提高app安全性的方法。 150 | 151 | **数据通信安全:** 152 | - 与服务器数据交互时进行数据加密: 153 | 154 | - 对称加密:AES主要用来对数据本身进行加密,弊端就是密钥存储在app中,app如果被成功破解,数据就暴露了。 155 | - 非对称加密:RSA加密有长度限制,这就导致了RSA加密不能用于所有的数据交互。 156 | - 不可逆加密:比如MD5加密、SHA加密等。所谓的不可逆加密就是,只能单向加密,不能反向解密。MD5把数据加密,最后得到固定长度的16进制编码。这个加密的作用一般是匹配验证,验证某个数据是否改变。 157 | 158 | - 使用令牌: 159 | 160 | - 令牌过期。这个一般是用户长期不登录,服务器设置的过期时间已经到了。 161 | - 令牌错误,一搬是黑客拿未知令牌恶意请求数据。 162 | - 令牌更换,一般是客户端在另一台设备上登录重新获取了最新令牌。 163 | 164 | **app数据存储:** 165 | - 秘钥及敏感信息:此类配置应当妥善存放,不要在类中硬编码敏感信息,可以使用JNI将敏感信息写到Native层。 166 | 167 | - SharePreferences:首先不应当使用SharePreferences来存放敏感信息。存储一些配置信息时也要配置好访问权限,如私有的访问权限 MODE_PRIVATE,避免配置信息被篡改。 168 | 169 | - 数据加密:一些重要数据(用户账号密码等),或者标记存储本地的时候也应该进行加密,或者直接存储hash码,而不能直接存储明文。有些存储本地的数据,比如令牌,就需要进行加密处理。登录成功后的重要信息,如需要存储本地,也需要加密。 170 | 171 | 172 | #### 5、 窗口泄露原因,及解决办法。 173 | 174 | 175 | #### [6、Android如何打印dump信息。](https://www.heqiangfly.com/2017/06/13/android-source-code-analysis-dumpsys/) 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /Android/四.ⅵ、other.md: -------------------------------------------------------------------------------- 1 | #### [1、Android消息循环机制。](http://gityuan.com/android/#33-handler) 2 | 3 | Handler消息机制是由MessageQueue、Message、Looper和Handler共同组成的。 4 | 5 | MessageQueue是一个阻塞队列,当MessageQueue中没有消息的时候,会阻塞掉所在的线程。 6 | 7 | **进程间的通讯方式有Binder/Socket等,而Handler能用于进程通讯吗?** 8 | 9 | Handler是不能用于进程间通讯的,Handler只能用于共享内存地址的两个线程之间的通信,即同进程的两个线程之间的通信。很多时候Handler是工作线程向主线程发送消息,由于工作线程与主线程共享地址空间,即Handler实例位于线程间的共享内存堆上,工作线程和主线程都能使用该实例。工作线程通过`mHandler`想MessageQueue中添加新的Message,主线程一直处于loop循环中,当收到新的Message时按照一定规则分发给相应的`handleMeessage()`方法处理。所以Handler机制的核心是线程间的共享内存空间,而进程间的地址空间是不共享的,所以Handler不能用于进程间通信。 10 | 11 | **消息循环流程**:子线程执行完耗时操作,当Handler发送消息时,将会调用MessageQueue.enqueueMessage,向消息队列中添加消息。当通过Looper.loop开启循环后,会不断地从消息队列中读取消息,即调用MessageQueue.next,然后调用目标Handler(即发送该消息的Handler)的dispatchMessage方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage方法,接收消息,处理消息。 12 | 13 | ![消息循环](https://github.com/chen-eugene/Android-Interview/blob/master/image/handler_java.jpg) 14 | 15 | **MessageQueue,Handler和Looper三者之间的关系**:每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。 16 | 17 | #### 2、关于Handler,在任何地方new Handler都是在什么线程下。 18 | 19 | ``` 20 | public Handler(Callback callback, boolean async) { 21 | mLooper = Looper.myLooper(); 22 | if (mLooper == null) { 23 | throw new RuntimeException( 24 | "Can't create handler inside thread that has not called Looper.prepare()"); 25 | } 26 | mQueue = mLooper.mQueue; 27 | mCallback = callback; 28 | } 29 | ``` 30 | 31 | 通过Handler无参构造方法,默认获取的是当前线程ThreadLocal中的Looper对象,如果获取不到就会抛出异常,所以在子线程中,只要执行了`Looper.prepare()`方法,就可以有效的获取到Looper对象。 32 | 33 | 在默认情况下,new Handler()获取的是当前线程的Looper,Handler对象就在当前线程中。当然也可以通过有参构造方法传入Looper对象,则Handler对象就属于Looper对象所在的线程中。 34 | 35 | 36 | #### 3、在创建了looper的线程中,如果什么都不做会怎么样?这时线程怎么退出,IntentService中也有Handler,它是怎么结束的。 37 | 38 | 再调用了`Looper.prepare(),Looper.loop()`创建了Looper对象并开启loop循环之后,如果什么都不做,那么线程将会一直阻塞在run方法中,不会结束。 39 | 40 | 这时可以在最后一个消息处理完成之后,调用`Looper.quit()`,线程将退出消息循环后结束掉。 41 | 42 | 当创建一个IntentService对象的时候,在其onCreate方法中通过HandlerThread创建并开启了消息循环,将任务移到了子线程中去完成。在handler处理完任务之后将会调用stopself方法结束掉Service,并在onDestory中调用`Looper.quit()`退出消息循环。 43 | 44 | #### [4、主线程中的`Looper.lopp()`一直无限循环检测消息队列中是否有新消息,为什么不会造成ANR?](https://www.jianshu.com/p/cfe50b8b0a41) 45 | 46 | 试想一下,如果`Looper.loop()`不是死循环的话,当主线程处理完了消息之后,线程就会自动退出,应用也就退出了,主线程只能完成一帧界面的渲染之后就会退出,这肯定是符合逻辑的。 47 | 48 | 我们知道,Android是事件驱动的,`Looper.loop()`不断地获取并分发事件,每一帧的绘制、每一次点击都会被当成一个事件存入到`MessageQueue`这个消息队列中去,由`Looper.loop()`来进行统一的分发处理,所以整个代码也是在loop循环里面完成调用的,所以肯定不会阻塞。 49 | 50 | 造成ANR的原因一般有两种: 51 | - 当前的时间没有及时得到处理(主线程正在处理前一个事件,没有及时完成或者looper循环被阻塞了)。 52 | - 当前事件正在处理,但是没有及时完成。 53 | 54 | `Looper.loop()`方法可能会造成主线程的阻塞(当消息队列中没有消息可以处理时),但只要消息循环没有被阻塞,那么就能一直的处理事件而不会引起ANR。ANR的原因是因为loop循环被阻塞了,导致任务没能及时完成。 55 | 56 | #### 5、HandlerThread的使用场景和原理。 57 | 58 | 如果经常要开启线程和销毁线程,这无疑比较消耗性能,HandlerThread可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler+Looper实现的。 59 | 60 | ``` 61 | public void run() { 62 | mTid = Process.myTid(); 63 | Looper.prepare(); 64 | synchronized (this) { 65 | mLooper = Looper.myLooper(); 66 | notifyAll(); 67 | } 68 | Process.setThreadPriority(mPriority); 69 | onLooperPrepared(); 70 | Looper.loop(); 71 | mTid = -1; 72 | } 73 | ``` 74 | HandlerThread的run方法在新线程中创建Looper对象,并进行了关联。 在创建HandlerThread对象并调用其start方法之后,该HandlerThread线程就已经关联了looper对象(通过Looper.prepare()方法关联),并且该线程内部的消息队列循环了起来(通过Looper.loop()方法)。 75 | 76 | 77 | #### 6、Android PendingIntent内部机制。 78 | 79 | 80 | #### [7、Android ART机制,与dalvik的区别,JIT与AOT的区别是什么?ART GC有什么改善,还有什么缺点。](https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/dalvik-art.html) 81 | 82 | **Dalvik:** 83 | 84 | Dalvik是Google公司自己设计用于Android平台的Java虚拟机,支持dex格式(Dalvik Executable)的Java应用程序的运行。dex格式是专门为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。 85 | 86 | Android2.2时添加了JIT,当APP运行时,JIT编译器就会对新类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码,这样在下次执行到相同逻辑的时候,速度就会更快。 87 | 88 | **Dalvik虚拟机和Java虚拟机的区别** 89 | 90 | - Java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码。 91 | - Dalvik可执行文件体积小。Android SDK中有一个叫dx的工具负责将Java字节码转换为Dalvik字节码。 92 | - Java虚拟机与Dalvik虚拟机架构不同。这也是Dalvik与JVM之间最大的区别。Java虚拟机基于栈架构,Dalvik虚拟机基于寄存器架构。 93 | 94 | **优点:** 95 | 96 | - 安装速度快,占用空间小。 97 | 98 | **缺点:** 99 | 100 | - 运行时编译开销大,容易造成卡顿。 101 | 102 | 103 | **ART:** 104 | 105 | 在Android5.0和Android6.0放弃了Dalvik,采用ART。ART在应用安装时字节码就会被编译成指令码,使其成为真正的本地应用,在APP运行时不需要再进行编译,提高了启动速度和运行速度,这一机制叫Ahead-Of-Time (AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。 106 | 107 | **优点:** 108 | 109 | - 系统性能的显著提升。 110 | - 应用启动更快、运行更快、体验更流畅、触摸反馈更及时。 111 | - 更强的电池续航能力。 112 | - 支持更低的硬件。 113 | 114 | **缺点:** 115 | 116 | - 更大的存储空间占用,可能会增加10%-20%。 117 | - 更长的应用安装时间。 118 | 119 | 120 | **AOT+JIT:** 121 | 122 | 在Android7.0采用了AOT+JIT混合模式,揉合了两者的优点。Android 7.0添加了一个JIT编译器与代码分析到ART,JIT编译器是对ART当前的AOT编译器的一个补充,提高运行时性能,节省存储空间,加快应用程序更新和系统更新。 123 | 124 | 在Android 7.0中,安装应用时不再像6.0那样对应用代码进行完整的预编译,而是会根据JIT编译器的分析结果,在设备充电或其余空闲时间对“cold code”进行编译,对于“hot code”,则在实际使用时由JIT进行编译,因此应用安装时间和占用空间大大减少了,像手机淘宝这个APP在Android 7.0系统中的占用大小为156MB,和Android 4.4系统中非常接近。主要的是,它还不会影响到应用的运行速度。 125 | 126 | **优点:** 127 | 128 | - APP安装时间大幅缩短,只有Android 6.0系统的50%。 129 | - 应用占用空间更少,一般会少10%。 130 | - 系统升级OTA速度加快,不再需要优化。 131 | - 降低系统开销,改善电池消耗。 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /note/计算机网络/计算机网络第五章.md: -------------------------------------------------------------------------------- 1 | ### 第五章:传输层 2 | 3 | **要点:** 4 | 5 | - 运输层为相互通信的应用进程提供逻辑通信。 6 | 7 | - 端口和套接字的意义。 8 | 9 | - 面向连接的TCP的特点。 10 | 11 | - 在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议。 12 | 13 | - TCP的滑动窗口、流量控制、拥塞控制和连接管理。 14 | 15 | #### 1、进程之间的通信 16 | 17 | 从通信和信息处理的角度看,**运输层向它上面的应用提供通信服务**,它属于面向通信部分的最高层,同时也是用户功能中的最底层。当网络的边缘部分中的两台主机使用网络的核心部分的功能进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。 18 | 19 | 两台主机进行通信就是两台主机中的**应用进程互相通信**。IP协议虽然能把分组送到目的主机,但是这个分组还停留在主机的网络层,而没有交付主机中的应用进程。从运输层的角度看,**通信的真正端点并不是主机而是主机中的进程**。也就是说,端到端的通信是**应用进程之间的通信**。 20 | 21 | **网络层为主机之间提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信**。 22 | 23 | **运输层还要对收到的报文进行差错检测。网络层只对IP数据报首部进行擦错检测,不检查数据部分。** 24 | 25 | #### 2、运输层的两个主要协议 26 | 27 | **用户数据报协议UDP(User Datagram Protocol)** :UDP在传送数据之前**不需要先建立连接**。远程主机的运输层在收到UDP报文之后,不需要给出任何确认。虽然UDP不提供任何可靠交互,但在某些情况下UDP却是一种最有效的工作方式。 28 | 29 | **传输控制协议TCP(Transmission Control Protocol)** :TCP则**提供面向连接的服务**。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或多播服务。由于TCP要提供可靠的、面向连接的运输服务,因此不可避免地增加了许多开销,如确认、流量控制、计时器以及连接管理等。 30 | 31 | #### 3、软件端口是应用层的各种协议与运输层进行交互的一种地址,区别于硬件端口,硬件端口时不同设备进行交互的接口。 32 | 33 | TCP/IP的运输层用一个16为端口号来标志一个端口。但请注意,**端口号只具有本地意义**,它只是为了标志本计算机应用层的进程在和运输层交互时的接口。在互联网不同计算机中,相同的款口号是没有关联的。 34 | 35 | #### 4、UDP:只在IP的数据报服务之上增加了很少的一点功能,这就是复用和分用的功能以及差错检测的功能。 36 | 37 | - UDP是**无连接的**,即发送数据之前不需要建立连接,因此减少了开销和发送数据之前的时延。 38 | 39 | - UDP使用**尽最大努力交付**,即不保证可靠交付,因此主机不需要维持复杂的连接状态表。 40 | 41 | - UDP时**面向报文的**。即应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 42 | 43 | - **UDP没有拥塞控制**,因此网络出现的拥塞不会使源主机的发送速率降低。 44 | 45 | - **UDP支持一对一、一对多、多对一和多对多的交互通信。** 46 | 47 | - **UDP的首部开销小**,只有8个字节,比TCP的20个字节的首部要短。 48 | 49 | #### 5、TCP的主要特点 50 | 51 | - TCP是**面向连接的运输层协议**。即应用程序在使用TCP协议之前,必须先建立TCP连接。在传输数据完毕之后,必须释放已经建立的TCP连接。 52 | 53 | - 每一条TCP连接只能有两个端点,每一条TCP连接只能是**点对点**。 54 | 55 | - TCP提供**可靠交付**的服务。通过TCP连接传送的数据,无差错、不丢失、不重复,并且按序到达。 56 | 57 | - TCP提供**全双工通信**。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据。 58 | 59 | - **面向字节流**。TCP中的“流”指的是**流入到进程或从进程流程的字节序列**。 60 | 61 | #### 6、TCP把连接作为最基本的抽象。每一条TCP连接有两个端点。这个端点不是主机的IP地址,不是应用进程,也不是运输层的协议端口。TCP连接的端点叫做套接字(socket)[此套接字非彼套接字,套接字有多种含义]。 62 | 63 | **套接字socket = (IP地址:端口号)** 64 | 65 | ![常见的接口](https://github.com/chen-eugene/Interview/blob/master/image/sdgwerjktrreyjk.png) 66 | 67 | #### 7、TCP首部 68 | 69 | - 确认号:占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号。 70 | 71 | **若确认号 = N,则表明:到序号N-1为止的所有数据都已正确收到** 72 | 73 | - 确认ACK(ACKnowledgment):仅当ACK = 1时确认号字段才有效。当ACK = 0时,确认号无效。TCP规定,在连接建立后所有传送的报文段必须把ACK置1。 74 | 75 | - 同步SYN(SYNchronization):在连接建立时用来同步序号。当SYN = 1而ACK = 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在相应的报文段中使SYN = 1和ACK = 1,因此,SYN置为1就表示这是一个连接请求或连接接受报文。 76 | 77 | - 终止FIN(FINis):用来释放一个连接。当FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。 78 | 79 | - 窗口:占2字节,是指从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。窗口值作为接收方让发送方设置其发送窗口的依据。 80 | 81 | #### 8、在计算机网络中的链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏,这种情况就叫做拥塞。 82 | 83 | #### 9、TCP的运输连接管理 84 | 85 | 运输连接有三个阶段:**连接建立**、 **数据传送**和**连接释放**。 86 | 87 | **连接建立**:TCP建立连接的过程叫做握手 88 | 89 | ![握手过程](https://github.com/chen-eugene/Interview/blob/master/image/1541818566(1).png) 90 | 91 | - 最初两端的TCP进程都处于**CLOSED(关闭)** 状态。一开始,B的TCP服务器进程先创建**传输控制块TCB**,准备接受客户进程的连接请求。然后服务器进程处于**LISTEN(收听)** 状态,等待客户的连接请求。 92 | 93 | - A的TCP客户进程也是首先创建**传输控制模块TCB**。然后,在打算建立TCP连接时,向B发出连接请求报文段,这是首部中的同步位 **SYN = 1**,同时选择一个初始序号 seq = x。TCP规定,SYN报文段(即 SYN = 1 的报文段)不能携带数据,但要消耗掉一个序号。这是TCP客户端进入 **SYN-SENT(同步已发送)** 状态。 94 | 95 | - B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时自己选择一个初始序号 seq = y。这个报文段也不能携带数据,但同时要消耗掉一个序号。这是TCP服务器进入 **SYN-RCVD(同步收到)** 状态。 96 | 97 | - A收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号 ack = y + 1,而自己的序号seq = x + 1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq = x + 1。这时,TCP连接已经建立,A进入**ESTABLISHED(已建立连接)** 状态。 98 | 99 | - 当B收到A的确认后,也进入**ESTABLISHED**状态。 100 | 101 | #### 10、为什么A最后还要发送一次确认呢。 102 | 103 | **这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。** 104 | 105 | 假定一种异常情况,即A发出的第一个连接请求报文段没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用报文握手,那么只要B发出确认,新的连接就建立了。 106 | 107 | 由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并以致等待A发来数据。B的许多资源就这样拜拜浪费了。 108 | 109 | 采用三报文握手的办法,可以防止上述现象的发生。例如刚才的异常情况,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。 110 | 111 | #### 11、TCP的连接释放 112 | 113 | ![挥手过程](https://github.com/chen-eugene/Interview/blob/master/image/1541818620(1).png) 114 | 115 | - 数据传输结束之后,通信双发都可以释放连接。现在A和B都处于**ESTABLISHED**状态。A的应用进程先向发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位**FIN**置1,其序号 seq = u,它等于前面遗传送过的数据的最后一个字节的序号加1。这时A进入**FIN-WAIT-1(终止等待1)**状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。 116 | 117 | - B收到连接释放报文段后即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是v,等于B前面已传送过的数据最后一个字节的序号加1.然后B就进入**CLOSED-WAIT(关闭等待)** 状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于**半关闭(half-close)** 状态,即A已经没有数据要求发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一段时间。 118 | 119 | - A收到来自B的确认后,就进入**FIN-WAIT-2(终止等待2)** 状态,等待B发出的连接释放报文段。 120 | 121 | - 若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使 FIN = 1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号 ack = u + 1。这时B就进入**LAST-ACK(最后确认)** 状态,等待A的确认。 122 | 123 | - A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把**ACK置1**,确认号 ack = w + 1,而自己的序号是 seq = u + 1。然后进入到**TIME-WAIT(时间等待)** 状态。请注意,现在TCP连接还没有释放掉。必须经过**时间等待计时器(TIME-WAIT timer)** 设置的时间**2MSL**后,A才进入到**CLOSED**状态。时间**MSL**叫做**最长报文段寿命(Maximum Segment Lifetime)** 。 当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。 124 | 125 | - B只要收到了A发出的确认,就进入CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次的TCP连接。 126 | 127 | #### 12、为什么A在TIME-WAIT状态必须等待2MSL的时间呢? 128 | 129 | - 为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在**LAST-ACK**状态的B收不到对已发送的**FIN + ACK**报文段的确认。B会超时重传这个**FIN + ACK**报文段,而A就能在**2MSL**时间内收到这个重传的FIN + ACK报文段。接着A重传一次确认,重新启动 2MSL 计时器。最后,A和B都正常进入到**CLOSED**状态。如果A在**TIME-WAIT**状态不等待一段时间,而是发送完ACK报文段后就立即释放连接,那么就无法收到B重传的FIN + ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常步骤进入CLOSED状态。 130 | 131 | - 防止已失效的连接请求报文段出现在本连接中。A在发送完最后一个ACK报文段后,在经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。 132 | 133 | 134 | #### 13、TCP的流量控制。 135 | 136 | **流量控制就是让发送方的发送速率不要太快,让接收方来得及接受数据。** 137 | 138 | 139 | #### 14、TCP的拥塞控制。 140 | 141 | 在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等,都是网络的资源。在某段时间若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏,这种情况就叫做**拥塞(congestion)**。 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Android/四.ⅱ、资源管理框架.md: -------------------------------------------------------------------------------- 1 | #### 1、Apk文件解析。 2 | 3 | - META-INF:存放的是签名信息,用来保证apk包的完整性和系统的安全性。 4 | 5 | 在eclipse编译生成一个apk包时,会对所有要打包的文件做一个校验计算,并把计算结果放在META-INF目录下。而在Android平台上安装apk包时,应用管理器会按照同样的算法对包里的文件做校验,如果校验结果与META-INF下的内容不一致,系统就不会安装这个apk。这就保证了apk包里的文件不能被随意替换。比如拿到一个apk 包后,如果想要替换里面的一幅图片,一段代码,或一段版权信息,想直接解压缩、替换再重新打包,基本是不可能的。如此一来就给病毒感染和恶意修改增加了难度,有助于保护系统的安全。 6 | 7 | - classes.dex:java源码编译生成的字节码文件。 8 | - resources.arsc:资源索引文件,用来描述那些就有ID值的资源的配置信息,内容相当于一个资源索引表。 9 | - res:二进制资源文件,除了assets、res/raw目录下的资源和非xml格式的资源被原封不动的打包进Apk之外,其他的资源都会被编译成二进制文件。 10 | 11 | - 二进制格式的xml文件占用控件更小。 12 | - 二进制格式的xml文件解析速度更快。 13 | 14 | - AndroidManifest.xml:二进制格式的AndroidManifest.xml 15 | 16 | #### [2、 Android程序资源分类。](https://blog.csdn.net/luoshengyang/article/details/8738877) 17 | 18 | Android应用程序资源可以分为两大类,分别是assets和res: 19 | 20 | - assets:存放在工程根目录的assets子目录下,它里面保存的是一些原始的文件,可以以任何方式来进行组织。这些文件最终会被原装不动地打包在apk文件中。如果我们要在程序中访问这些文件,那么就需要指定文件名来访问。 21 | ``` 22 | AssetManager am = getAssets(); 23 | InputStream is = am.open("filename"); 24 | ``` 25 | 26 | - res:存放在工程根目录的res子目录下,它里面保存的文件大多数都会被编译,并且都会被赋予资源ID。这样我们就可以在程序中通过ID来访问res类的资源。res类资源按照不同的用途可以进一步划分为以下9种子类型: 27 | - animator:以XML文件保存在res/animator目录下,用来描述属性动画。 28 | - anim:以XML文件保存在res/anim目录下,用来描述补间动画。 29 | - color:以XML文件保存在res/color目录下,用描述对象颜色状态选择子。 30 | - drawable:以XML或者Bitmap文件保存在res/drawable目录下,用来描述可绘制对象。 31 | - layout:以XML文件保存在res/layout目录下,用来描述应用程序界面布局。 32 | - menu:以XML文件保存在res/menu目录下,用来描述应用程序菜单。 33 | - raw:以任意格式的文件保存在res/raw目录下,它们和assets类资源一样,都是原装不动地打包在apk文件中的,不过它们会被赋予资源ID,这样我们就可以在程序中通过ID来访问它们。 34 | ``` 35 | Resource res = getResource(); 36 | InputStream is = res.openRawResource(R.raw.filename); 37 | ``` 38 | - values:以XML文件保存在res/values目录下,用来描述一些简单值。 39 | - xml:以XML文件保存在res/xml目录下,一般就是用来描述应用程序的配置信息。 40 | 41 | **注意:** 42 | - 除了raw类型资源,以及Bitmap文件的drawable类型资源之外,其它的资源文件均为文本格式的XML文件,它们在打包的过程中,会被编译成二进制格式的XML文件。 43 | - 二进制格式的XML文件占用空间更小。 44 | - 二进制格式的XML文件解析速度更快。 45 | 46 | - 每一个res资源在编译的打包完成之后,都会被分配一个资源ID,这些资源ID被终会被定义为Java常量值,保存在一个R.java文件中,与应用程序的其它源文件一起被编译到程序中,这样我们就可以在程序或者资源文件中通过这些ID常量来访问指定的资源。 47 | 48 | **总结:** 49 | ![资源打包](https://github.com/chen-eugene/Interview/blob/master/image/1364831271_1701.jpg) 50 | 51 | - 除了assets和res/raw资源被原装不动地打包进APK之外,其它的资源都会被编译或者处理。 52 | - 除了assets资源之外,其它的资源都会被赋予一个资源ID。 53 | - 打包工具负责编译和打包资源,编译完成之后,会生成一个resources.arsc文件和一个R.java,前者用来描述那些具有ID值的资源的配置信息,相当于是一个资源索引表,后者定义了各个资源ID常量。 54 | - 应用程序配置文件AndroidManifest.xml同样会被编译成二进制的XML文件,然后再打包到APK里面去。 55 | - 应用程序在运行时通过AssetManager来访问资源,或通过资源ID来访问,或通过文件名来访问。 56 | 57 | #### 2、 Android资源加载和打包的机制介绍。一个图片在app中调用R.id.调用后是如何找到的? 58 | ![工具](https://github.com/chen-eugene/Interview/blob/master/image/1544338055(1).png) 59 | 60 | **[资源文件打包过程:](https://blog.csdn.net/luoshengyang/article/details/8744683)** 61 | - 解析AndroidManifest.xml:获得要编译资源的应用程序的包名。 62 | - 添加被引用系统资源包: 63 | 64 | Android系统定义了一套通用资源,这些资源可以被应用程序引用。例如,我们在XML布局文件中指定一个LinearLayout的android:orientation属性的值为“vertical”时,这个“vertical”实际上就是在系统资源包里面定义的一个值。 65 | 66 | - 收集资源文件 67 | 68 | 在编译应用程序资源之前,Android资源打包工具aapt会创建一个AaptAssets对象,用来收集当前需要编译的资源文件。这些需要编译的资源文件就保存在AaptAssets类的成员变量mRes中。 69 | 70 | - 将收集到的资源增加到资源表 71 | 72 | 前面收集到的资源只是保存在一个AaptAssets对象中,这一步需要将这些资源同时增加到一个资源表中去,即增加到前面所创建的一个ResourceTable对象中去,因为最后我们需要根据这个ResourceTable来生成资源索引表,即生成resources.arsc文件。 73 | 74 | - 编译values类资源 75 | 76 | 类型为values的资源描述的都是一些简单的值,如数组、颜色、尺寸、字符串和样式值等,这些资源是在编译的过程中进行收集的。 77 | 78 | - 给Bag资源分配ID 79 | 80 | 类型为values的资源除了是string之外,还有其它很多类型的资源,其中有一些比较特殊,如bag、style、plurals和array类的资源。这些资源会给自己定义一些专用的值,这些带有专用值的资源就统称为Bag资源。例如,Android系统提供的android:orientation属性的取值范围为{“vertical”、“horizontal”},就相当于是定义了vertical和horizontal两个Bag。 81 | 82 | - 编译Xml资源文件 83 | 84 | 除了values类型的资源文件,其它所有的Xml资源文件都需要编译。如layout类型的xml资源。 85 | 86 | - 生成资源符号 87 | 88 | 这里生成资源符号为后面生成R.java文件做好准备的。Android资源打包工具aapt只要遍历每一个Package里面的每一个Type,然后取出每一个Entry的名称,并且根据这个Entry在自己的Type里面出现的次序来计算得到它的资源ID,那么就可以生成一个资源符号了,这个资源符号由名称以及资源ID所组成。 89 | 90 | - 生成资源索引表 91 | 92 | Android资源打包工具aapt生成资源索引表resources.arsc了。 93 | 94 | - 编译AndroidManifest.xml文件 95 | 96 | 应用程序的配置文件AndroidManifest.xml也编译成二进制格式的Xml文件。之所以要在应用程序的所有资源项都编译完成之后,再编译应用程序的配置文件,是因为后者可能会引用到前者。 97 | 98 | - 生成R.java文件 99 | 100 | 前面已经将所有的资源项及其所对应的资源ID都收集起来了,因此,这里只要将直接将它们写入到指定的R.java文件去就可以了。 101 | 102 | - 打包APK文件: 103 | - assets目录。 104 | - res目录,但是不包括res/values目录, 这是因为res/values目录下的资源文件的内容经过编译之后,都直接写入到资源项索引表去了。 105 | - 资源项索引文件resources.arsc。 106 | 107 | **[Apk打包过程:](https://blog.csdn.net/songjinshi/article/details/9059611)** 108 | - 打包资源文件,生成R.java文件:Resource文件(就是工程中res中的文件)、Assets文件、AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、Android基础类库(Android.jar文件)。 109 | - 处理AIDL文件,生成对应的.java文件(当然,有很多工程没有用到AIDL,那这个过程就可以省了)。 110 | - 编译Java文件,生成对应的.class文件:源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件) 111 | - 把.class文件转化成Davik VM支持的.dex文件 112 | - 打包生成未签名的.apk文件 113 | - 对未签名.apk文件进行签名 114 | - 对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的) 115 | 116 | **[资源管理器创建:](https://blog.csdn.net/luoshengyang/article/details/8791064)** 117 | 118 | 在ResourcesManager中,所有的资源对象都被存储在ArrayMap中,首先根据当前的请求参数去查找资源,如果找到了就返回,否则就创建一个资源对象放到ArrayMap中。有一点需要说明的是为什么会有多个资源对象,原因很简单,因为res下可能存在多个适配不同设备、不同分辨率、不同系统版本的目录,按照android系统的设计,不同设备在访问同一个应用的时候访问的资源可以不同,比如drawable-hdpi和drawable-xhdpi就是典型的例子。 119 | 120 | ResourcesManager采用单例模式,这样就保证了不同的ContextImpl访问的是同一套资源,注意,这里说的同一套资源未必是同一个资源,因为资源可能位于不同的目录,但它一定是我们的应用的资源,或许这样来描述更准确,在设备参数和显示参数不变的情况下,不同的ContextImpl访问到的是同一份资源。设备参数不变是指手机的屏幕和android版本不变,显示参数不变是指手机的分辨率和横竖屏状态。也就是说,尽管Application、Activity、Service都有自己的ContextImpl,并且每个ContextImpl都有自己的mResources成员,但是由于它们的mResources成员都来自于唯一的ResourcesManager实例,所以它们看似不同的mResources其实都指向的是同一块内存(C语言的概念),因此,它们的mResources都是同一个对象(在设备参数和显示参数不变的情况下)。在横竖屏切换的情况下且应用中为横竖屏状态提供了不同的资源,处在横屏状态下的ContextImpl和处在竖屏状态下的ContextImpl访问的资源不是同一个资源对象。 121 | 122 | **[资源查找过程:](https://blog.csdn.net/luoshengyang/article/details/8806798)** 123 | 124 | 通过资源Id的形式去获取资源的,那么就会调用Resource的同名函数,而Resource类中所有资源的查找都会先通过getValue去将资源赋值到一个TypeValue中,然后通过对应的方法(比如drawable就是loadDrawable)去加载资源。而在getV内部调用AssetManager的getResourceValue方法。这个方法最终调用一个native方法loadResourceValue去寻找资源。 125 | 126 | loadResourceValue中先获取一个ResTable,再通过ResTable的getResource去获取资源。这个方法是我们寻找资源的核心。 127 | 128 | getResource中会通过资源Id分别获取对应的Package,Type和Entry。然后通过getEntry去获取具体的资源。在getEntry中又回去遍历一个TypeList对象,通过比较不同的Config获取最合适的资源。 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /other/设计模式.md: -------------------------------------------------------------------------------- 1 | #### [1、对象和类之间的相互关系。](http://www.cnblogs.com/duanxz/archive/2012/06/13/2547801.html) 2 | - 继承:带空心箭头的实线线表示。 3 | - 实现:空心箭头和虚线表示。 4 | - 关联(Association):实线箭头表示。 5 | 6 | 特征:表示类与类或类与接口之间的依赖关系,表现为“拥有关系”;具体到代码可以用实例变量来表示。(A类有一个成员变量保存的是B类的一个引用,也就是说由A类可以找到B类)。 7 | 8 | ![关联](https://github.com/chen-eugene/Interview/blob/master/image/1537194464(1).png) 9 | 10 | - 依赖(Dependency):虚线箭头表示。 11 | 12 | 特点:当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种“相识关系”,只在某个特定地方(比如某个方法体内)才有关系。 13 | 14 | ![依赖](https://github.com/chen-eugene/Interview/blob/master/image/1537194978(1).png) 15 | 16 | - 聚合(Aggregation):带空心菱形头表示。 17 | 18 | 特征:属于是关联的特殊情况,体现部分-整体关系,是一种弱拥有关系;整体和部分可以有不一样的生命周期;是一种弱关联。 19 | 20 | ![聚合](https://github.com/chen-eugene/Interview/blob/master/image/1537195015(1).png) 21 | 22 | - 组合(Composition):带实心菱形头的实线表示。 23 | 24 | 特征:属于是关联的特殊情况,也体现了体现部分-整体关系,是一种强“拥有关系”;整体与部分有相同的生命周期,是一种强关联。 25 | 26 | ![组合](https://github.com/chen-eugene/Interview/blob/master/image/1537195039(1).png) 27 | 28 | #### [2、单例模式。](https://blog.csdn.net/inventor1024/article/details/79684053) 29 | ![单例](https://github.com/chen-eugene/Interview/blob/master/image/20180325101500413.jpg) 30 | 31 | #### 3、工具类使用单例和静态内部类有什么区别。 32 | [工具类和单例的区别](https://blog.csdn.net/johnny901114/article/details/11969015) 33 | 34 | 静态类: 35 | - 不维持任何状态,仅仅是提供全局的访问,如java.lang.Math就是用静态方法来实现。 36 | - static class有更好的访问效率,因为static bind发生在编译期。 37 | 38 | 单例: 39 | - 当需要维护状态时,singleTon优于静态类,如单个资源的访问。 40 | - 支持延时加载,对于重量级的对象,延时加载尤为重要。 41 | - singleTon具有面向对象的特性,可以使用继承和多态来进行扩展,如java.lang.Runtime就是一个单例。 42 | 43 | #### [4、创建者模式](https://blog.csdn.net/inventor1024/article/details/79685259) 44 | ![创建者模式](https://github.com/chen-eugene/Interview/blob/master/image/20180325121814300.jpg) 45 | 46 | 使用场景: 创建复杂的对象,如需要创建的对象有很多参数。 47 | 48 | 优点: 49 | - 将配置从目标类中隔离出来,避免了过多的setter方法。常见的实现形式是通过链式调用,例如ImageLoader通过ImageLoaderConfig进行配置,避免了目标类中被过多的接口“污染”。 50 | - 良好的封装性,使用builder构建对象是不必知道其内部的细节。 51 | - 良好的扩展性。 52 | 53 | **Android源码应用:AlertDialog.Builder** 54 | 55 | #### [5、策略模式](https://blog.csdn.net/inventor1024/article/details/79781593) 56 | ![策略模式](https://github.com/chen-eugene/Interview/blob/master/image/20180401225108270.jpg) 57 | 58 | 使用场景: 59 | - 针对同一种问题有不同的解决方式,不同的解决方式之间可以互换,不会造成影响。 60 | - 当出现if-else判断语句来选择具体的行为对象的时候可以考虑策略模式。 61 | 62 | 优点: 63 | - 不同策略之间互不影响,完全可以互换,能够很好的解耦合,扩展性好。 64 | - 具有良好的封装,结构简单清晰,便于维护不同的策略。 65 | 66 | **Android源码应用:动画插值器** 67 | 68 | #### [6、模板方法模式](https://blog.csdn.net/inventor1024/article/details/79858800) 69 | ![模板方法](https://github.com/chen-eugene/Interview/blob/master/image/20180408221356219.jpg) 70 | 71 | 将固定的流程封装到一个final方法中,并且让子类能够定制这个流程中的某些或者所有步骤,父类提取公共代码,提升代码的复用率,提高了程序的扩展性。 72 | 73 | 使用场景:在父类中封装重复的逻辑,存在差异的部分由子类具体实现。 74 | 75 | 优点:良好的扩展性,封装重复逻辑,特定的逻辑由子类来实现。 76 | 77 | **Android源码实现:Activity的生命周期函数、AsyncTask** 78 | 79 | #### [7、代理模式](https://blog.csdn.net/inventor1024/article/details/79921258) 80 | 81 | ![代理模式](https://github.com/chen-eugene/Interview/blob/master/image/20180412224905920.jpg) 82 | 83 | 使用场景: 当不想直接方位或者访问某个对象存在困难的时候就可以通过代理对象来间接的完成访问。 84 | 85 | **[动态代理原理:](https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html)** 86 | - 通过实现 InvocationHandler 接口创建自己的调用处理器。 87 | - 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类。 88 | - 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型。 89 | - 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。 90 | ``` 91 | // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 92 | // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 93 | InvocationHandler handler = new InvocationHandlerImpl(..); 94 | 95 | // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象 96 | Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 97 | 98 | // 通过反射从生成的类对象获得构造函数对象 99 | Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 100 | 101 | // 通过构造函数对象创建动态代理类实例 102 | Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); 103 | ``` 104 | 实际使用过程中,Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程,所以简化后的过程如下 105 | ``` 106 | // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 107 | InvocationHandler handler = new InvocationHandlerImpl(..); 108 | 109 | // 通过 Proxy 直接创建动态代理类实例 110 | Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 111 | new Class[] { Interface.class }, 112 | handler ); 113 | ``` 114 | **Android源码实现:Android的Binder跨进程通信机制和AIDL** 115 | 116 | #### [8、适配器模式](https://blog.csdn.net/inventor1024/article/details/79874218) 117 | 118 | 使用场景: 119 | 1. 对于不兼容类型的接口进行转换。 120 | 2. 在输入有很多种,输出只有一种的情况,可以使用适配器进行统一输出,如ListView的Adapter,不管用户的ItemView是什么类型,getView方法只返回View。 121 | 122 | 123 | - 类适配器:通过多重继承的方式,分别继承目标者接口和被适配者类来实现适配,其中继承目标接口达到适配的目的,继承被适配者类,通过调用被适配者类的方法来实现转换成目标接口的功能。 124 | ![类适配器](https://github.com/chen-eugene/Interview/blob/master/image/20180409230329278.jpg) 125 | - 对象适配器:使用组合的方式,通过继承目标接口来达到适配的目的,同时适配器持有被适配者的引用,以此来调用被适配者的方法来实现接口转换的功能。 126 | ![对象适配器](https://github.com/chen-eugene/Interview/blob/master/image/2018040923163567.jpg) 127 | 128 | **类适配器和对象适配器比较:** 129 | - 对象适配器使用的是组合模式,比类适配器更加灵活。 130 | - 对象适配器中被适配的方法不会暴露出来,类适配器由于继承了被适配对象,因此被适配对象的函数在Adapter类中也含有,导致Adapter被接口污染。 131 | 132 | 优点: 133 | - 良好的复用性:通过适配器模式可以让现有类的功能得到很好的复用。 134 | - 良好的扩展性 135 | 136 | 缺点: 137 | - 过多的使用适配器会让系统非常凌乱,增加使用难度。 138 | 139 | **Android源码应用:ListView的Adapter就是对象适配器,被适配对象为itemView** 140 | 141 | #### [9、装饰者模式](https://blog.csdn.net/inventor1024/article/details/80147571) 142 | 使用场景:需要动态的扩展类的功能,当成继承的一种替代方案。 143 | ![装饰者](https://github.com/chen-eugene/Interview/blob/master/image/20180430170416746.jpg) 144 | 145 | 总结: 146 | 装饰者模式和代理模式很类似,在代理对象中同样持有被代理对象的引用。装饰者模式是继承的一种代替的方案,通常情况下,装饰者应为所装饰的对象进行功能的增强和扩展,如Java的IO操作,而代理模式则是对被代理对象进行控制,不会进行功能上的增强。 147 | 148 | **Android源码应用:Java的IO操作,Android的Context对象** 149 | 150 | #### [10、外观模式](https://blog.csdn.net/inventor1024/article/details/80150912) 151 | 使用场景: 152 | - 当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观模式。 153 | - 外观模式将客户端从一个复杂的子系统中接口。 154 | - 实现外观模式,需要将子系统组合进外观中,然后将具体操作委托给子系统。 155 | ![外观模式](https://github.com/chen-eugene/Interview/blob/master/image/20180430191550814.jpg) 156 | 157 | 优点: 158 | - 隐藏子系统接口,对外提供一个统一的高层接口,减少客户端与子系统之间的耦合,更加便于使用。 159 | 160 | 缺点: 161 | - 外观接口接口膨胀,外观对象包含了过多的子系统接口,一定程度上增加了使用成本。 162 | - 没有遵循开闭原则,当业务出现变更时,可能需要修改外观类。 163 | 164 | **Android源码应用:Android的Context对象** 165 | 166 | #### [11、观察者模式](https://blog.csdn.net/inventor1024/article/details/82958656) 167 | 关键点:观察者模式的重要作用就是解耦,定义对象之间一对多的依赖关系,被依赖对象为Subject,依赖对象为Observer,Subject通知Observer更新状态。 168 | 169 | 使用场景: 170 | - 跨系统的消息交换,如消息队列,事件总线的处理机制。 171 | - 事件多级触发。 172 | - 关联的行为。 173 | ![观察者模式](https://github.com/chen-eugene/Interview/blob/master/image/Main.jpg) 174 | 优点:观察者和被观察者之间依赖于Observer和Observable抽象,实现解耦。 175 | 176 | 缺点:默认的观察者是顺序执行的,当一个观察者卡顿时会影响到整体的效率,可以通过异步回调的方式来解决,这样执行顺序就不能保证。 177 | 178 | **Android源码应用:ListView更新数据,BroadcastReceiver** 179 | 180 | 181 | -------------------------------------------------------------------------------- /Android/五.ⅰ、渲染优化.md: -------------------------------------------------------------------------------- 1 | 2 | 目录: 3 | [1、App启动速度优化](#App启动速度优化) 4 | [2、卡顿问题的检测与分析](#卡顿问题定位和优化) 5 | 6 | 7 | ### [App启动速度优化](https://www.jianshu.com/p/f5514b1a826c) 8 | 9 | #### 1、App启动方式。 10 | 11 | - 冷启动:冷启动指的是应用程序从头开始:系统的进程没有,直到此开始,创建了应用程序的进程。 在应用程序自设备启动以来第一次启动或系统杀死应用程序等情况下会发生冷启动。在这种启动方式下,系统和应用程序比其他启动方式做跟多的工作。 12 | - 温启动:当退出应用时(如back健),该应用的进程会保留在后台。重新启动应用时,进程和Activity需要重新启动,活动的状态被传递到onCreate中。 13 | - 热启动:(home键)系统将应用进程切换至前台,热启动时应用做的工作更少,启动时间更短。 14 | 15 | #### 2、为什么会出现启动白屏。 16 | 17 | 应用在冷启动之前,要执行三个任务: 18 | - 加载并启动App。 19 | - 加载预览窗口,这时会立即展示出一个空白的Window。 20 | - 创建App进程。 21 | 22 | 在这三个任务执行完毕之后马上会执行一下任务: 23 | - 创建Application对象。 24 | - 启动MainThread。 25 | - 创建要启动的Activity对象。 26 | - 加载View。 27 | - 布置屏幕。 28 | - 进行第一次绘制。 29 | 30 | 在App进程完成了第一次绘制之后,系统进程就会用MainActivity的视图替换掉已经展示的Background Window。在这个过程中将会出现白屏(或者黑屏 取决于app的Theme使用的是dark还是light主题)问题。 31 | 32 | #### 3、App启动速度优化方案。 33 | 34 | 分析工具:Systrace+函数插桩 35 | 36 | ``` 37 | class Trace { 38 | public static void i(String tag) { 39 | Trace.beginSection(name); 40 | } 41 | public static void o() { 42 | Trace.endSection(); 43 | } 44 | } 45 | ``` 46 | 47 | 白屏问题不可避免,但是可以尽可能的缩短白屏的时间,提高用户体验。 48 | 49 | 不同应用中,App进程的创建等环节不能控制,可以优化的也就是Application、Activity的创建的回调过程。所以优化方向如下: 50 | 51 | - 闪屏优化: 52 | - 预览窗口:系统在启动app时,会根据Theme主题创建预览窗口。如果我们禁用预览窗口或者将预览窗口指定为透明,那么用户在这段时间依然看到的是桌面。 53 | 54 | 我们可以自定义默认展示Window的主题,快速展示出来一个界面,给用户快速反馈的体验。或者直接将预览窗口是线程闪屏的效果,这要用户在很短的时候就可以看到“预览闪屏”。 55 | 56 | - 避免首屏UI过于复杂:尽量保持布局层级的扁平化,避免出现重复的嵌套布局。 57 | 58 | - 业务上的优化: 59 | 60 | - 避免在主线程中做耗时的初始化操作(Heavy app initialization)。 61 | - 异步初始化:不阻塞主线程。在异步初始化化的时候可以使用`new Thread()`来代替线程池,应为初始化是单任务的,采用线程池更消耗性能。 62 | - 延迟初始化:异步初始化可能存在在WorkThread中使用的时候还未初始化完成的情况,所以可以在Application的onCreate方法中延时初始化三方组件,将初始化延迟到使用前。 63 | 64 | 如友盟、Bugly、BlockCanary等可以放到线程中去初始化。地图定位和自有统计可以延时一定的时间,如延时3秒。ImageLoader可以延迟到SplashActivity初始化。EventBus因为再Activity中使用所以必须在Application中初始化。 65 | 66 | - 避免UI阻塞:在初始化的时候做一些短耗时操作,如I/O操作、反序列化、网络操作等。 67 | 68 | 69 | ### 卡顿问题定位和优化 70 | 71 | #### 4、卡顿问题的分析和定位。 72 | 73 | Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,,否则会发生丢帧的现象,丢帧越多,用户明确感到卡顿。 74 | 75 | **[卡顿问题监测和分析工具](https://www.jianshu.com/p/17b0ac9b40fa)** 76 | - HierarchyViewer、LayoutInspect:分析Layout的层次关系。 77 | - AndroidStudio Profiler 78 | - [StrictMode](https://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/):严格模式,用来检测程序中违例情况的开发者工具。最常用的场景就是检测主线程中本地磁盘和网络读写等耗时的操作。系统检测出主线程违例的情况会做出相应的反应,如日志打印,弹出对话框亦或者崩溃等。换言之,严格模式会将应用的违例细节暴露给开发者方便优化与改善。 79 | - 三方库:BockCanary、ANRWatchDog 80 | - 三方库:Hugo,可以输出方法的具体执行时间。 81 | - [Systrace](https://www.jianshu.com/p/75aa88d1b575):只能监控特定系统调用的耗时情况,并且开发非常的低。我们可以通过编译时给程序进行插桩的方式这样就能够监控我们程序代码的耗时情况。 82 | - [TraceView](https://www.jianshu.com/p/b492140a555f):它可以查看整个过程的函数调用情况,但是本身工具的开销过大,分析的结果可能存在出入。 83 | 84 | **造成页面卡顿的原因:** 85 | - UI布局方面: 86 | - 过度绘制(Overdraw):可以通过手机的GPU Overdraw选项来直接查看过度绘制,对于自定义控件被遮挡的部分可以使用`canvas.clipRect()`只绘制展示出来的部分。 87 | - UI布局过于复杂:HierarchyViewer和LayoutInspect可以分析UI布局的层次关系,尽量保持布局层级的扁平化,避免出现重复的嵌套布局。 88 | 89 | - 主线程阻塞:TraceView和Systrace可以具体定位具体的耗的方法。 90 | - [不要滥用SharedPreferences](http://weishu.me/2016/10/13/sharedpreference-advices/) 91 | - 避免主线程I/O操作,使用子线程来处理耗时操作或阻塞任务。 92 | 93 | - 内存不足也会导致卡顿,内存不足导致GC频繁。 94 | 95 | #### 5、BlockCanary原理。 96 | 97 | 利用了Looper.loop()中每个Message被分发前后的Log打印,而我们设置自己的Printer就可以根据Log的不同的处理: 98 | ``` 99 | public static void loop() { 100 | final Looper me = myLooper(); 101 | 102 | final MessageQueue queue = me.mQueue; 103 | for (;;) { 104 | Message msg = queue.next(); // might block 105 | // This must be in a local variable, in case a UI event sets the logger 106 | Printer logging = me.mLogging; 107 | if (logging != null) { 108 | logging.println(">>>>> Dispatching to " + msg.target + " " + 109 | msg.callback + ": " + msg.what); 110 | } 111 | // focus 112 | msg.target.dispatchMessage(msg); 113 | 114 | if (logging != null) { 115 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 116 | } 117 | } 118 | msg.recycleUnchecked(); 119 | } 120 | } 121 | 122 | public class BlockDetectByPrinter { 123 | 124 | public static void start() { 125 | 126 | Looper.getMainLooper().setMessageLogging(new Printer() { 127 | 128 | private long startTime; 129 | private long blockThreshold; 130 | private long startedPrinting; 131 | 132 | @Override 133 | public void println(String x) { 134 | if (!startedPrinting) { 135 | mStartTimeMillis = System.currentTimeMillis(); 136 | startedPrinting = SystemClock.currentThreadTimeMillis(); 137 | mStartedPrinting = true; 138 | } else { 139 | final long endTime = System.currentTimeMillis(); 140 | startedPrinting = false; 141 | //判断是否为卡顿 142 | if (isBlock(endTime)) { 143 | StringBuilder sb = new StringBuilder(); 144 | //打印栈信息 145 | StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace(); 146 | for (StackTraceElement s : stackTrace) { 147 | sb.append(s.toString() + "\n"); 148 | } 149 | } 150 | } 151 | } 152 | }); 153 | } 154 | 155 | private boolean isBlock(long endTime) { 156 | return endTime - startTime > blockThreshold; 157 | } 158 | 159 | } 160 | ``` 161 | 可以通过 `Looper.getMainLooper().setMessageLogging(mainLooperPrinter);` 162 | 并在mainLooperPrinter中判断start和end,来获取主线程dispatch该message的开始和结束时间,并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生,并dump出各种信息。 163 | 164 | #### [6、ANR的原理。](http://gityuan.com/2016/07/02/android-anr/) 165 | 166 | ANR(Application Not responding):ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。 167 | 168 | 造成ANR的场景: 169 | - Service Timeout:前台服务20秒内,后台服务在200秒内没有执行完毕。 170 | - BroadcastQueue Timeout:在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。 171 | - ContentProvider Timeout:ContentProvider的publish在10s内没进行完。 172 | - InputDispatching Timeout: 5秒内无法响应屏幕触摸事件或键盘输入事件。 173 | 174 | **[原理分析:](http://gityuan.com/2016/07/02/android-anr/)** 175 | 176 | 在启动Service的同时会向AMS的消息队列中添加一个超时的定时任务,然后在Service的onCreate方法回调完成之后AMS移除队列中的超时任务,如果没有及时移除,就会执行超时任务,触发ANR。 177 | 178 | 179 | #### [7、ANR信息收集过程。](https://www.jianshu.com/p/6d855e984b99) 180 | 181 | 只要触发ANR,最终都会调用`AMS.appNotResponding()`。 182 | 183 | 获取重要进程的trace信息,并保存到`/data/anr/traces.txt`,只会保留最后一次的ANR信息。 184 | 185 | [ANR日志的分析和上传](https://codezjx.com/2017/08/06/anr-trace-analytics/) 186 | 187 | **如何去监控应用的ANR异常呢?** 188 | 189 | - 通过使用FileObserver监听/data/anr/traces.txt文件的变化。 190 | 191 | traces.txt文件中只保存着异常进程的信息,并没有记录ANR的Cause reason,只能重logcat日志输出中去获取。获取方式就是通过`ActivityManagerService.getProcessesInErrorState()`来获取,并进行过滤,筛选出ANR导致的异常信息。 192 | 193 | 由于权限的原因,某些高版本的ROM没有读取traces.txt文件的权限。 194 | 195 | - 通过监控消息队列的运行时间,这个方法不能准确的判断出是否真出现了ANR,也不能得到完整的ANR日志,一般用于监测卡顿。 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /Android/四.ⅰ、Binder.md: -------------------------------------------------------------------------------- 1 | ![Android系统](https://github.com/chen-eugene/Android-Interview/blob/master/image/android-boot.jpg) 2 | 3 | [Android系统架构](http://gityuan.com/android/) 4 | 5 | - Loader层: 6 | - Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在ROM里的预设出代码开始执行,然后加载引导程序到RAM; 7 | - Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。 8 | 9 | - Kernel层: 10 | - 启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作; 11 | - 启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。 12 | - kthreadd进程是所有内核进程的鼻祖。 13 | 14 | - Native层:这里的Native层主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程。 15 | - init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程; 16 | - init进程还启动servicemanager(binder服务管家)、bootanim(开机动画)等重要服务 17 | - init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程),Zygote是所有Java进程的父进程,Zygote进程本身是由init进程孵化而来的。 18 | - init进程是所有用户进程的鼻祖。 19 | 20 | - Framework层: 21 | - Zygote进程,是由init进程通过解析init.rc文件后fork生成的,Zygote进程主要包含: 22 | - 加载ZygoteInit类,注册Zygote Socket服务端套接字; 23 | - 加载虚拟机; 24 | - preloadClasses; 25 | - preloadResouces。 26 | - System Server进程,是由Zygote进程fork而来,System Server是Zygote孵化的第一个进程,System Server负责启动和管理整个Java framework,包含ActivityManager,PowerManager等服务。 27 | - Media Server进程,是由init进程fork而来,负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service,等服务。 28 | 29 | - App层: 30 | - Zygote进程孵化出的第一个App进程是Launcher,这是用户看到的桌面App。 31 | - Zygote进程还会创建Browser,Phone,Email等App进程,每个App至少运行在一个进程上。 32 | - 所有的App进程都是由Zygote进程fork生成的。 33 | 34 | 35 | ******** 36 | 37 | 38 | #### 1、Android进程的优先级。 39 | 40 | - ① 前台进程:一般前台进程都不会因为内存不足被杀死。 41 | 42 | - 拥有正在与用户交互的activity(已调用onResume)。 43 | - 拥有某个绑定到用户正在交互的Activity的Service。 44 | - 拥有正在“前台”运行的Service(调用了startFofeground)。 45 | - 拥有正在整形一个生命周期回调的Service(onCreate、onStart、onDestroy)。 46 | - 拥有正在执行其onReceive方法的BroadcastReceiver。 47 | 48 | - ② 可见进程:没有任何前台组件,但仍会影响用户在屏幕上所见内容的进程。除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。 49 | 50 | - 拥有不在前台、但仍对用户可见的 Activity(已调用 onPause())。 51 | - 拥有绑定到可见(或前台)Activity 的 Service。 52 | 53 | - ③ 服务进程:尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。 54 | 55 | - 正在运行 startService() 方法启动的服务,且不属于上述两个更高类别进程的进程。 56 | 57 | - ④ 后台进程:后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 58 | 59 | - 对用户不可见的 Activity 的进程(已调用 Activity的onStop() 方法)。 60 | 61 | - ⑥ 空进程:保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 62 | 63 | - 不含任何活动应用组件的进程。 64 | 65 | #### 2、进程间的通信方式。 66 | 67 | - Messenger: 68 | - 简单,不需要编写AIDL文件。 69 | - Messenger会把所有的请求派入队列,可以不用担心并发问题。 70 | 71 | - AIDL: 72 | - 可以满足并发的需求。 73 | - Messenger只能通过Message来传递信息,有局限性,ADIL可以直接调用Server的方法。 74 | 75 | **aidl工具生成了三个类:IXXX、IXXX.Stub和IXXX.Stub.Proxy** 76 | - IXXX:实现了android.os.IInterface,定义了通信接口。 77 | 78 | - IXXX.Stub:抽象类,继承了android.os.Binder,在服务端实现相应的接口,在客户端调用该类的静态方法`asInterface`获取一个Binder对象。如果Client和Server在同一个进程中,就直接返回服务端的Binder,接下来的调用就是直接用服务端的Binder调用服务端的程序,不存在IPC。否则的话,就将该IBinder(其实是BinderProxy类型了)包装成一个新的类Proxy类,接下来调用Proxy的stract方法实质上是用的Binder驱动中的远程Binder的引用mRemote来调用的,是IPC。 79 | 80 | - IXXX.Stub.Proxy:IBinder的代理类(其实是BinderProxy类型了),在IPC中,Proxy的stract方法实质上是用的Binder驱动中的远程Binder的引用mRemote来调用的。 81 | 82 | **[AIDL中的in,out,inout:](https://blog.csdn.net/luoyanglizi/article/details/51958091)** 83 | 84 | - 所有的非基本参数都需要一个定向tag来指出数据流通的方式,不管是 in , out , 还是 inout 。基本参数的定向tag默认是并且只能是 in 。 85 | 86 | - AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。 87 | 88 | - 数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。 89 | 90 | #### [3、什么是内存映射?](https://blog.csdn.net/mg0832058/article/details/5890688) 91 | 92 | [在搞清楚内存映射之前,先弄清楚什么是:虚拟内存、虚拟内存地址、物理内存、物理内存地址。](https://www.cnblogs.com/curtful/archive/2012/02/16/2354496.html) 93 | 94 | 正在运行的一个进程所需要的内存有可能大于内存条的容量(比如内存条是256M,程序却要创建1G的数据区域),这时把程序所有的数据都加载到内存中肯定是不现实的。所以不是所有的数据都能一起加载到内存(物理内存)中,势必有一部分数据要放到其它介质中(如硬盘),待进程需要访问那部分数据时,再通过调度进入物理内存。所以,虚拟内存是进程运行时所有内存空间的总和,并且可能有一部分不在物理内存中,而物理内存就是我们平时所了解的内存条。 95 | 96 | 虚拟内存地址:假设计算机是32位的,那么它的地址总线就是32位,寻址空间0—0xFFFFFFFF(4G)的地址空间。 97 | 物理内存地址:256M的物理内存的寻址空间为0—0x0FFFFFFF(256M)。 98 | 99 | **内存映射:** 100 | 101 | 指的是硬盘上的文件位置与进程逻辑地址空间中一块大小相同的区域之间的一一对应关系。在内存映射过程中,没有实际的数据拷贝,文件没有被载入内存中,只是在逻辑上被放入了内存。内存映射是逻辑上关系,物理上不存在这种映射关系。映射过程是通过`mmap()`来实现的。 102 | 103 | `mmap()`函数会返回一个指针ptr,指向进程逻辑地址空间中的一个地址,这样就不需要先将文件加载进内存当中,二是直接通过ptr操作文件。 104 | 105 | ptr指向的是一个逻辑地址,要操作其中的数据必须通过MMU将逻辑地址转换成物理地址,如果MMU在地址映射表中无法找到与ptr对应的物理地址,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中,如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上。 106 | 107 | 从硬盘上将文件读入内存,都要经过文件系统将文件先拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,这个过程进行了两次数据拷贝。 108 | 109 | 内存映射通过`mmap()`将文件直接映射到用户空间,中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了一次拷贝,效率更高效。 110 | 111 | 112 | #### [4、Binder是什么?它是如何实现跨进程通信的?(详细解释Binder机制。)](https://blog.csdn.net/carson_ho/article/details/73560642) 113 | 114 | - 从机制、模型角度来说:Binder是一种跨进程(IPC)的方式,即Binder机制模型。 115 | - 从模型的结构、组成来说:Binder是一种虚拟的物理设备驱动,即Binder驱动。连接Server进程、Client进程和ServerManager进程。 116 | - Android源码角度:Binder是一个类,实现了IBinder接口。 117 | 118 | 相比较于其他的IPC方式,如管道、SystemV、Socket等: 119 | - Binder相对于传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。 120 | - 传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支持对通信双方做身份校检,从而大大提升了安全性。 121 | 122 | ![binder](https://github.com/chen-eugene/Interview/blob/master/image/20181206225009.png) 123 | 124 | Binder架构包括Server、Client和Binder驱动三个部分。 125 | - Binder服务端:使用服务的进程。 126 | 127 | - Binder客户端:提供服务的进程。 128 | 129 | - Binder驱动:连接Server进程、Client进程和ServerManager进程的桥梁。 130 | - 传递进程间的数据:通过内存映射的方式。 131 | - 实现线程控制:采用Binder的线程池,并由Binder驱动自身进行管理。 132 | 133 | - Service Manager:是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力。 134 | 135 | **Binder驱动工作流程:** 136 | - ① Binder驱动创建一块接受缓存区。 137 | - ② 实现地址映射关系:即根据需要映射的接收进程信息,实现内核缓存区和接受进程用户空间地址同时映射到同一个共享接收缓存区中。 138 | - ③ 发送进程通过系统调用copy_from_user发送数据到虚拟内存区域(数据拷贝1次)。 139 | - ④ 由于内核缓存区和接收进程的用户空间地址存在映射关系(同时映射Binder创建的接收缓存区中),故相当于也发送到了接收进程的用户空间地址,即实现了跨进程通信。 140 | 141 | **优点:** 142 | - 传输效率:数据拷贝次数少(1次)、用户控件和内核空间可直接通过共享对象直接交互。 143 | - 为接收进程分配了不确定大小的接收缓存区。 144 | 145 | **Binder请求的线程管理:** 146 | - Server进程会创建很多线程来处理Binder请求。 147 | - Binder模型的线程管理采用Binder驱动的线程池,并由Binder驱动自身进程管理,而不是由Server进程来管理的。 148 | - 一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。 149 | 所以,在进程间通信时处理并发问题时,如使用ContentProvider时,它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程同时工作。 150 | 151 | **Binder机制情景分析:** 152 | - [Service Manager是如何成为一个守护进程的?即Service Manager是如何告知Binder驱动程序它是Binder机制的上下文管理者。](https://blog.csdn.net/luoshengyang/article/details/6621566) 153 | Service Manger、Client和Server三者分别是运行在独立的进程当中,它们之间的通信也属于进程间通信了,而且也是采用Binder机制进行进程间通信,因此,Service Manager在充当Binder机制的守护进程的角色的同时,也在充当Server的角色,是一种特殊的Server。 154 | 155 | - [Server和Client是如何获得Service Manager接口的?即defaultServiceManager接口是如何实现的。](https://blog.csdn.net/luoshengyang/article/details/6627260) 156 | 157 | - BpServiceManager:p是proxy即代理的意思,Bp就是BinderProxy,BpServiceManager,就是SM的Binder代理。既然是代理,那肯定希望对用户是透明的。对应的是C层的Binder代理。 158 | 159 | - [Server是如何把自己的服务启动起来的?Service Manager在Server启动的过程中是如何为Server提供服务的?即IServiceManager::addService接口是如何实现的。](https://blog.csdn.net/luoshengyang/article/details/6629298) 160 | 161 | - [Service Manager是如何为Client提供服务的?即IServiceManager::getService接口是如何实现的。](https://blog.csdn.net/luoshengyang/article/details/6633311) 162 | 163 | #### [5、如何实现一个自定义的系统级服务。](https://blog.csdn.net/luoshengyang/article/details/6642463) 164 | SystemServer对象是在系统启动的时候创建的,它被创建的时候会启动一个线程来创建HelloService,并且把它添加到Service Manager中去。 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /Java/一、Java基础.md: -------------------------------------------------------------------------------- 1 | #### 1、面向过程、面向对象和面向切面编程的区别和特点。 2 | 3 | - 面向过程:一种以过程为中心的编程思想。以正在发生的为主要目标进行编程。与面向对象的最大区别就是没有面向对象中对象的概念。 4 | - 优势:性能高于面向对象,因为类调用时需要实例化,开销比较大,比较消耗资源。在单片机、嵌入式开发、Linux/Unix等注重性能领域一般采用面向过程开发。 5 | - 缺点:没有面向对象易于维护、良好的复用性和易于扩展的优点。 6 | 7 | - 面向对象(OOP):提倡的是将功能模块化,对象化,面向对象把所有的事物都当做对象看待,因此每一个对象都有自己的生命周期,都是一个封装的整体。 8 | - 优势:每一个对象都有自己的一套垂直的系列方法和属性,使得我们使用对象的时候不需要太多的关系它的内部细节和实现过程,只需要关注输入和输出。 9 | - 缺点:并不是所有的问题都能够完美的划分到模块中,不可避免的会出现重复代码。 10 | 11 | - 面型切面(AOP):把程序逻辑分解成**关注点**,基于AOP的编程可以让我们横向的切割某一类方法和属性(不需要关心他是什么类别!),提倡的是针对同一类问题的统一处理。这意味着,在 AOP 中,我们不需要显式的修改就可以向代码中添加可执行的代码块。 12 | - 优势:弥补了OOP的不足,极大程度的降低了重复代码,提高了代码的复用率和开发效率。 13 | 14 | #### 2、面向对象的三大特征:封装、继承、多态。 15 | 16 | - 封装:把一个对象的属性私有化,只提供一些可以被外界访问的属性的方法。 17 | - 继承:在已经存在的类的基础上创建新类,新类的定义可以增加新的数据或新的功能,也可以用父类的功能。 18 | - 多态:引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。 19 | 20 | 实现多态有两种方式:继承和实现接口。 21 | 22 | 23 | #### 3、什么是JDK,什么是JRE,什么是JVM。 24 | 25 | - JDK:给开发者提供的开发工具,它除了包括完整的JRE(Java Runtime Environment),Java运行环境,还包含了其他供开发者使用的工具包。 26 | - JRE:Java运行环境,用于运行java程序。 27 | - JVM:当运行一个程序时,JVM 负责将字节码转换为特定机器代码。JVM 提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统时的Java具有跨平台的特性。 28 | 29 | #### 4、字符串字面量和new String()有什么区别。 30 | 31 | - 字符串字面量:String str = “abc”;可能创建一个或者不创建对象,如果”abc”在字符串池中不存在,会在java字符串池中创建一个String对象(”abc”),然后str指向这个内存地址,无论以后用这种方式创建多少个值为”abc”的字符串对象,始终只有一个内存地址被分配。 32 | - String str = new String(“abc”);至少会创建一个对象,也有可能创建两个。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在”abc”,则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。 33 | 34 | 35 | #### 5、 String、StringBuffer和StringBuilder的区别。 36 | 37 | String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响。 38 | 39 | StringBuffer、StringBuilder在每次修改的时候是对自身对象进行操作,而不是生成新的对象,再改变对象引用。 40 | 41 | 在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了StringBuffer对象的拼接,所以这些时候 String 对象的速度并不会比StringBuffer对象慢,而特别是以下的字符串对象生成中。如: 42 | `String S1 = “This is only a” + “ simple” + “ test” ` 43 | 但是如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: 44 | ``` 45 | String S2 = “This is only a”; 46 | String S3 = “ simple”; 47 | String S4 = “ test”; 48 | String S1 = S2 +S3 + S4; 49 | ``` 50 | 51 | StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。 52 | 53 | StringBuilder是Java5新增的一个StringBuffer的替代类,它不是线程安全的。 54 | 55 | #### 6、在静态方法中能不能调用非静态的成员变量。(能不能覆盖一个static的方法) 56 | 57 | static关键字:在JVM加载一个类的时候,若该类存在static修饰的成员变量和成员方法,则会为这些成员变量和成员方法在固定的位置开辟一个固定大小的内存区域,有了这些“固定”的特性,那么JVM就可以非常方便地访问他们。 58 | 59 | 对象的创建是在运行时创建的,而static所修饰的属性和方法是在编译时创建的,所以静态方法不能访问非静态属性,同时也不能覆盖一个static的方法,两者概念都不一样。 60 | 61 | 简单来说static修饰的成员和方法属于类而不属于对象。 62 | 63 | #### 7、抽象类和接口的区别。 64 | 1.语法层面上的区别 65 | 66 |   1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法; 67 | 68 |   2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的; 69 | 70 |   3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法; 71 | 72 |   4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。 73 | 74 | 2.设计层面上的区别 75 | 76 |   1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。 77 | 78 |   2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。 79 | 80 | #### 8、 equals方法的作用是什么,它和==有什么区别。 81 | 82 | - "==":作用是判断两个对象的地址是不是相等,即两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)。 83 | - equals:作用时判断两个对象是否相等。默认情况下,调用的是Object中的equals方法,等价于“==”,即比较两个对象的地址是否相等。通常情况下会覆盖equals方法。 84 | 85 | #### 9、 hashCode方法的作用是什么,它和equals方法有什么联系。 86 | 87 | - hashCode():作用是获取哈希码,也叫做散列码。将任意长度的数据映射成一个固定长度的值。 88 | 89 | 在用到hash进行管理的数据结构中,比如hashmap,hash值存在的目的是加速键值对的查找。 90 | 91 | - hash碰撞:对于两个任意的数据,可能生成相同的hash值,但这种的可能性很小。 92 | 93 | - hashcode()和equals(): 94 | - 如果两个对象相等,则hashcode一定也是相同的。 95 | - 两个对象相等,对两个对象分别调用equals方法都返回true。 96 | - 两个对象有相同的hashcode值,它们不一定是相等的。 97 | - 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。 98 | 99 | 100 | 对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。 101 | 102 | 为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在) 103 | 104 | 也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。 105 | 106 | #### 10、可以直接根据hashcode值判断两个对象是否相等吗? 107 | 108 | 肯定是不可以的,因为不同的对象可能会生成相同的hashcode值,即所谓的hash碰撞。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等,如果两个对象的hashcode值不等,则必定是两个不同的对象。如果要判断两个对象是否真正相等,必须通过equals方法。 109 | 110 | - 也就是说对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等; 111 | - 如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同; 112 | - 如果两个对象的hashcode值不等,则equals方法得到的结果必定为false; 113 | - 如果两个对象的hashcode值相等,则equals方法得到的结果未知。 114 | 115 | 在重写equals方法的同时,必须重写hashCode方法,因为默认情况下,hashCode方法是将对象的存储地址进行映射。 116 | 117 | #### 11、fianl关键字的作用。 118 | 119 | - 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。 120 | - 当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。 121 | - 使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。 122 | 123 | #### [12、什么是序列化,序列化前后对象有何区别。](https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html) 124 | 125 | 序列化:表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。 126 | 127 | - **虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否相同,更重要的是两个类的序列化 ID 是否相同(serialVersionUID)。** 128 | - **序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。** 129 | - 要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就需要有默认的无参的构造函数。 130 | - Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。 131 | - 在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。基于这个原理,可以在实际应用中得到使用,用于敏感字段的加密工作。 132 | - Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。 133 | 134 | #### [13、序列化Serializable和Parcelable的区别。](https://blog.csdn.net/SilenceOO/article/details/73469237) 135 | 136 | - Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。 137 | - Parcelable的性能比Serializable好,因为后者在反射过程频繁GC,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据。 138 | - Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化。 139 | - Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。 140 | 141 | #### [14、try catch finally,try里有return,finally还执行么。](http://www.cnblogs.com/lanxuezaipiao/p/3440471.html#top) 142 | 总结: 143 | - try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。 144 | - 在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。 145 | - finally语句在return语句执行之后return返回之前执行的。 146 | - finally块中的return语句会覆盖try块中的return返回。 147 | - 如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变。 148 | - try块里的return语句在异常的情况下不会被执行,这样具体返回哪个看情况。 149 | - 当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。 150 | 151 | #### [15、Exception与Error类结构,Exception与Error的区别。(Java异常分类)](https://blog.csdn.net/hguisu/article/details/6155636) 152 | ![异常分类](https://github.com/chen-eugene/Interview/blob/master/image/1354020417_5176.jpg) 153 | 154 | - Error:是程序无法处理的错误,表示运行应用程序中较严重问题,如ava虚拟机运行错误(Virtual MachineError)、内存溢出(OutOfMemoryError)等,这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时。 这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。 155 | 156 | - Exception(异常):是程序本身可以处理的异常。 157 | - 运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。 158 | - 非运行时异常 (编译异常):是RuntimeException以外的异常,如IOException、SQLException,类型上都属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。 159 | 160 | 在以下4种特殊情况下,finally块不会被执行: 161 | - 在finally语句块中发生了异常。 162 | - 在前面的代码中用了System.exit()退出程序。 163 | - 程序所在的线程死亡。 164 | - 关闭CPU。 165 | 166 | 167 | -------------------------------------------------------------------------------- /常用框架.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | | 项目 | 简介 | 链接 | 4 | | :-------- | :--------| :------ | 5 | | FFmpegAndroid | FFmpeg Demo | https://github.com/xufuji456/FFmpegAndroid| 6 | | FFmpeg-Android | FFmpeg Demo | https://github.com/bravobit/FFmpeg-Android | 7 | | WeaponApp |MVVM+Retrofit+RxJava+Small|https://github.com/G-Joker/WeaponApp| 8 | | Android-ZBLibrary | MVP快速开发框架 | https://github.com/TommyLemon/Android-ZBLibrary| 9 | | GSYVideoPlayer | 基于ijkplayer的播放器 | https://github.com/CarGuo/GSYVideoPlayer | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 |
类别框架简介链接
网络访问
OkHttp3.xhttps://github.com/square/okhttp
Retrofit2.xhttps://github.com/square/retrofit
IO操作
Okio1.xhttps://github.com/square/okio
屏幕适配
AndroidScreenAdaptationhttps://github.com/yatoooon/AndroidScreenAdaptation
AutoLayouthttps://github.com/hongyangAndroid/AndroidAutoLayout
响应式编程
RxJava2.xhttps://github.com/ReactiveX/RxJava
RxLifecycle 2.xhttps://github.com/trello/RxLifecycle
网络缓存
RxCache2.xRetrofit缓存库https://github.com/VictorAlbertos/RxCache
ASimpleCache轻量级缓存https://github.com/yangfuhai/ASimpleCache
事件总线
EventBus3.xhttps://github.com/greenrobot/EventBus
Otto1.xhttps://github.com/square/otto
图片处理
Glide4.xhttps://github.com/bumptech/glide
Fresco1.xhttps://github.com/facebook/fresco
Tiny图片压缩https://github.com/Sunzxyong/Tiny
libjpeg图片压缩https://github.com/libjpeg-turbo/libjpeg-turbo
Luban图片压缩https://github.com/Curzibn/Luban
Album图片选择https://github.com/yanzhenjie/Album
PhotoView图片预览https://github.com/chrisbanes/PhotoView
数据库
GreenDAO3.xhttps://github.com/greenrobot/greenDAO
OrmLitehttps://github.com/j256/ormlite-android
依赖注入
Dagger2.xhttps://github.com/google/dagger
ButterKnife8.xhttps://github.com/JakeWharton/butterknife
AndroidAnotationhttps://github.com/androidannotations/androidannotations
日志
Timber4.xhttps://github.com/JakeWharton/timber
内存泄漏
LeakCanary1.xhttps://github.com/square/leakcanary
卡顿监测
BlockCanaryhttps://github.com/markzhai/AndroidPerformanceMonitor
路由
ActivityRouterhttps://github.com/mzule/ActivityRouter
布局
vLayouthttps://github.com/alibaba/vlayout
二维码
zxing
安全
Hawk本地文件加密https://github.com/orhanobut/hawk
AndResGuard资源混淆https://github.com/shwenzhang/AndResGuard
sqlcipher数据加密https://github.com/sqlcipher/sqlcipher
文件下载
FileDownloaderhttps://github.com/lingochamp/FileDownloader
序列化
Jolyglothttps://github.com/VictorAlbertos/Jolyglot
图表库
MPAndroidCharthttps://github.com/PhilJay/MPAndroidChart
音视频
FFmeghttps://github.com/bilibili/ijkplayer
ijkplayer哔哩哔哩视频播放库https://github.com/FFmpeg/FFmpeg
FFmpegMediaMetadataRetriever视屏取帧https://github.com/wseemann/FFmpegMediaMetadataRetriever
android-youtube-playerYouTube视屏播放器https://github.com/PierfrancescoSoffritti/android-youtube-player
权限管理
HiPermissionhttps://github.com/yewei02538/HiPermission
RxPermissionshttps://github.com/tbruyelle/RxPermissions
Bintry.com
bintry-releasehttps://github.com/novoda/bintray-release
UI布局
SuperTextView富文本https://github.com/lygttpod/SuperTextView
bannerBannerhttps://github.com/youth5201314/banner
BRVAHRecyclerViewhttps://www.jianshu.com/p/b343fcff51b0
MultiTypeRecyclerViewhttps://github.com/drakeet/MultiType
Js交互
JsBridgehttps://github.com/lzyzsd/JsBridge
AgentWebhttps://github.com/Justson/AgentWeb
Json
Jacksonhttps://github.com/FasterXML/jackson-core
Kotlin
AnkoKotlin工具类https://github.com/Kotlin/anko
Android KTXKotlin工具类https://developer.android.com/kotlin/ktx?hl=zh-cn
沉浸式
ImmersionBar沉浸式状态栏https://github.com/gyf-dev/ImmersionBar
工具类
AndroidUtilCodeAndroid强大的工具集https://github.com/Blankj/AndroidUtilCode
396 | -------------------------------------------------------------------------------- /Android/五.ⅱ、内存优化.md: -------------------------------------------------------------------------------- 1 | 目录: 2 | - [基础知识](#基础知识) 3 | - [内存泄漏](#内存泄漏) 4 | - [内存优化方案](#内存优化方案) 5 | 6 | ### 基础知识 7 | 8 | #### [1、Android系统的进程管理。](http://gityuan.com/2015/10/01/process-lifecycle/) 9 | 10 | 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是清除重要性稍低一级的进程,依此类推,以回收系统资源。 11 | 12 | - 前台进程(Foreground process):正在与用户进行交互的进程,一般系统是不会杀死前台进程的,除非用户强制停止应用或者系统内存不足等极端情况会杀死。 13 | - 拥有用户正在交互的 Activity(已调用onResume()) 14 | - 拥有某个 Service,后者绑定到用户正在交互的 Activity 15 | - 拥有正在“前台”运行的 Service(服务已调用 startForeground()) 16 | - 拥有正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy()) 17 | - 拥有正执行其 onReceive() 方法的 BroadcastReceiver 18 | 19 | - 可见进程(Visible process):没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。 20 | - 拥有不在前台、但仍对用户可见的 Activity(已调用onPause())。 21 | - 拥有绑定到可见(或前台)Activity 的 Service 22 | 23 | - 服务进程(Service process):在内存不足以维持所有前台进程和可见进程同时运行的情况下,服务进程会被杀死。 24 | - 正在运行startService()方法启动的服务,且不属于上述两个更高类别进程的进程。 25 | 26 | - 后台进程(Background process):通常会有很多后台进程在运行,因此它们会保存在LRU列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。 27 | - 对用户不可见的Activity的进程(已调用Activity的onStop()方法)。 28 | 29 | - 空进程(Empty process):保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 30 | - 不含任何活动应用组件的进程 31 | 32 | 33 | #### [2、Android内存管理机制(LowMemoryKiller)。](http://gityuan.com/2016/09/17/android-lowmemorykiller/) 34 | 35 | **Android中的内存回收主要依靠LowMemoryKiller来完成,一种根据阈值级别出发相应力度的内存回收的机制。** 36 | 37 | Android基于Linux的系统,其实Linux有类似的内存管理策略——OOM killer,全称(Out Of Memory Killer), OOM的策略更多的是用于分配内存不足时触发,将得分最高的进程杀掉。而LMK则会每隔一段时间检查一次,当系统剩余可用内存较低时,便会触发杀进程的策略,根据不同的剩余内存档位来来选择杀不同优先级的进程,而不是等到OOM时再来杀进程,真正OOM时系统可能已经处于异常状态,系统更希望的是未雨绸缪,在内存很低时来杀掉一些优先级较低的进程来保障后续操作的顺利进行。 38 | 39 | 40 | #### 3、Java的四种引用,强弱软虚,及其适用的场景。 41 | - 强引用: 42 | - 强引用可以直接访问目标对象。 43 | - 强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。 44 | - 强引用可能导致内存泄露。 45 | - 软引用: 46 | - 在OutOfMemory异常发生之前,被占用的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。 47 | - 需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。 48 | 49 | - 如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。 50 | - 还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。 51 | 52 | - 弱引用:在系统GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。但是,由于垃圾回收器的线程通常优先级很低,因此,并一不定能很快的发现持有弱引用的对象。这种情况下,弱引用对象可以存在较长的一段时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册引用队列中。 53 | 54 | 实际应用:播放器的播放Panel,是一个View,就是在视频播放时,可以show、hide, 也可以拖拽进度条之类,还有上面的音量,亮度调节等。这样一个view,我们用弱引用,因为在视频播放过程中,不论硬解还是软解,都将占用大量内存。 55 | 56 | - 虚引用: 57 | - 虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。 58 | - 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,奖这个虚引用加入引用队列。 59 | 60 | 61 | ### 内存泄漏 62 | 63 | #### [4、内存泄漏如何产生,常见的Android内存泄漏的场景,怎么解决。](https://yq.aliyun.com/articles/3009) 64 | 65 | - 单例造成的内存泄露。 66 | - 匿名内部类/非静态内部类和异步线程。 67 | ``` 68 | public class MainActivity extends AppCompatActivity { 69 | 70 | private static TestResource mResource = null; 71 | 72 | @Override 73 | protected void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | setContentView(R.layout.activity_main); 76 | if (mManager == null) { 77 | mManager = new TestResource(); 78 | } 79 | //... 80 | } 81 | 82 | class TestResource { 83 | //... 84 | } 85 | } 86 | ``` 87 | 每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。 88 | 89 | 正确的做法为:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例。 90 | 91 | - 匿名内部类:android开发经常会继承实现Activity/Fragment/View,此时如果你使用了匿名类,并被异步线程持有了,那要小心了,如果没有任何措施这样一定会导致泄露。 92 | - Handler 造成的内存泄漏。 93 | - 集合类泄漏。 94 | 95 | #### 5、 怎么发现和分析内存泄漏。 96 | 97 | 监测工具: 98 | - LeakCanary:square出品的内存泄漏检测库,可以定位90%的内存泄漏问题 99 | - MemoryProfiler:AndroidStudio自带的内存分析工具。 100 | - AllocationTracer:AndroidStudio自带的查看内存分配的工具。 101 | 102 | #### 6、 OOM能不能用try catch捕获。 103 | 104 | OOM(OutOfMemeryError)属于Error,只有在一种情况下可以捕获OOM,只有在确认并OOM是由try语句中的对象声明导致的,那么在catch语句中,可以释放掉这些对象,解决OOM的问题,继续执行剩余语句。 105 | 106 | #### 7、 OOM遇到过哪些情况,如何解决的。 107 | 108 | [美团OOM案例详细分析](https://tech.meituan.com/oom_analysis.html) 109 | [Handler造成的OOM分析](http://www.chenwenguan.com/android-oom-analysis/) 110 | 111 | OOM类型: 112 | - Java堆溢出(java.lang.OutOfMemoryError: Java heap space):一般通过内存映像分析工具MAT(Eclipse Memory Analyzer)对dump出来的HPROF文件进行分析,确认是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。 113 | 114 | - 内存泄漏(Memory Leak):分析GC Roots引用链,定位出内存泄漏的代码。 115 | - 内存溢出(Memory Overflow):从代码上检查某些对象的生命周期过长、持有状态时间过长。 116 | 117 | - 虚拟机栈和本地方法栈溢出 118 | 119 | - 方法区和运行时常量池溢出 120 | 121 | - 本机直接内存溢出 122 | 123 | 常见的OOM场景: 124 | - Adapter没使用缓存的convertView。 125 | - Bitmap没有及时回收,调用recycle()函数并不能立即释放Bitmap,读取Bitmap到内存的时候没有做采样率的设置。 126 | - 线程数超限,proc/pid/status中记录的线程数超过proc/sys/kernel/threads-max中规定的最大线程数,场景如随意创建线程,没有使用线程池来管理。 127 | - 广播注册之后没有进行注销。 128 | - WebView没有销毁,应该调用destroy()函数去销毁。 129 | - Handler使用不当导致。 130 | 131 | #### [8、Bitmap使用的时候注意什么(Bitmap优化)。](https://www.jianshu.com/p/e49ec7d053b3) 132 | 133 | **基础知识:** 134 | - Options.inPreferredConfig修改图片编码格式: 135 | 136 | Bitmap.Config ALPHA_8 每个像素占用1 bit (8位)内存 137 | Bitmap.Config ARGB_4444 每个像素占用2 bit (16位)内存 138 | Bitmap.Config ARGB_8888 每个像素占用4 bit (32位)内存 139 | Bitmap.Config RGB_565 每个像素占用2 bit (16位)内存 140 | 141 | - 获取Bitmap大小: 142 | 143 | **加载一张本地资源图片,那么它占用的内存 = width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存。** 144 | 145 | 以1024 * 594的图片为例: 146 | ``` 147 | // 不做处理,默认缩放。 148 | BitmapFactory.Options options = new BitmapFactory.Options(); 149 | Bitmap bmp01 = BitmapFactory.decodeResource(getResources(), R.mipmap.bmp, options); 150 | 151 | int size01Allocation = bmp01.getAllocationByteCount(); 152 | int size01 = bmp01.getByteCount(); 153 | 154 | // 手动设置inDensity与inTargetDensity,影响缩放比例。 155 | BitmapFactory.Options options_setParams = new BitmapFactory.Options(); 156 | options_setParams.inDensity = 320; 157 | options_setParams.inTargetDensity = 320; 158 | 159 | Bitmap bmp02 = BitmapFactory.decodeResource(getResources(), R.mipmap.bmp, options_setParams); 160 | 161 | int size02Allocation = bmp02.getAllocationByteCount(); 162 | int size02 = bmp02.getByteCount(); 163 | ``` 164 | xhdpi的文件夹下,inDensity为320,inTargetDensity为440,内存大小为4601344;而4601344 = 1024 * 594 * (440 / 320)* (440 / 320)* 4 165 | 166 | 手动设置inDensity与inTargetDensity,使其比例为1,内存大小为2433024;2433024 = 1024 * 594 * 1 * 1 * 4。 167 | 168 | **除了加载本地资源文件的解码方法会默认使用资源所处文件夹对应密度和手机系统密度进行缩放之外,别的解码方法默认都不会。此时Bitmap默认占用的内存 = width * height * 一个像素所占的内存。** 169 | 170 | **优化策略:** 171 | - 使用setImageBitmap、setImageResource、BitmapFactory.decodeResource这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,而BitmapFactory.decodeStream方法则是通过JNI来创建Bitmap,更节约内存。 172 | ``` 173 | InputStream is = getResources().openRawResource(R.drawable.pic); 174 | Bitmap bitmap = BitmapFactory.decodeStream(is); 175 | ``` 176 | 177 | - 对图片进行压缩显示,按需分配内存。 178 | 179 | - 对图片进行质量压缩。bitmap.compress 180 | - 对图片进行尺寸缩放。Bitmap.Options.inSampleSize 181 | inSampleSize的取值必须为2的整数倍,因为直接从点阵中隔行抽取最有效率,所以为了兼顾效率, inSampleSize 这个属性只认2的整数倍为有效。 182 | 183 | - 使用三方库对图片进行压缩。libjpeg 184 | 185 | - 使用LruCache和DiskLruCache做内存和磁盘缓存。 186 | 187 | - Bitmap进行复用:Android 3.0之前Bitmap.recycle,3.0之后进行复用。 188 | 189 | - 使用WeakReference,保证资源能够被及时回收。 190 | 191 | **[Bitmap在不同文件夹下面的大小计算](https://ivonhoe.github.io/2017/03/22/Bitmap&Memory/)** 192 | 193 | [dpi](https://github.com/chen-eugene/Android-Interview/blob/master/image/dpi.png) 194 | 195 | 当Android系统加载图片时,会针对当前系统的dpi和图片目录的source dpi比较做相应比例的缩放,如果一张图片放在drawable-xxxhdpi目录,这是告诉系统,针对dpi为640的手机屏幕上,这张图片是刚刚好的,它的scale为1.0。 196 | 197 | 例如一张180的图片在dpi为560的手机中,只有将其放在drawable-nodpi或者drawable-560dpi目录中才可以显示真正的大小。 198 | 199 | - 如果将这张图片放在xxxhdpi文件夹中,那么可以计算它的大小:size = (int)(180 * (560 / 640) + 0.5f) = 158px 200 | - 放入xxhdpi目录中,实际大小应该为:size = int (180 * (560 / 480) +0.5f ) = 210px 201 | - 放入xhdpi目录中,实际大小应该为:size = int (180 * (560 / 320) +0.5f ) = 315px 202 | - 放入hdpi目录中,实际大小应该为:size = int (180 * (560 / 240) +0.5f ) = 420px 203 | 204 | 205 | #### 9、 Bitmap recycler相关(Bitmap复用)。 206 | 207 | Google 官方教程 [Managing Bitmap Memory](https://developer.android.com/topic/performance/graphics/manage-memory) 208 | [Glide Bitmap复用](https://www.jianshu.com/p/d6cae68175f2) 209 | 210 | Android2.2(API 8)一下的时候,当 GC 工作时,应用的线程会暂停工作,同步的 GC 会影响性能。而 Android2.3 之后,GC 变成了并发的,意味着 Bitmap 没有引用的时候其占有的内存会很快被回收。 211 | 212 | 在Android2.3.3(API10)之前,Bitmap 的像素数据存放在 Native 内存,而 Bitmap 对象本身则存放在 Dalvik Heap 中。Native 内存中的像素数据以不可预测的方式进行同步回收,有可能会导致内存升高甚至 OOM Crash。而在 Android3.0 之后,Bitmap 的像素数据也被放在了 Dalvik Heap 中。 213 | 214 | Android2.3.3 及以下:推荐使用Bitmap#recycle方法进行内存回收。 215 | 216 | Android3.0 及以上:推荐使用Bitmap复用。 217 | 218 | **Bitmap复用:** 219 | - 被复用的 Bitmap 必须设置inMutable为true(通过 BitmapFactory.Options 设置。 220 | - Android4.4(API 19)之前只有格式为jpg、png,同等宽高(要求苛刻),inSampleSize为1的Bitmap才可以复用。 221 | - Android4.4(API 19)之前被复用的Bitmap的inPreferredConfig会覆盖待分配内存的Bitmap设置的inPreferredConfig。 222 | - Android4.4(API 19)之前待加载Bitmap的Options.inSampleSize必须明确指定为1。 223 | - Android4.4(API 19)之后被复用的Bitmap的内存必须大于需要申请内存的Bitmap的内存。 224 | 225 | ``` 226 | BitmapFactory.Options options = new BitmapFactory.Options(); 227 | // 图片复用,这个属性必须设置; 228 | options.inMutable = true; 229 | // 手动设置缩放比例,使其取整数,方便计算、观察数据; 230 | options.inDensity = 320; 231 | options.inTargetDensity = 320; 232 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.resbitmap, options); 233 | // 对象内存地址; 234 | Log.i(TAG, "bitmap = " + bitmap); 235 | Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); 236 | 237 | // 使用inBitmap属性,这个属性必须设置; 238 | options.inBitmap = bitmap; 239 | options.inDensity = 320; 240 | // 设置缩放宽高为原始宽高一半; 241 | options.inTargetDensity = 160; 242 | options.inMutable = true; 243 | Bitmap bitmapReuse = BitmapFactory.decodeResource(getResources(), R.drawable.resbitmap_reuse, options); 244 | // 复用对象的内存地址; 245 | Log.i(TAG, "bitmapReuse = " + bitmapReuse); 246 | Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); 247 | Log.i(TAG, "bitmapReuse:ByteCount = " + bitmapReuse.getByteCount() + ":::bitmapReuse:AllocationByteCount = " + bitmapReuse.getAllocationByteCount()); 248 | 249 | 输出: 250 | I/lz: bitmap = android.graphics.Bitmap@35ac9dd4 251 | I/lz: width:1024:::height:594 252 | I/lz: bitmap:ByteCount = 2433024:::bitmap:AllocationByteCount = 2433024 253 | I/lz: bitmapReuse = android.graphics.Bitmap@35ac9dd4 // 两个对象的内存地址一致 254 | I/lz: width:512:::height:297 255 | I/lz: bitmap:ByteCount = 608256:::bitmap:AllocationByteCount = 2433024 256 | I/lz: bitmapReuse:ByteCount = 608256:::bitmapReuse:AllocationByteCount = 2433024 // ByteCount比AllocationByteCount小 257 | ``` 258 | 259 | #### [10、WebView的泄露如何解决。](https://lipeng1667.github.io/2016/08/06/memory-optimisation-for-webview-in-android/) 260 | 261 | 很少发现WebView出现内存泄露的情况,在Android5.1系统中会出现,WebView没有得到及时的释放。 262 | 263 | - 不在xml中使用WebView,而是在Activity中代码创建,并且Context传入ApplicationContext。 264 | 265 | 并不能解决WebView的泄露问题,因为WebView持有Context而造成的Context泄露,而是WebView本身的泄露。 266 | 267 | - 销毁WebView前,先将WebView从View树中移除,然后在销毁。 268 | 269 | ``` 270 | if(mWebView != null){ 271 | ((ViewGroup)mWebView.getParent()).removeView(mWebView); 272 | mWebView.destroy(); 273 | mWebView = null; 274 | } 275 | ``` 276 | 277 | - 单独启动一个进程来进行WebView的相关操作,在结束的时候直接kill掉WebView所在的进程。(没有试过) 278 | 279 | 280 | ### [内存优化方案](https://www.jianshu.com/p/218e5cde47fe) 281 | 282 | #### 11、内存优化方案 283 | 284 | 内存优化工具: 285 | - LeakCanary:square出品的内存泄漏检测库,可以定位90%的内存泄漏问题 286 | - MemoryProfiler:AndroidStudio自带的内存分析工具。 287 | - AllocationTracer:AndroidStudio自带的查看内存分配的工具。 288 | 289 | 优化方向: 290 | - [① 解决所有的内存泄漏问题](https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=2649796884&idx=1&sn=92b4e344060362128e4a86d6132c3736&scene=0#wechat_redirect) 291 | - ② 避免内存抖动 292 | - 避免在循环中创建临时对象 293 | - 避免在onDraw中创建Paint、Bitmap对象等。 294 | 295 | - ③ Bitmap的优化 296 | - ④ 使用优化过的数据结构 297 | - ⑤ 使用onTrimMemory根据不同的内存状态做相应处理 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | -------------------------------------------------------------------------------- /Android/二、四大组件相关.md: -------------------------------------------------------------------------------- 1 | ### 四大组件 2 | ##### 1、手动画出Activity、Fragment的生命周期,他们是怎么关联的。 3 | 4 | ![Activity生命周期](https://github.com/chen-eugene/Interview/blob/master/image/activity_lifecycle.png) 5 | 6 | - onCreate():当 Activity 第一次创建时会被调用。这是生命周期的第一个方法。在这个方法中,可以做一些初始化工作,比如调用setContentView去加载界面布局资源,初始化Activity所需的数据。当然也可借助onCreate()方法中的Bundle对象来回复异常情况下Activity结束时的状态。 7 | 8 | - onRestart():表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为导致的,比如用户按Home键切换到桌面或打开了另一个新的Activity,接着用户又回到了这个Actvity。 9 | 10 | - onStart(): 表示Activity正在被启动,即将开始,这时Activity已经出现了,但是还没有出现在前台,无法与用户交互。**这个时候可以理解为Activity已经显示出来,但是我们还看不到。** 11 | 12 | - onResume():**表示Activity已经可见了,并且出现在前台并开始活动**。需要和onStart()对比,onStart的时候Activity还在后台,onResume的时候Activity才显示到前台。 13 | 14 | - onPause():表示 Activity正在停止,仍可见,正常情况下,紧接着onStop就会被调用。在特殊情况下,如果这个时候快速地回到当前Activity,那么onResume就会被调用(极端情况)。**onPause中不能进行耗时操作,会影响到新Activity的显示。因为onPause必须执行完,新的Activity的onResume才会执行。** 15 | 16 | - onStop():表示Activity即将停止,不可见,位于后台。可以做稍微重量级的回收工作,同样不能太耗时。 17 | 18 | - onDestory():表示Activity即将销毁,这是Activity生命周期的最后一个回调,可以做一些回收工作和最终的资源回收。 19 | 20 | ##### 2、Activity常见情形下的生命周期,如按下home键、锁屏。 21 | - Back键:onPause → onSaveInstanceState → onStop → onDestroy 22 | - Home键:onPause → onStop / onRestart → onStart → onResume 23 | - 锁屏: onPause → onSaveInstanceState → onStop / onRestart → onStart → onResume 24 | - A启动B:A:onPause → B:onCreate → B:onStart → B:onResume → A:onSaveInstanceState → A:onStop → A:onDestroy(A是否调用finish) 25 | - 被打断时(如接电话):onPause → onSaveInstanceState → onStop / onRestart → onStart → onResume 26 | ##### 3、 异常情况下Activity的生命周期,如横竖屏切换、系统资源不足。 27 | - 横竖屏切换 28 | Activity被销毁:onPause → onSaveInstanceState(与onPause没有时序关系,可能在之前,也可能在之后) → onStop → onDestroy 29 | Activity被重新创建:onCreate → onRestoreInstanceState → onStart → onResume 30 | 当不想Activity被重新创建,则需要在Manifest中添加configChanges属性,在Activity中复写onConfigurationChanged方法。 31 | - 资源内存不足导致低优先级Activity被kill 32 | ①前台Activity —— 正在和用户交互的Activity,优先级最高。 33 | ②可见但非前台Activity —— 如Activity弹了一个对话框,导致Activity可见但位于后台,无法与用户直接交互。 34 | ③后台Activity —— 已经被暂停的Activity,比如执行了onStop,优先级最低。 35 | 当系统内存不足时,会按照优先级去杀死Activity,并通过onSaveInstanceState和onRestoreInstanceState来保存和恢复数据。 36 |
37 | 38 | #### [4、Activity异常生命周期下的数据缓存。](https://github.com/xitu/gold-miner/blob/master/TODO/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders.md) 39 | 40 |
41 | 42 | ##### 5、什么时Activity任务栈。 43 | - 是什么: 44 | 任务栈Task,是一种用来放置Activity实例的容器,他是以栈的形式进行盛放 45 | 启动一个Application的时候,系统会为它默认创建一个对应的Task,用来放置根Activity。默认启动Activity会放在同一个Task中,新启动的Activity会被压入启动它的那个Activity的栈中,并且显示它。当用户按下回退键时,这个Activity就会被弹出栈,按下Home键回到桌面,再启动另一个应用,这时候之前那个Task就被移到后台,成为后台任务栈,而刚启动的那个Task就被调到前台,成为前台任务栈,Android系统显示的就是前台任务栈中的Top实例Activity。 46 | - 作用: 47 | 由于android强化了组件概念,弱化了Aplication的概念,所以在android程序开发中,A应用的A组件想要使用拍照或录像的功能就可以不用去针对Camera类进行开发,直接调用系统自带的摄像头应用(称其B应用)中的组件(称其B组件)就可以了,但是这就引发了一个新问题,A组件运行在A应用中,B组件运行在B应用中,自然都不在同一个进程中,那么从B组件中返回的时候,如何实现正确返回到A组件呢?Task就是来负责实现这个功能的,它是从用户角度来理解应用而建立的一个抽象概念。 48 |
49 | 50 | ##### [6、 Activity启动模式以及使用的场景,对应的Intent Flag。](https://cloud.tencent.com/developer/article/1035528) 51 | 1. 启动模式 52 | - standard 标准模式(系统默认模式) 53 | 每次启动一个Activity都会重新创建一个新的实例。 54 | 当用ApplicationContext去启动standard模式的Activity时,会报如下错误![](https://github.com/chen-eugene/Interview/blob/master/image/1536215413(1).png) 55 | 这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有任务栈。 56 | 解决方法就是为Intent添加FLAG_ACTIVITY_NEW_TASK标记,这样启动的时候就会创建一个新的任务栈,相当于singleTask模式。 57 | 以standard方式启动的Activity被跨进程调用,在5.0之前新启动的Activity实例会放入发送Intent的Task的栈的顶部,尽管它们属于不同的程序,在5.0之后,上述情景会创建一个新的Task,新启动的Activity就会放入刚创建的Task中。 58 | 59 | - singleTop 栈顶复用 60 | 如果新Activity已经位于任务栈的栈顶,那么Activity不会被重新创建,同时它的onNewIntent方法会被调用。如果新Activity不在栈顶,那么新Activity仍会被重新创建。 61 | 62 | 应用场景:在通知栏点击收到的通知,然后需要启动一个Activity,这个Activity就可以用singleTop,否则每次点击都会新建一个Activity。 63 | 64 | - singleTask 栈内复用 65 | 当一个具有singleTask模式的Activity被启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例并放入到栈中。如果存在A所需的任务栈,并且存在A的实例,那么系统就会回调A的onNewIntent方法。singleTask具有ClearTop效果,会将位于它以上的Activity实例全部清除出栈。 66 | 67 | TaskAffinity属性可以为Activity指定一个任务栈,属性值不能和包名相同,否则就相当于没有指定,因为默认情况下,所有的Activity的任务栈为应用的包名。 68 | 当TaskAffinity和singleTask配对使用的时候,待启动的Activity会运行在名字和TaskAffinity形同的任务栈中。 69 | 70 | 应用场景:大多数App的主页。对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用,那么当我们第一次进入主界面之后,主界面位于栈底,以后不管我们打开了多少个Activity,只要我们再次回到主界面,都应该使用将主界面Activity上所有的Activity移除的方式来让主界面Activity处于栈顶,而不是往栈顶新加一个主界面Activity的实例,通过这种方式能够保证退出应用时所有的Activity都能报销毁。 71 | 72 | 在跨应用Intent传递时,如果系统中不存在singleTask Activity的实例,那么将创建一个新的Task,然后创建SingleTask Activity的实例,将其放入新的Task中。 73 | 74 | - singleInstance 单实例模式 75 | 整个手机系统只有一个实例存在,不同的应用去打开这个activity 共享公用的同一个activity。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。 76 | 77 | 应用场景:呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。 78 | 79 | 2. Intent Flag标识 80 | 有两种方式为Activity指定启动模式,1、通过AndroidMenifest,2、通过Intent设置标识来启动。第二种的优先级高于第一种,当两种方式同时存在时,以第二种方式为准。 81 | FLAG_ACTIVITY_NEW_TASK:指定singleTask启动模式。 82 | FLAG_ACTIVITY_SINGLE_TOP:指定singleTop启动模式。 83 | FLAG_ACTIVITY_CLEAR_TOP:当Activity启动时,在统一任务栈中所有位于它上面的Activity都要出栈。一般和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,如果被启动的Activity实例已经存在,系统就会回调onNewIntent方法;如果被启动的Activity采用的时standard模式,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶。 84 |
85 | 86 | #### 7、 IntentFilter匹配规则。 87 | Activit的启动模式分为显示调用和隐式调用,如果两者同时出现以显示调用为主。 88 | - action匹配规则 89 | Intent中的action必须能够和过滤规则中的action完全匹配(action的字符串值完全一样)。一个过滤规则中可以有多个action,只要Intent中的action存在且必须和过滤规则中的其中一个action相同。 90 | - category匹配规则 91 | Intent中如果携带category,那么所有的category都必须和过滤规则中的其中一个相同。 92 | 如果不设置category,系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上"android.intent.category.DEFAULT",为了Activity能够接收隐士调用,就必须在intent-filter中指定"android.intent.category.DEFAULT"。 93 | - data匹配规则 94 | data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data。 95 | ①如果Intent中的mimeType属性为"image/*",这种情况下虽然没有指定URI,但却又默认值,URL的默认值为content和file,也就是说,虽然没有指定URI,但是Intent中的URI部分的schema必须为content或者file才能匹配。 96 | ②如果要为Intent指定完整的data,必须要调用setDataAndType方法,不能先调用setData再调用setType,因为这两个方法会彼此清除掉对方的值。 97 | 98 | 当我们通过隐士方式启动一个Activity的时候,可以通过PackageManager的resolveActivity方法或者Intent的resolveActivity方法来进行判断。PackageManager还提供了queryIntentActivities方法。 99 |
100 | 101 | ##### 8、Service生命周期,与IntentService的区别。 102 | IntentService是一个基于Service的一个类,用来处理异步的请求。你可以通过startService(Intent)来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。 103 | IntentService最起码有两个好处,① 不需要自己去new Thread;② 不需要考虑在什么时候关闭该Service。 104 |
105 | 106 | #### 9、startService和bindService混合使用的场景,这时候Service的生命周期是怎样的。 107 | ![service](https://github.com/chen-eugene/Interview/blob/master/image/b2cc0c785131b7b7.png) 108 | - startService:调用onCreate()->onStartCommand() 109 | bindService:调用onBind() 110 | stopService:没有调用onDestory() Service仍然在运行! 111 | unbindService:调用onUnbind()->onDestory() 此时Service关闭! 112 | 113 | - startService:调用onCreate()->onStartCommand() 114 | bindService:调用onBind() 115 | unbindService:调用onUnbind() Service仍然在运行! 116 | stopService:调用onDestory() 此时Service才关闭! 117 | 118 | 若被停止的服务依然有ServiceConnection 与其绑定,则服务不能销毁,直至我们把所有ServiceConnection 解绑。 119 | 120 | 当所有ServiceConnection 解绑后,系统会自动销毁服务。不包括同时用startService()启动的情况。此时,我们不得不再调用一次stopService来销毁它。 121 | 122 | **startService和bindService的区别:** 123 | - startService启动一个Service之后,这个Service将独立运行,它的生命周期和启动者的生命周期没有任何的关系。Service在任务完成的时候应该调用stopSelf或者启动者调用stopService来结束Service,否则将会一直运行。 124 | - 当我们需要和Service进行交互时,甚至IPC通信,我们可以通过bindService来绑定一个服务。当所有的ServiceConnection解除绑定之后,服务将会被销毁。 125 |
126 | 127 | #### 10、广播的两种注册方式有什么区别。 128 | - 静态注册:静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立。 129 | 自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。 130 | 由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。 131 | - 动态注册:当此Activity销毁时,动态注册的广播接收器将不再接收到相应的广播。 132 |
133 | 134 | #### 11、怎么增加广播的安全性。 135 | Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下: 136 | 137 | 1. 其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理; 138 | 139 | 2. 其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。 140 | 141 | 无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是: 142 | 143 | 1. 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收; 144 | 145 | 2. 在广播发送和接收时,都增加上相应的permission,用于权限验证; 146 | 147 | 3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。 148 | 4. App应用内广播(LocalBroadcast),广播的发送者和接收者都同属于一个App。 149 | 相比于全局广播,App应用内广播优势体现在: 150 | 151 | - 安全性更高; 152 | 153 | - 更加高效。 154 |
155 | 156 | #### 12、什么是Fragment回退栈。 157 | 158 | Fragment的回退栈是用来保存每一次Fragment事务发生的变化 如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。 159 |
160 | 161 | #### 14、Fragment commit和commitAllowingStateLossing的区别。 162 | 163 | 当Activity被异常销毁时,Activity会对自身状态进行保存(包括添加的fragment),在Activity被重建时,会对Fragment进行恢复。 164 | 165 | 调用commit方法时,系统会判断(mStateSaved)是否已经保存,如果已经保存,则会抛出IllegalStateException("Can not perform this action after onSaveInstanceState")异常,而commitAllowingStateLoss则会则会在恢复状态时丢失(不会恢复Fragment)。 166 | 167 | 解决IllegalStateException("Can not perform this action after onSaveInstanceState")异常可以在使用commit时判断savedInstanceState是否为空。 168 |
169 | 170 | #### 15、Fragment之间传递数据的方式。 171 | 172 | - 调用setArguments方法,通过bundle来传递参数。 173 | 174 | Activity意外销毁,被重新创建时会重新构建它所管理的Fragment,原先的Fragment的数据将会丢失,通过setArguments方法设置的bundle将会保留下来,最后恢复bundle里面的数据。 175 | 176 | - 直接在Activity中通过findViewById可以获取到Fragment的View。所有的操作都在Activity中,造成Activity臃肿。 177 | 178 | - 在onActivityCreate中通过findViewById可以获取到Fragment的View,然后进行操作。耦合性太高。 179 | 180 | - 通过接口回调的方式。 181 |
182 | 183 | #### [16、FragmentPager、FragmentStatePagerAdapter区别。](http://www.apkbus.com/android-90417-1-1.html) 184 | 185 | - FragmentStatePagerAdapter:在切换不同的Fragment的时候,会把前面的Fragment销毁,同时会把Fragment的Bundle在onSaveInstanceState(Bundle)保存下来。等用户切换回来的时候,Fragment就会根据我们的instance state恢复出来。 186 | 187 | FragmentPagerAdapter适用于Fragment比较少的情况,因为会把每一个Fragment保存在内存中,不用每次切换的时候,去保存现场,切换回来在重新创建,所以用户体验比较好。 188 | 189 | - FragmentPagerAdapter:在切换的时候,Fragment不会销毁,而只是调用事务中的detach方法,只会把Fragment的view销毁,而保留了以前的Fragment对象。所以通过这种方式创建的Fragment一直不会被销毁。 190 | 191 | FragmentStatePagerAdapter适用于Fragment比较多的情况,切换的时候销毁以前的Fragment以释放内存。 192 |
193 | 194 | #### 17、什么是Context,一个应用中有几个Context。 195 | 196 | Context翻译为“上下文”,它提供了关于应用环境全局信息的接口。具体实现有Android系统提供。通过它可以获取到应用程序的资源(包括应用级别操作,如启动Activity,发广播,接受Intent等)。 197 | 198 | 单进程中Context的数量 = Activity数量 + Service数量 + 1 199 |
200 | 201 | #### 18、能不能通过Application来启动Activity。 202 | 203 | ![Contentx的作用域](https://github.com/chen-eugene/Interview/blob/master/image/1543831272.png) 204 | 205 | 出于安全的考虑,一个Activity的启动必须要建立在另一个Activity的基础之上,在默认情况下是不允许通过非Activity来启动另一个Activity的,当我们使用Application来启动一个LaunchMode为standard的Activity时会保如下异常: 206 | ``` 207 | android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the 208 | FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? 209 | ``` 210 | 这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。 211 | 212 | 解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。 213 | ``` 214 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 215 | ``` 216 |
217 | 218 | #### [19、多进程的优劣及其应用。](https://blog.csdn.net/csdn_aiyang/article/details/69734557) 219 | 220 | **多进程的好处:** 221 | - 系统会为多进程的应用分配更大的内存,并且将业务放到子进程中,主进程所占用的内存将会减少,降低了主进程系统杀死的概率。因为系统对每个应用进程的内存占用是有限制的,而且占用内存越大的进程,通常被系统杀死的可能性越大。 222 | - 主进程退出了,子进程任然可以继续工作,比如说推送服务。 223 | - 主进程和子进程之间可以相互拉起,可以尽可能的保证app不被杀死。 224 | 225 | **多线程需要注意的地方:** 226 | - 多进程创建多个Application实例,如果不对进程加以区分,会导致初始化工作被执行多次。 227 | - 静态变量失效 228 | Android 为每一个进程分配一个独立的虚拟机,不同虚拟机在内存分配上有不同地址空间,这就导致多进程下访问同一个类的对象会产生多分副本。所以在一个进程中修改某个值,只会在当前进程有效,对其他进程不会造成任何影响。 229 | 230 | - SharedPreferences 的可靠性下降 231 | 因为SharedPreferences 底层通过读写XML实现,并发读写显然是不安全的操作,甚至会出现数据错乱。 232 | 233 | - 线程同步机制完全失效。 234 | 因为多进程的内存地址空间不同,锁的不是同一个对象,所以不管是锁对象还是锁全局对象都无法保证线程同步。 235 | 236 | - 进程间的通讯方式相对于线程间的通信更加麻烦。 237 | 238 | **[多进程的应用](https://blog.csdn.net/lixpjita39/article/details/77435156)** 239 | 240 | - 分散内存的占用:让一个组件运行在单独的进程中,可以减少主进程所占用的内存,避免OOM问题,降低被系统杀死的概率。 241 | - 实现多模块:比如我做的应用大而全,里面肯定会有很多模块,假如有地图模块、大图浏览、自定义WebView等等(这些都是吃内存大户),还会有一些诸如下载服务,监控服务等等,一个成熟的应用一定是多模块化的。 242 | - 子进程崩溃,主进程可以继续工作 243 | - 主进程退出,子进程可以继续工作:即使主进程退出了,我们的子进程仍然可以继续工作,假设子进程是推送服务,在主进程退出的情况下,仍然能够保证用户可以收到推送消息。 244 | - 实现守护进程:如果主线程中的服务要从开机起持续运行,若由于内存等原因被系统kill掉,守护进程可以重新启动主线程的服务。 245 | - 通过JNI利用C/C++,调用fork()方法来生成子进程,一般开发者会利用这种方法来做一些daemon(守护进程)进程,来实现防杀保活等效果。 246 | - 实现监控系统进程,将错误上报给系统,告知他在什么机型、环境下、产生了什么样的Bug,提升用户体验。 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /Android/三、View剖析.md: -------------------------------------------------------------------------------- 1 | #### [1、Touch事件的分发流程。](https://blog.csdn.net/carson_ho/article/details/54136311) 2 | 3 | [ViewGroup事件传递](https://blog.csdn.net/yanbober/article/details/45912661) 4 | [View事件传递](https://blog.csdn.net/yanbober/article/details/45887547) 5 | 6 | Touch事件的分发过程有三个重要的方法来完成:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。 7 | 8 | **分发流程:** 9 | - Android事件派发是先传递到最顶级的ViewGroup,再由ViewGroup递归传递到View的。 10 | - 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true不在向下继续传递它的onTouchEvent会被调用去处理此事件;如果这个ViewGroup的onInterceptTouchEvent方法返回false就表示它不拦截当前事件,此将会递归传递给子视图。 11 | 12 | - 当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被调用,如果onTouch返回false,则当前View的onTouchEvent方法会被调用;如果返回true,则onTouchEvent将不会被调用,表示当前View消耗掉了此事件。 13 | 14 | - 在整个事件传递过程中,如果一个View的onTouchEvent返回false,那么他的父视图的onTouchEvent方法将会被调用,以此类推。如果所有的View都不处理当前事件,那么此事件最终将会被传递给Activity处理,即Activity的onTouchEvent方法会被调用。 15 | 16 | **注意点:** 17 | - View没有onInterceptTouchEvent方法,一点有事件传递给它,那么它的onTouchEvent方法将会被调用。 18 | 19 | - 如果DOWN事件没有被消耗,那么后续的事件将不会继续下发。 20 | 21 | - 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么它将不会接收到同一个事件序列的后续事件,并且事件将会重新交给他的父视图处理,即父视图的onTouchEvent方法会被调用。 22 | 23 | - 如果View不消耗除ACTION_DOWN意外的其他事件,此父视图的onTouchEvent方法不会被调用,但是当前的View可以持续接收到后续的事件,最终事件会被传递给Activity处理。 24 | 25 | - dispatchTouchEvent回true就是消费事件,这种说法不完全正确。dispatchTouchEvent事件派发是传递的,如果返回值为false将停止下次事件派发,如果返回true将继续下次派发。 26 | 27 | 28 | #### [2、点击事件被拦截,但是想传到下面的view,如何操作。](https://blog.csdn.net/xw13782513621/article/details/77528518) 29 | 30 | 如果`onInterceptTouchEvent`返回true,那么本次touch事件之后的所有action都不会在向下传递,将会交给此View的`onTouchEvent`方法处理。 31 | 子视图调用`getParent().requestDisallowInterceptTouchEvent(true)`方法,将会阻止父视图拦截touch事件,此touch事件的后续action也不会被拦截。 32 | 33 | 34 | #### [3、具体问题如下图所示](https://www.jianshu.com/p/94307344eed4) 35 | 36 | ![第三题](https://github.com/chen-eugene/Android-Interview/blob/master/image/wqiruy.png) 37 | 38 | 不只是重写onTouchEvent方法,重写onDispatchTouchEvent方法是一样的道理。返回true表示消耗事件,剩下所有的事件会传递到这里,由它处理。返回false表示不消耗事件,正常传递,并最终向上交还给父视图。 39 | 40 | 问题分析: 41 | - C的onTouchEvent方法返回true,那么它消耗了DOWN事件,其父视图将会继续分发后续的事件。 42 | - B拦截MOVE事件,那么MOVE和后续事件将会最终传递到B,不会再继续向下分发。 43 | - 所以C只能接收到DOWN事件,B能够接收到整个事件序列。 44 | 45 | 46 | 47 | #### 4、View的位置参数有哪些,left、x、translationX的含义以及三者的关系。 48 | - view的位置由left、top、right、bottom四个属性决定,这几个坐标可以通过getLeft()、getTop()、getRight()、getBottom()获取。注意这四个坐标是相对坐标,即相对于父容器的坐标。当view发生移动时,这几个坐标是不变的。 49 | 50 | - 从Android 3.0开始,增加了几个参数:x、y、translationX、translationY,都是相对于父容器的坐标。 51 | 52 | x指view左上角的横坐标,当view发生移动时,x会变化; 53 | 54 | translationX指view左上角的横坐标相对于父容器的偏移量,当view发生移动时,translationX会变化。 55 | `x = left + translationX ` 56 | 57 | - rawX是绝对坐标,是相对于屏幕左上角的横坐标,view本身没有getRawX的方法,这个方法一般在MotionEvent对象里使用。 58 | 59 | - scrollX指的是view在滑动过程中,view的左边缘和view内容的左边缘在水平方向的距离(注意与translationX 的区别,translationX 指的是view本身的移动,scrollX是view的内容移动),也就是说调用了view的scrollTo或scrollBy方法,view本身不会移动,只会移动view的内容。 60 | ![坐标图](https://github.com/chen-eugene/Interview/blob/master/image/20160808154319878.png) 61 | 62 | 63 | #### 5、什么是MeasureSpec。 64 | MeasureSpec是一个32位的int值,高2位表示SpecMode,指测量模式;低30位表示SpecSize,指在某种测量模式下的规格大小。MeasureSpec一旦确定后,onMeasure方法就可以确定View的测量宽/高。 65 | 66 | - 对于DecorView,其MeasureSpec由窗口的尺寸和其资深的LayoutParams来共同决定。 67 | - 对于普通的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。 68 | 69 | ![MeasureSpec](https://github.com/chen-eugene/Interview/blob/master/image/20170311114110621.jpg) 70 | 71 | 72 | #### [6、View绘制过程。](https://blog.csdn.net/yanbober/article/details/46128379) 73 | ViewRoot对应于ViewRootImpl类,是链接WindowManager和DocorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。 74 | 75 | View的绘制流程就是从ViewRoot的performTraversals方法开始的,经过measure、layout和draw三个过程之后最终将View绘制出来。 76 | 77 | **measure过程:** 78 | - View的measure过程:根据父类的measureSpec和自身的layoutParams来确定自身的measureSpec。 79 | 80 | 直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于match_content。 81 | 82 | - ViewGroup的measure过程:ViewGroup没有重写onMeasure方法,但是提供了measureChild方法来循环遍历,ViewGroup会根据子元素的大小来测量自己的大小,但不能超过父容器剩余的大小。 83 | 84 | **layout过程:** 85 | - 当ViewGroup的位置被确定后,它会在onLayout方法中遍历所有的子元素并调用其layout方法,在layout方法中onLayout方法又会被调用。 86 | - layout方法确定View本身的位置,而onLayout方法则会确定所有子元素的位置。 87 | 88 | **draw过程:** 89 | - 绘制背景background.draw(canvas)。 90 | - 绘制自身(onDraw)。 91 | - 绘制children(dispatchDraw)。 92 | - 绘制装饰(如前景,scrollbar等)。 93 | 94 | 95 | #### 7、怎么获取View的宽高。 96 | **getMeasuredWidth和getWidth的区别:** 97 | 98 | - getMeasuredWidth返回的是View的测量宽/高,但某些情况下(如ListView)系统可能需要多次测量才能确定最终的测量宽/高,此时在onMeasure方法中拿不到准确的测量宽度。 99 | - getWidth返回是最终宽度,通常情况下,View的测量宽/高就等于最终宽/高,但也存在极端情况导致两者不一致(如重写layout方法,手动修改最终宽/高)。 100 | - getMeasureWidth必须在onMeasure之后才有效,getWidth必须在onLayout之后才有效。 101 | 102 | **获取View的宽/高:** 103 | 104 | View的measure过程和Activity的生命周期不是同步执行的,所以不能通过Activity的生命周期来直接获取宽/高。 105 | 106 | - Activity/View#onWindowFocusChanged:View初始化已经完成时会调用。 107 | 108 | 需要注意的是:当Activity得到焦点和失去焦点时均会被调用一次,如果频繁的执行onResume和onPause,那么onWindowFocusChanged将会被频繁调用。 109 | 110 | - view.post:通过post可以将runnable投递到消息队列的尾部,当Looper调用此runnable的时候,View已经初始化完成。 111 | - ViewTreeObserver:随着View树状态的改变,onGlobalLayout会被多次调用。 112 | - view.measure(int widthMeasureSpec,int heightMeasureSpec)通过手动对View进行测量来得到宽/高,但也分情况处理: 113 | 114 | match_parent:无法测量具体的宽/高,因为构造此种MeasureSpec需要知道parentSize,这个时候无法知道parentSize大小,所以测不出View的大小。 115 | 116 | 具体的数值: 117 | ``` 118 | 比如宽/高都是100px 119 | int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY); 120 | int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY); 121 | view.measure(widthMeasureSpec, heightMeasureSpec); 122 | ``` 123 | wrap_content: 124 | ``` 125 | int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST); 126 | int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST); 127 | view.measure(widthMeasureSpec, heightMeasureSpec); 128 | ``` 129 | 130 | 131 | #### 8、在Activity的onCreate方法中能不能获取宽高,在onResume方法呢?ui绘制流程和Activity的生命周期有什么关系,ui开始绘制的时机到底是什么时候? 132 | 133 | 在onCreate中获取View的宽高都是0,因为View还没有进行测量和绘制,在onResume方法中同样也无法获取到View的宽高。 134 | 135 | 在Activity的attach方法中,会为Activity创建一个Window对象,在`Activity.handleResumeActivity`方法中才会去创建ViewRootImpl并添加视图,这个过程在`performResumeActivity`方法调用完成之后,所以在onResume中也不能获取到View的宽高,View还没有开始绘制。所以UI绘制的起点是在onResume方法调用完成之后。 136 | 137 | 138 | #### 9、Android能够在子线程中更新UI吗?为什么要设计成不能在子线程中更新UI? 139 | 140 | ``` 141 | void checkThread() { 142 | if (mThread != Thread.currentThread()) { 143 | throw new CalledFromWrongThreadException( 144 | "Only the original thread that created a view hierarchy can touch its views."); 145 | } 146 | } 147 | ``` 148 | 149 | 更新UI是监测是否为UI线程是由ViewRootImpl的checkThread方法来完成的,而ViewRootImpl的创建是在`ActivityThread.handleResumeActivity`方法中。所以在第一次onResume方法调用的时候或者之前的回调方法中,在子线程中更新UI是不会抛出异常的。 150 | 151 | 152 | 这样设计的考虑:Android的UI操作并不是线程安全的,如果允许在子线程中更新UI的话,必然会导致界面混乱。想要避免这个问题就必须采用加锁来保证UI操作的线程安全,这势必会导致效率的下降。所以为了提高UI操作的效率和UI操作的线程安全,就禁止在子线程中更新UI。 153 | 154 | 155 | #### [10、invalidate、postInvalidate、requestLayout的区别。](https://blog.csdn.net/a553181867/article/details/51583060) 156 | 157 | - invalidate:请求重绘View树(也就是draw方法),如果View大小没有发生变化就不会调用layout过程,并且指挥绘制哪些需要重绘的View,也就是哪个View(View只绘制该View,ViewGroup绘制整个ViewGroup)请求invalidate方法就绘制该View。 158 | - postInvalidate:invalidate方法只能在UI线程中请求,postInvalidate可以在非UI线程中调用。 159 | - requestLayout:如果view的尺寸和布局被改变,那么requestLayout()方法会调用measure过程和layout过程和draw过程。 160 | 161 | 162 | #### 11、自定义View的流程,自定义View需要注意的问题,例如自定义View是否需要重写onLayout,onMeasure。 163 | 164 | **自定义View的流程:** 165 | 166 | - 集成特定的View(如TextView)。 167 | 168 | 主要用于扩展已有的View的功能,这种方法不需要自己支持wrap_content和padding。 169 | 170 | - 继承特定的ViewGroup(如LinearLayout) 171 | 172 | 主要用于将几种View进行组合,这种方法不需要自己处理ViewGroup的测量和布局。 173 | 174 | - 继承View重写onDraw方法。 175 | 176 | 主要用于不规则的效果,这种方式需要对View进行绘制,重写onDraw方法,并且采用这种方法需要自己支持wrap_content和padding。 177 | 178 | - 继承ViewGroup。 179 | 180 | 相当于自定义新的布局,这种方式需要自己处理ViewGroup的测量和布局两个过程,并且同时处理子视图的测量和布局过程。 181 | 182 | **注意点:** 183 | - 集成子View或ViewGroup的控件,如果不在onMeasure方法中对warp_content进行处理,那么设中为warp_content的时候,实际的大小为parentSize。 184 | - 直接继承自ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子视图margin对其造成的影响,不然将导致padding和子视图的margin失效。 185 | - View带有滑动嵌套情形时,需要处理好滑动冲突。 186 | - View中如果有线程或者动画,需要及时停止。 187 | 188 | 当包含View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow,当View变得不可见时同样需要停止线程和动画,否则可能会造成内存泄漏。 189 | 190 | 191 | #### 12、动画的原理,底层如何给上层信号。 192 | 193 | - 补间动画:通过确定开始的视图样式和结束的视图样式,中间变化过程由系统补全。包括平移、缩放、旋转和透明度四种变换。**只能作用于View。** 194 | 195 | - 逐帧动画:将动画拆分成帧的形式,且定义一帧为一张图片,按顺序播放一组预先定义好的图片。 196 | 197 | - 属性动画:在一定时间间隔内,通过不断对值进行改变和不断查找对象的set方法将值赋给该对象,从而实现该对象在该属性上的动画效果。**可以作用任意对象。** 198 | 199 | 200 | #### [13、插值器和估值器。](https://blog.csdn.net/carson_ho/article/details/72863901) 201 | 202 | - 插值器:设置属性值从初始值过渡到结束值的变化规律,即确定了动画变化的趋势,如匀加速,匀减速等。 203 | 204 | - 估值器:设置属性值从初始值过渡到结束值的变化具体数值,即确定了动画变化的具体效果。如修改颜色,大小等。 205 | 206 | 207 | #### 14、[RecyclerView和ListView复用机制。](https://www.jianshu.com/p/9306b365da57) 208 | 209 | Recycler是RecyclerView的内部类,是整个复用机制的核心,专门向外提供复用机制的api。Recycler的几个属性变量十分重要。 210 | - mCachedViews:默认大小为2,可以理解为一级缓存,只有跟中条件都匹配成功(postion是否一致,type是否相同,ViewHolder是否被remove掉),就可以直接进行复用,无需绑定数据。 211 | 212 | - RecyclerViewPool:是RecyclerView的内部类,二级缓存,会根据不同的item type来创建不同的List,每个List的默认大小为5。只要type值匹配成功,那么就可以复用,需要调用onBindViewHolder方法,绑定数据。 213 | 214 | 复用机制: 215 | 216 | - 滑动过程中的回收和复用都会优先从mCacheViews集合中查找,只有原来的卡位可以重新复用这个 ViewHolder,新位置的卡位无法从 mCachedViews 里拿 ViewHolder出来用。 217 | 218 | 怎么理解呢:当滑动RecyclerView使一二个位置上的item移出屏幕,那么会被优先缓存到这个集合中,当再次滑动使得一二位置上的item移入屏幕,那么见直接从mCacheViews中查找,如找到直接复用,不需要重新填充数据。 219 | 220 | - 如果mCachedViews中没有找到可以复用的ViewHolder,那么将在ViewPool中查找。不同于mCacheViews的查找规则那么严格(postion是否一致,type是否相同,ViewHolder是否被remove掉),ViewPool中只要存在匹配上type,那么就可以复用,并且会重新调用onBindViewHolder绑定数据。 221 | 222 | - 如果以上都没有找到可以复用的ViewHodler,那么就会调用onCreateViewHolder创建新的ViewHolder。 223 | 224 | 回收机制: 225 | 226 | - 首先会将回收的ViewHolder放入mCacheViews中,如果mCacheView已经满了,那么就会将mCacheViews中第0个位置上的ViewHolder放到ViewPool中去,第1的位置向前移动,然后在放入最新的ViewHolder。 227 | 228 | - 回收完成之后会回调onRecycled方法。 229 | 230 | 231 | #### 15、处理滑动的几种方式,Scroller滑动的原理。 232 | 233 | ![滑动冲突场景](https://github.com/chen-eugene/Interview/blob/master/image/1543826555.png) 234 | 235 | - 场景1:外部滑动方向和内部滑动方向不一致。如ScrollView嵌套RecyclerView。 236 | 237 | 处理规则:当用户左右滑动时,需要让外部的View拦截事件,当用户上下滑动时,需要让内部的View拦截事件。 238 | 239 | 240 | - 场景2:外部滑动方向和内部滑动方向一致。 241 | 242 | 处理规则:根据业务状态,来决定让那一层的View滑动。 243 | 244 | - 场景3:场景1和场景2的嵌套。 245 | 246 | 247 | #### 16、简述工作线程更新UI的方法。 248 | 249 | - 通过handler.sendMessage,更新UI主线程。 250 | 251 | - 通过handler.post,更新UI主线程。 252 | 253 | - 使用runOnUiThread,更新UI主线程。 254 | 255 | - 通过view.post,更新UI主线程。 256 | 257 | 258 | 259 | #### 17、Selector是怎么实现的。 260 | 261 | 262 | #### [18、View动画是怎么实现的,为什么移动后点击事件还在原来的位置,属性动画的原理机制。](https://www.jianshu.com/p/3530ba1e9885) 263 | 264 | **补间动画:** 265 | ``` 266 | View通过调用startAnimation()方法设置补间动画。 267 | public void startAnimation(Animation animation) { 268 | animation.setStartTime(Animation.START_ON_FIRST_FRAME); 269 | setAnimation(animation); 270 | invalidateParentCaches(); 271 | invalidate(true); 272 | } 273 | ``` 274 | 由此可见补间动画是通过调用invalidate()方法来进行重绘,只会触发onDraw方法,不会调用onMeasure和onLayout方法,也就是说虽然View的原本位置是没有改变的。 275 | 276 | **属性动画:** 277 | 属性动画设置完成之后调用start开始执行动画,最终会执行到ValueAnimator的shceduleAnimation方法 278 | ``` 279 | private void scheduleAnimation() { 280 | if (!mAnimationScheduled) { 281 | mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null); 282 | mAnimationScheduled = true; 283 | } 284 | } 285 | ``` 286 | 由此可见属性动画通过Choregrapher分发VSnyc信号来更新的,这个过程如果检测到View的属性值发生变化,那么将会重新执行View的onMeasure、onLayout和onDraw这个过程。 287 | ``` 288 | final Runnable mAnimate = new Runnable() { 289 | @Override 290 | public void run() { 291 | mAnimationScheduled = false; 292 | doAnimationFrame(mChoreographer.getFrameTime()); 293 | } 294 | }; 295 | 296 | void initAnimation() { 297 | if (!mInitialized) { 298 | // mValueType may change due to setter/getter setup; do this before calling super.init(), 299 | // which uses mValueType to set up the default type evaluator. 300 | final Object target = getTarget(); 301 | if (target != null) { 302 | final int numValues = mValues.length; 303 | for (int i = 0; i < numValues; ++i) { 304 | mValues[i].setupSetterAndGetter(target);//会通过反射去查找target的Setter和Getter方法,并存储在HashMap中 305 | } 306 | } 307 | super.initAnimation(); 308 | } 309 | } 310 | ``` 311 | 在这个过程中会执行到initAnimation()方法,ObjectAnimator重写了此方法,并调用setupSetterAndGetter查找Setter和Getter方法并存储在HashMap中,然后会在ValueAnimator的animationFrame方法中对target属性进行赋值。 312 | 313 | 所以说补间动画不会修改View的实际位置,只会修改View的显示位置,而属性动画将会对View进行重新测量、布局和绘制,会修改View的实际位置。 314 | 315 | #### 19、Android多点触控。 316 | 317 | 318 | #### 20、ViewPager、RecyclerView和SrcollView嵌套滑动冲突,显示不完整,滑动卡顿问题。 319 | 320 | 321 | #### 21、滑动卡顿如何解决(不同原因及对应处理方式)。 322 | 323 | 324 | #### [22、Native和js交互流程。](https://blog.csdn.net/carson_ho/article/details/64904691) 325 | 326 | - Native调用js: 327 | - 通过WebView的loadUrl() 328 | - JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。 329 | - 效率低,会使页面重新刷新,不能获取返回值 330 | - 通过WebView的evaluateJavascript() 331 | - 效率高,但只支持4.4以上版本 332 | 333 | - js调用Native: 334 | - 通过WebView的addJavascriptInterface()进行对象映射 335 | - [在Android4.2以下,存在安全漏洞](https://www.jianshu.com/p/3a345d27cd42) 336 | - 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url 337 | - 操作麻烦,不方便获取返回值。 338 | - 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息 339 | - 使用复杂 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | -------------------------------------------------------------------------------- /Android/七、常用框架.md: -------------------------------------------------------------------------------- 1 | 目录: 2 | - [图片下载](#图片下载) 3 | - [网络请求](#网络请求) 4 | - [网络缓存](#网络缓存) 5 | - [响应式编程](#响应式编程) 6 | - [性能优化](#性能优化) 7 | - [依赖注入](#依赖注入) 8 | 9 | ### 图片下载 10 | 11 | #### 1、Glide传入不同Context的区别,如何绑定生命周期的。 12 | - 因为Glide需要知道加载的生命周期。如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片就不应当继续加载。 13 | 14 | - Glide怎么感知Activity的生命周期呢:Glide添加了隐藏Fragment,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。 15 | ``` 16 | @NonNull 17 | public RequestManager get(@NonNull FragmentActivity activity) { 18 | if (Util.isOnBackgroundThread()) { 19 | return get(activity.getApplicationContext()); 20 | } else { 21 | assertNotDestroyed(activity); 22 | FragmentManager fm = activity.getSupportFragmentManager(); 23 | return supportFragmentGet( 24 | activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); 25 | } 26 | } 27 | ``` 28 | 29 | #### [2、Glide使用什么缓存,何如设置缓存大小。](https://juejin.im/entry/5ab7aa2ef265da237410f181) 30 | - 内存缓存分为两部分: 31 | - 一部分使用了LruCache算法机制; 32 | - 另一部分使用了弱引用,用来缓存正在使用中的图片,可以保护这些图片不会被LruCache算法回收掉。 33 | 34 | 优先使用弱引用机制来缓存正在使用的图片,当图片不在使用时,就将图片移到LruCache缓存中。 35 | 36 | 从LruCache中获取到缓存图片之后会将它从缓存中移除,然后放到弱引用缓存中去。 37 | 38 | - 磁盘缓存:该算法基于 Lru 算法中的DiskLruCache算法。 39 | 40 | - Bitmap缓存池:对象池减少了生成和回收对象的频率,减少了GC的频率,由于是按需生成且存在缓存,所以原则上,只要及时清理缓存,是不会造成内存浪费的。 41 | 42 | 缓存大小的计算:计算屏幕的字节大小 = 宽 * 高 * 4字节(ARGB 8888),然后再通过屏幕大小乘以比例参数最终得到bitmap池的大小和内存缓存的大小。 43 | ``` 44 | int widthPixels = builder.screenDimensions.getWidthPixels(); 45 | int heightPixels = builder.screenDimensions.getHeightPixels(); 46 | int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL; 47 | 48 | int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens); 49 | int targetMemoryCacheSize = Math.round(screenSize * builder.memoryCacheScreens); 50 | ``` 51 | 52 | 可以通过继承GlideModule的方式来修改Glide的初始化参数。 53 | 54 | #### 3、Glide优势是什么。 55 | 56 | 57 | ### 网络请求 58 | 59 | 60 | #### [1、OkHttp采用的是哪种线程池,这样设计的好处是什么。](https://blog.piasy.com/2016/07/11/Understand-OkHttp/) 61 | ``` 62 | asyncCall.executeOn(executorService()); 63 | 64 | public synchronized ExecutorService executorService() { 65 | if (executorService == null) { 66 | executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, 67 | new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false)); 68 | } 69 | return executorService; 70 | } 71 | 72 | private boolean promoteAndExecute() { 73 | assert (!Thread.holdsLock(this)); 74 | 75 | List executableCalls = new ArrayList<>(); 76 | boolean isRunning; 77 | synchronized (this) { 78 | for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) { 79 | AsyncCall asyncCall = i.next(); 80 | 81 | if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity. 82 | if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity. 83 | 84 | i.remove(); 85 | executableCalls.add(asyncCall); 86 | runningAsyncCalls.add(asyncCall); 87 | } 88 | isRunning = runningCallsCount() > 0; 89 | } 90 | 91 | for (int i = 0, size = executableCalls.size(); i < size; i++) { 92 | AsyncCall asyncCall = executableCalls.get(i); 93 | asyncCall.executeOn(executorService()); 94 | } 95 | 96 | return isRunning; 97 | } 98 | ``` 99 | - ① Dispathcer参与异步任务的调度,内部维护一个readyAsyncCalls等待执行的队列和runningAsyncCalls正在执行的队列。 100 | - ② 线程池的核心线程数为0,最大线程数为Integer.MAX_VALUE,线程空闲时存活时间为60秒,SynchronousQueue是不会保存任务,所以只要把任务添加进去就会执行。 101 | - ③ OkHttp不是在线程池中维护线程的个数的,线程数是在Dispatcher直接控制的,根据maxRequests和maxRequestsPerHost来调整runningAsyncCalls和readyAsyncCalls,使运行中的异步请求不超过两种最大值。 102 | 103 | **个人认为使用Dispatcher控制线程数量而不使用线程池来控制:** 104 | - ① 使用线程池来控制线程数的话不好确定线程池的最大线程数,如果最大线程数设置太大了,就达不到控制并发量的目的,太小了就可能造成任务丢失。 105 | - ② 线程池的最大线程数一旦确认就不能再进行修改,而使用Dispatcher来控制的话用户就可以根据需要修改最大的线程数。 106 | 107 | 108 | #### [2、OkHttp怎么实现网络连接复用的。](https://www.jianshu.com/p/a2fcf1dad6b5) 109 | 110 | [OkHttp连接和连接管理](https://www.jianshu.com/p/671a123ec163) 111 | 112 | **连接的复用:** 113 | - Call:对Http请求的封装 114 | - Connection/RealConnection:物理连接的封装,其内部有List>的引用计数 115 | - ConnecitonPool:管理连接和复用连接 116 | - StreamAllocation:连接与流的桥梁,负责管理一个连接上的流,同时在connection中也通过一个StreamAllocation的引用的列表来管理一个连接的流,从而使得连接与流之间解耦。 117 | - HttpCodec:流对象,用于读取和写入数据,对应不同版本的Http协议。 118 | 119 | ``` 120 | @Override 121 | public Response intercept(Chain chain) throws IOException { 122 | RealInterceptorChain realChain = (RealInterceptorChain) chain; 123 | Request request = realChain.request(); 124 | StreamAllocation streamAllocation = realChain.streamAllocation(); 125 | 126 | // We need the network to satisfy this request. Possibly for validating a conditional GET. 127 | boolean doExtensiveHealthChecks = !request.method().equals("GET"); 128 | HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); 129 | RealConnection connection = streamAllocation.connection(); 130 | 131 | return realChain.proceed(request, streamAllocation, httpCodec, connection); 132 | } 133 | ``` 134 | 在Connection.intercept方法中,stramAllocation.newStream方法负责寻找连接,并在该连接上创建新的流对象并返回。 135 | 136 | 在streamAllocation.findConnection方法中: 137 | - 查看当前streamAllocation是否有之前已经分配过的连接,有则直接使用 138 | - 从连接池中查找可复用的连接,有则返回该连接 139 | - 配置路由,配置后再次从连接池中查找是否有可复用连接,有则直接返回 140 | - 新建一个连接,并修改其StreamAllocation标记计数,将其放入连接池中 141 | - 查看连接池是否有重复的多路复用连接,有则清除 142 | 143 | 144 | **连接的回收:** 145 | ConnectionPool中有一个独立的cleanupRunnable来清理连接池。其触发时机有两个: 146 | - 当连接池中put新的连接时 147 | - 当connectionBecameIdle接口被调用时 148 | 149 | #### [3、为什么使用Retrofit,Retrofit与其它网络库有什么优势。](https://blog.csdn.net/carson_ho/article/details/73732115) 150 | 151 | Retrofit 本质上是一个 RESTful 的HTTP 网络请求框架的封装,即通过 大量的设计模式 封装了 OkHttp ,使得简洁易用。 152 | - Retrofit 将 Http请求 抽象 成 Java接口。 153 | - 在接口里用 注解 描述和配置 网络请求参数。 154 | - 用动态代理 的方式,动态将网络请求接口的注解解析成HTTP请求。 155 | - 最后执行HTTP请求。 156 | 157 | 158 | ### 网络缓存 159 | 160 | 161 | #### 1、OkHttp缓存策略的缺点。 162 | 163 | OkHttp缓存的存储规则是key-value的方式,不便于进行动态配置,以发起的请求request作为key,当网络参数变化的时候(如① 不同的网络环境下url会相应变化,在wifi的网络环境下:?id=1&name=”Hello”&network=”Wifi”;② 如果url中带有token),网络缓存就会失效。 164 | 165 | 166 | ### 响应式编程 167 | 168 | #### [1、什么是响应式编程,它有什么优点。](https://github.com/hehonghui/android-tech-frontier/tree/master/androidweekly/那些年我们错过的响应式编程) 169 | 170 | **响应式编程就是与异步数据流交互的编程范式。** 171 | 简单来说,响应式编程的思路就是: 172 | 173 | 任何东西都可以成为一个数据流,比如变量、用户输入、属性、缓存等,这样你就可以对这样的数据流进行监听,并作出相应的操作。比如通过一些函数去组合、创建和过滤任何一组数据流。并且一个数据流可以作为另一个数据流的输入,甚至多个数据流也可以作为另一个数据流的输入。 174 | 175 | **数据流是整个响应式编程体系中的核心。** 176 | 177 | 178 | #### [2、RxJava切换线程的原理。](http://lyldalek.cn/2018/01/06/RxJava-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%901/) 179 | 180 | RxJava的调用采用的是责任链模式,和OkHttp的调用链类似。 181 | 182 | - 为什么subsribeOn()只能第一次切换有效 183 | 184 | `subscribeOn()`的线程切换发生在`ObservableSubscribeOn`中,即在通知上一级`OnSubscribe`的时候,这时的事件流还没有开始发送,所以`subscribeOn()`的线程控制可以从事件发送的开端就造成影响,即只有第一个`subscribeOn()`有效。 185 | 186 | ``` 187 | final class SubscribeTask implements Runnable { 188 | private final SubscribeOnObserver parent; 189 | 190 | SubscribeTask(SubscribeOnObserver parent) { 191 | this.parent = parent; 192 | } 193 | 194 | @Override 195 | public void run() { 196 | source.subscribe(parent); 197 | } 198 | } 199 | ``` 200 | 201 | `observeOn()`的线程切换则发生在内部创建的Observer中,即发生在它即将给下一级发送事件流的时候,因此`observeOn()`可以控制它后面的线程。 202 | 203 | ``` 204 | protected void subscribeActual(Observer observer) { 205 | if (scheduler instanceof TrampolineScheduler) { 206 | source.subscribe(observer); 207 | } else { 208 | Scheduler.Worker w = scheduler.createWorker(); 209 | 210 | source.subscribe(new ObserveOnObserver(observer, w, delayError, bufferSize)); 211 | } 212 | } 213 | ``` 214 | 215 | - observableOn(AndroidSchedulers.mainThread())通过Handler将线程切换到主线程, 216 | ``` 217 | static final Scheduler DEFAULT 218 | = new HandlerScheduler(new Handler(Looper.getMainLooper()), false); 219 | 220 | final class HandlerScheduler extends Scheduler { 221 | 222 | public Disposable schedule(Runnable run, long delay, TimeUnit unit) { 223 | ...... 224 | 225 | run = RxJavaPlugins.onSchedule(run); 226 | ScheduledRunnable scheduled = new ScheduledRunnable(handler, run); 227 | Message message = Message.obtain(handler, scheduled); 228 | message.obj = this; // Used as token for batch disposal of this worker's runnables. 229 | 230 | handler.sendMessageDelayed(message, unit.toMillis(delay)); 231 | 232 | if (disposed) { 233 | handler.removeCallbacks(scheduled); 234 | return Disposables.disposed(); 235 | } 236 | return scheduled; 237 | } 238 | 239 | } 240 | ``` 241 | 242 | #### 3、RxJava的线程池与自己实现的任务管理框架有什么区别。 243 | 244 | **RxJava所有的线程创建都是采用的是一个核心线程数为1,最大线程数不限制的线程池来代替的,避免直接通过`new Thread()`。** 245 | 246 | 个人认为:这样做的好处和OkHttp的线程管理有异曲同工之妙 247 | 248 | ``` 249 | public NewThreadWorker(ThreadFactory threadFactory) { 250 | executor = SchedulerPoolFactory.create(threadFactory); 251 | } 252 | ``` 253 | 254 | - Schedulers.io:内部创建了一个队列用来缓存线程池,对线程进行复用。创建一个核心线程池为1,最大线程数不限制的线程池,适用于非CPU密集的I/O工作,比如访问文件系统、执行网络调用、访问数据库等。 255 | ``` 256 | CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { 257 | this.expiringWorkerQueue = new ConcurrentLinkedQueue(); 258 | } 259 | ``` 260 | - Schedulers.compulation:创建一个大小为CPU数量的数组,用于存放线程池,线程池先用的是一个核心线程数为1,最大线程数不限制的线程池(这点和Schedulers.io()是一样的),这样做的好处就是可以避免任务过多造成丢失。用于执行CPU密集的工作,比如处理大规模的数据集、图像处理等。 261 | ``` 262 | MAX_THREADS = cap(Runtime.getRuntime().availableProcessors(), Integer.getInteger(KEY_MAX_THREADS, 0)); 263 | 264 | this.eventLoops = new PoolWorker[maxThreads]; 265 | for (int i = 0; i < maxThreads; i++) { 266 | this.eventLoops[i] = new PoolWorker(threadFactory); 267 | } 268 | ``` 269 | - Schedulers.newThread():每次都会为任务创建一个线程池,并没有对线程做任何的缓存和复用处理。 270 | - Schedulers.single():内部只有一个线程池,当多个任务的时候只能够同步的执行。 271 | 272 | 273 | 274 | 275 | ### 性能优化 276 | 277 | #### [1、LeakCanary的实现原理](https://ivanljt.github.io/blog/2017/12/15/%E6%8B%86%E8%BD%AE%E5%AD%90%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94LeakCanary%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/) 278 | 279 | LeakCanary的基本原理:通过注册`Application.ActivityLifecycleCallbacks`来监听Activity的onDestroy方法,手动调用GC,然后利用ReferenceQueue+WeakReference来判断Activity是否被释放掉。 280 | ``` 281 | public void watchActivities() { 282 | this.stopWatchingActivities(); 283 | this.application.registerActivityLifecycleCallbacks(this.lifecycleCallbacks); 284 | } 285 | 286 | public void onActivityDestroyed(Activity activity) { 287 | ActivityRefWatcher.this.onActivityDestroyed(activity); 288 | } 289 | 290 | void onActivityDestroyed(Activity activity) { 291 | this.refWatcher.watch(activity); 292 | } 293 | 294 | public void watch(Object watchedReference, String referenceName) { 295 | Preconditions.checkNotNull(watchedReference, "watchedReference"); 296 | Preconditions.checkNotNull(referenceName, "referenceName"); 297 | if (!this.debuggerControl.isDebuggerAttached()) { 298 | final long watchStartNanoTime = System.nanoTime(); 299 | String key = UUID.randomUUID().toString(); 300 | this.retainedKeys.add(key); 301 | final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, this.queue); 302 | this.watchExecutor.execute(new Runnable() { 303 | public void run() { 304 | RefWatcher.this.ensureGone(reference, watchStartNanoTime); 305 | } 306 | }); 307 | } 308 | } 309 | ``` 310 | 监听Activity的生命周期,当Activity被销毁时回调onDestroy方法,最后会回调到`RefWatcher#watch()`。 311 | KeyedWeakReference继承WeakReference,将Activity的对象添加到WeakRefrence中去,并传入一个引用队列,然后将生成一个String类型的Key,并作为KeyedWeakReference的成员变量进行赋值,然后将key添加到set集合中去,手动调用`Runtime.getRuntime().gc();`,检查引用队列来判断Activity是否被回收。 312 | 313 | 314 | #### [2、BlockCanary实现原理。](http://blog.zhaiyifan.cn/2016/01/16/BlockCanaryTransparentPerformanceMonitor/) 315 | 316 | 利用了Looper.loop()中每个Message被分发前后的Log打印,而我们设置自己的Printer就可以根据Log的不同的处理: 317 | ``` 318 | public static void loop() { 319 | final Looper me = myLooper(); 320 | 321 | final MessageQueue queue = me.mQueue; 322 | for (;;) { 323 | Message msg = queue.next(); // might block 324 | // This must be in a local variable, in case a UI event sets the logger 325 | Printer logging = me.mLogging; 326 | if (logging != null) { 327 | logging.println(">>>>> Dispatching to " + msg.target + " " + 328 | msg.callback + ": " + msg.what); 329 | } 330 | // focus 331 | msg.target.dispatchMessage(msg); 332 | 333 | if (logging != null) { 334 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 335 | } 336 | } 337 | msg.recycleUnchecked(); 338 | } 339 | } 340 | 341 | public class BlockDetectByPrinter { 342 | 343 | public static void start() { 344 | 345 | Looper.getMainLooper().setMessageLogging(new Printer() { 346 | 347 | private long startTime; 348 | private long blockThreshold; 349 | private long startedPrinting; 350 | 351 | @Override 352 | public void println(String x) { 353 | if (!startedPrinting) { 354 | mStartTimeMillis = System.currentTimeMillis(); 355 | startedPrinting = SystemClock.currentThreadTimeMillis(); 356 | mStartedPrinting = true; 357 | } else { 358 | final long endTime = System.currentTimeMillis(); 359 | startedPrinting = false; 360 | //判断是否为卡顿 361 | if (isBlock(endTime)) { 362 | StringBuilder sb = new StringBuilder(); 363 | //打印栈信息 364 | StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace(); 365 | for (StackTraceElement s : stackTrace) { 366 | sb.append(s.toString() + "\n"); 367 | } 368 | } 369 | } 370 | } 371 | }); 372 | } 373 | 374 | private boolean isBlock(long endTime) { 375 | return endTime - startTime > blockThreshold; 376 | } 377 | 378 | } 379 | ``` 380 | 可以通过 `Looper.getMainLooper().setMessageLogging(mainLooperPrinter);` 381 | 并在mainLooperPrinter中判断start和end,来获取主线程dispatch该message的开始和结束时间,并判定该时间超过阈值(如2000毫秒)为主线程卡慢发生,并dump出各种信息。 382 | 383 | 384 | ### 依赖注入 385 | 386 | #### 1、@Lazy、@Provider、@Qualifier、@Named注解的使用 387 | 388 | - @Lazy:只有在Lazy的get()方法调用的时候才会初始化实例,注入依赖。 389 | - @Provider:有时候不仅仅是注入单个实例,我们需要多个实例,这时可以使用注入Provider,每次调用它的 get() 方法都会调用到 @Inject 构造函数创建新实例或者 Module 的 provide 方法返回实例。 390 | - @Qualifier:当提供多个同一类型的类实例的时候,需要标注来源,不然会出现依赖迷失。@Qualifier修饰注解。 391 | - @Named:同@Qualifier功能一样,@Named直接传入一个字符串。 392 | 393 | #### [2、@Component.Builder和@BindsInstance注解的作用。](https://juejin.im/post/5a4cf2b2f265da430d586ace) 394 | 395 | - @Component.Builder和@BindsInstance用于自定义Builder类,被@BindsInstance注解的方法里面的参数在Builder类中生成对应的成员变量。 396 | 397 | - 在Builder类中生成了对应的Provider **Providerchengyuan变量,并传入到要使用的module中去,通过**Provider.get()方法可以拿到实例。 398 | 399 | 使用场景: 400 | 401 | 如果module里面的某个被@Provider注解修饰的provider**(xxx obj)方法使用了某种类型的对象作为参数(比如appllication),而这个参数不想通过Module构造函数传递进来,那么可以使用@Component.Builder来定义Builder类,通过@BindsInstance注解的方法来提供这个参数。 402 | 403 | 404 | #### [3、@Component和@SubComponent注解的区别。](https://juejin.im/entry/587499ff128fe1006b42f7de) 405 | 406 | 通常情况下会有一个BaseComponent,子Component会依赖BaseComponent来提高代码的复用性,实现这种扩展有两种方式: 407 | - `@Component(dependencies = BaseComponent.class,modules = ...)` 408 | 409 | - 使用@subComponent(modules = ...)标记Component,同时在BaseComponent中提供一个返回子Component对象的方法。 410 | ``` 411 | @Singleton 412 | @Subcomponent(modules = UserModule.class) 413 | public interface UserComponent { 414 | void inject(MainActivity activity); 415 | } 416 | 417 | @Component(modules = PackModule.class) 418 | public interface PackComponent { 419 | // 提供 UserComponent 对象的获取方法 420 | UserComponent userComponent(UserModule module); 421 | } 422 | ``` 423 | **不同之处:** 424 | - 通过dependencies依赖的方式,子Component只能获取到BaseComponent中暴露的对象,如果在BaseComponent中没有暴露,那么就获取不到。 425 | - 通过@SubComponent则可以获取BaseComponent中的所有的对象。 426 | 427 | **使用场景:** 428 | - 想要明确的知道两个Component的依赖关系,使用`@Component(dependecies = ...)` 429 | - 想让两个Component内聚,不关心这个Component依赖的哪一个Component,使用@SubComponent 430 | 431 | 432 | #### [4、@Scope注解的作用。](https://juejin.im/entry/587499ff128fe1006b42f7de) 433 | 434 | 简单地说,被@Scope修饰的对象,在他的整个生命周期内,通过注入的成员变量是单例的。@Scope的默认实现是@SingleTon,在全局的单例。 435 | ``` 436 | @Scope 437 | @Rentention(RUNTIME) 438 | public @interface ActivityScope{} 439 | 440 | @module 441 | public class UserModule{ 442 | 443 | @Provider 444 | @ActivityScope 445 | User providerUser(){ 446 | return new User(); 447 | } 448 | 449 | } 450 | 451 | @ActivityScope 452 | public MainActivity extend AppCompatActivity{ 453 | 454 | @Inject 455 | User mUser; 456 | 457 | } 458 | 459 | ``` 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | -------------------------------------------------------------------------------- /Java/四、Java并发(Concurrent).md: -------------------------------------------------------------------------------- 1 | #### [1、什么是线程,什么是进程,能不能只用进程。](https://segmentfault.com/a/1190000005884656) 2 | - 进程(Process):是系统进行资源分配和调度的基本单位,是操作系统结构的基础。一个进程可以看作一个程序或者一个应用 3 | - 线程(thread):是程序执行的最小单元。线程是在进程中执行的一个任务。 4 | 5 | 引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。 6 | 7 | 区别: 8 | - ① 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 9 | - ② 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。 10 | - ③ 调度和切换:线程上下文切换比进程上下文切换要快得多。 11 | - ④ 在多线程OS中,进程不是一个可执行的实体。 12 | 13 | #### [2、多线程编程的好处是什么。](https://www.cnblogs.com/dolphin0520/p/3910667.html) 14 | 15 | 多个线程被并发的执行可以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。线程的创建只需要较少的资源,因此创建多个线程去执行任务比创建多个进程更好。 16 | 17 | #### [3、一个线程包含了哪些状态。](https://www.cnblogs.com/dolphin0520/p/3920357.html) 18 | 19 | ![线程状态](https://github.com/chen-eugene/Interview/blob/master/image/061046391107893.jpg) 20 | 21 | - 可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。 22 | - 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。 23 | - 阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu时间片,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu 时间片转到运行(running)状态。阻塞的情况分三种: 24 | - ① 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。 25 | - ② 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 26 | - ③ 其他阻塞: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。 27 | - 死亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。 28 | 29 | 30 | #### 4、wait()和sleep()的区别。 31 | - sleep:让线程睡眠,交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。 32 | - wait:wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。 33 | - yield:调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。 34 | 注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。 35 | - join:调用join方法是调用了Object的wait方法,由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。 36 | 37 | 38 | #### 5、有三个线程T1,T2,T3,怎么确保它们按顺序执行? 39 | 40 | 可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。 41 | 42 | 43 | #### 6、如何停止一个线程。 44 | 45 | ![线程状态](https://github.com/chen-eugene/Interview/blob/master/image/db150002205d9bc30b8f.jpg) 46 | 47 | 早期的jdk版本中提供了退出的方法:hread.stop, Thread.suspend, Thread.resume和Runtime.runFinalizersOnExit ,因为操作不安全,可能会出现不可控的结果,已不推荐使用。 48 | 49 | - ① 等线程自己执行完结束(这种最优雅,但是也没有讨论的意义了)。 50 | 51 | - ② 调用中断方法,判断中断标识。但是waiting状态下会抛异常,不能算作优雅。 52 | 53 | intercept:单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。直接调用interrupt方法不能中断正在运行中的线程。 54 | 55 | ``` 56 | public class Test { 57 | 58 | public static void main(String[] args) throws IOException { 59 | Test test = new Test(); 60 | MyThread thread = test.new MyThread(); 61 | thread.start(); 62 | try { 63 | Thread.currentThread().sleep(2000); 64 | } catch (InterruptedException e) { 65 | 66 | } 67 | thread.interrupt(); 68 | } 69 | 70 | class MyThread extends Thread{ 71 | @Override 72 | public void run() { 73 | int i = 0; 74 | while(!isInterrupted() && i③->②,在多线程环境下会出现uniqueInstance没有初始化的情况。而volatile关键在在一定程度上禁止了JVM的这种指令重排序。 219 | 220 | 221 | #### [14、讲一讲synchronized关键字的底层原理。](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&token=1604028915&lang=zh_CN#rd) 222 | 223 | synchronized关键字底层原理属于JVM层面,问的有点深了。 224 | 225 | - synchronized代码块:synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。 226 | 227 | 当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权。当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。 228 | 229 | - synchronized方法:synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。 230 | 231 | 232 | #### [15、讲讲 JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&token=1604028915&lang=zh_CN#rd) 233 | 234 | 这个问题也问的比较深。 235 | 236 | JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。 237 | 238 | 锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 239 | 240 | 241 | #### 16、谈谈synchronized和ReenTrantLock的区别。 242 | 243 | - ① 两者都是可重入锁,相比于synchronized,Lock不是Java语言内置的,Lock是一个接口,ReenTrantLock实现了Lock。 244 | 245 | 可重入锁:自己可以再次获得自己已经获的对象锁,比如一个线程访问了某个对象的同步方法fun1,此时这个线程又去访问这个对象的另一个同步方法fun2,这个线程可以再次获取这个对象的锁,如果是不可重入锁的话,将会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。 246 | 247 | - ② synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API。 248 | - ③ 使用synchronized时不需要去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。 249 | - ⑤ synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁; 250 | - ⑤ 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。 251 | 252 | 相比于synchronized,ReenTrantLock所具有的高级特性: 253 | - 等待可中断:ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。使用synchronized时,等待的线程会一直等待下去,不能够响应中断。 254 | - 可实现公平锁:ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。 255 | - 公平锁:多个线程按照申请锁的顺序来获取锁,所谓的公平锁就是先等待的线程先获得锁。 256 | - 非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能会造成优先级反转或者饥饿现象。 257 | - 可实现选择性通知(锁可以绑定多个条件): 258 | - synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制。 259 | - ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。 260 | 261 | Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 262 | 263 | 在使用notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知” ,这个功能非常重要,而且是Condition接口默认提供的。 264 | 265 | 而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。 266 | Codition.await方法将会释放掉锁。 267 | 268 | 269 | #### [17、什么是线程死锁,线程死锁的四个条件。](https://www.ibm.com/developerworks/cn/java/j-lo-deadlock/index.html) 270 | 271 | **死锁是指多线程环境中,线程之间循环等待其它线程所占用的资源而无限期等待下去的局面。** 272 | 273 | 出现死锁的条件: 274 | - 互斥条件:一个资源每次只能被一个线程占有。 275 | - 请求和保持条件:线程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外线程占有,此时该线程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。 276 | - 不剥夺条件:进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。 277 | - 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 278 | 279 | [死锁的场景和如何避免死锁:](https://www.cnblogs.com/xiaoxi/p/8311034.html) 280 | - 加锁顺序(线程按照一定的顺序加锁)。 281 | - 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)。 282 | 283 | 284 | #### [18、锁的等级:方法锁、对象锁、类锁。](https://blog.csdn.net/qq_35181209/article/details/72627363) 285 | 286 | Java的所有对象都含有一个互斥锁,这个锁由JVM自动获取和释放。 287 | 288 | - 方法锁:synchronized修饰方法时,默认的是当前的对象作为锁对象。 289 | - 对象锁:修饰代码块时,需要一个锁对象。 290 | - 修饰代码块:需要指定锁对象,进入同步代码块前要获得指定对象的锁。 291 | 292 | **方法锁也是一种对象锁。** 293 | 294 | 295 | #### [19、Java锁的分类。](https://www.cnblogs.com/qifengshi/p/6831055.html) 296 | 297 | - **公平锁/非公平锁**: 298 | - 公平锁:多个线程按照申请锁的顺序来获取锁。 299 | - 非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。 300 | 301 | ReentrantLock:默认是非公平锁,可以通过构造函数参数设置成公平锁。Synchronized:是一种非公平锁。 302 | 303 | - **可重入锁**:同一个线程在访问某个对象加锁的方法时获取了该对象的锁,在访问这个过程中又去访问其他加锁的方法,将会自动获取锁。 304 | 305 | synchronized和ReentrantLock都是可重入锁。 306 | 307 | - **独享锁/共享锁**: 308 | - 独享锁:指该锁一次只能被一个线程所持有。 309 | - 共享锁:指该锁可被多个线程所持有。 310 | 311 | ReentrantLock和Synchronized都是独享锁,ReadWriteLock的读锁是共享锁,写锁是独享锁。 312 | 313 | - **互斥锁/读写锁**:独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。 314 | - 互斥锁在Java中的具体实现就是ReentrantLock,Synchronized。 315 | - 读写锁在Java中的具体实现就是ReadWriteLock。 316 | 317 | - **乐观锁/悲观锁**:乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。 318 | - 悲观锁:对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。 319 | - 乐观锁:对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的更新数据。乐观的认为,不加锁的并发操作是没有事情的。 320 | 321 | 使用悲观锁和独占锁(其实是一个意思)的方式好处就是简单安全,但是挂起线程和恢复线程都需要转入内核态进行,这样做会带来很大的性能开销。悲观锁的代表是 synchronized。然而在真是的环境中,大部分时候都不会产生冲突。悲观锁会造成很大的浪费。 322 | 323 | 而乐观锁不一样,它假设不会产生冲突,先去尝试执行某项操作,失败了再进行其他处理(一般都是不断循环重试)。这种锁不会阻塞其他的线程,也不涉及上下文切换,性能开销小。代表实现是 CAS。 324 | 325 | 悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景。 326 | 327 | - **分段锁**:分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。 328 | 329 | #### [20、sychronized、CAS和AQS的区别,什么是CAS和AQS](https://juejin.im/post/5c37377351882525ec200f9e) 330 | 331 | **CAS:** 332 | CAS 是 compare and swap 的简写,即比较并交换。它是指一种操作机制,而不是某个具体的类或方法。是乐观锁的一种实现方式。在 Java 平台上对这种操作进行了包装。在 Unsafe 类中,调用代码如下: 333 | ``` 334 | unsafe.compareAndSwapInt(this, valueOffset, expect, update); 335 | ``` 336 | - valueOffset:内存位置 337 | - expect:期望值 338 | - update:新值 339 | 340 | 它需要三个参数,分别是内存位置 V,旧的预期值 A 和新的值 B。操作时,先从内存位置读取到值,然后和预期值A比较。如果相等,则将此内存位置的值改为新值 B,返回 true。如果不相等,说明和其他线程冲突了,则不做任何改变,返回 false。 341 | 342 | 这种机制在不阻塞其他线程的情况下避免了并发冲突,比独占锁的性能高很多。 CAS 在 Java 的原子类和并发包中有大量使用。 343 | 344 | Java中原子类的实现原理就是采用CAS机制来解决并发,参见AtomicInteger的代码。 345 | 346 | CAS 就是通过比较和交换操作的原子性的。值得注意的是, CAS 只是保证了操作的原子性,并不保证变量的可见性,因此变量需要加上 volatile 关键字。 347 | 348 | **AQS:以ReentranLock实现非公平锁来看AQS。** 349 | 350 | AQS 全称 AbstractQueuedSynchronizer。是Java提供的一套基于等待队列实现锁的框架。AQS 中有两个重要的成员: 351 | - 成员变量 state:state 为0表示没有任何线程持有这个锁,线程持有该锁后将 state 加1,释放时减1。多次持有释放则多次加减。实现可重入锁。 352 | - 还有一个双向链表,链表除了头结点外,每一个节点都记录了线程的信息,代表一个等待线程。这是一个 FIFO 的链表。 353 | 354 | 锁请求的步骤: 355 | - 1. 如果没有线程持有锁,则请求成功,当前线程直接获取到锁。 356 | - 2. 如果当前线程已经持有锁,则使用 CAS 将 state 值加1,表示自己再次申请了锁,释放锁时减1。这就是可重入性的实现。 357 | - 3. 如果由其他线程持有锁,那么将自己添加进等待队列。 358 | ``` 359 | ReentranLock源码 360 | final void lock() { 361 | if (compareAndSetState(0, 1)) 362 | setExclusiveOwnerThread(Thread.currentThread()); //没有线程持有锁时,直接获取锁,对应情况1 363 | else 364 | acquire(1); 365 | } 366 | 367 | public final void acquire(int arg) { 368 | if (!tryAcquire(arg) && //在此方法中会判断当前持有线程是否等于自己,对应情况2 369 | acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //将自己加入队列中,对应情况3 370 | selfInterrupt(); 371 | } 372 | ``` 373 | 非公平锁的实现:在获取锁失败之后,加入到等待队列中,当持有锁的线程释放掉锁的时候,还是会按照等待队列的顺序来获取锁。 374 | 公平锁的实现:在获取锁的时候,先判断等待队列中是否有等待线程,如果有则直接加入到等待队列中,这样就保证了先等待的线程先获取到锁。 375 | 376 | 377 | #### [21、Java中volatile变量是什么。](https://www.cnblogs.com/dolphin0520/p/3920373.html) 378 | - 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 379 | - 可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 380 | - 有序性:即程序执行的顺序按照代码的先后顺序执行。 381 | - 指令重排序:一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。 382 | 383 | 处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction 2必须用到Instruction 1的结果,那么处理器会保证Instruction 1会在Instruction 2之前执行。 384 | 指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。 385 | 386 | **要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。** 387 | 388 | volatile:一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 389 | - 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(保证可见性) 390 | - 禁止进行指令重排序。(一定程度上保证有序性) 391 | 392 | volatile关键字禁止指令重排序有两层意思: 393 | - 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行; 394 | 395 | - 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。 396 | 397 | ``` 398 | x = 2; //语句1 399 | y = 0; //语句2 400 | volatile flag = true; //语句3 401 | x = 4; //语句4 402 | y = -1; //语句5 403 | ``` 404 |  由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。 405 | 406 |   并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果对语句3、语句4、语句5是可见的。 407 |    408 | **volatile能够保证程序的可见性,在一定程度上保证有序性,但不能保证程序的原子性。** 409 | 410 | 使用volatile必须具备以下2个条件: 411 | 412 | - 对变量的写操作不依赖于当前值 413 | 414 | - 该变量没有包含在具有其他变量的不变式中 415 | 416 | 417 | #### 22、为什么要使用线程池。 418 | 419 | 线程池提供了一种限制和资源的原理(包括执行一个任务),每个线程池还维护一些基本统计信息,例如已完成任务的数量。 420 | 421 | 使用线程池的好处: 422 | - 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 423 | - 提高相应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。 424 | - 提高线程的客观理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 425 | 426 | 427 | #### 23、Java默认提供了几种线程池。 428 | 429 | - 核心线程数量(corePoolSize):默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当。当任务队列满了并且线程数小于maximumPoolSize,那么会创建新的线程来执行任务。 430 | - 最大线程数(maximumPoolSize):表示在线程池中最多能创建多少个线程。 431 | - 空闲线程存活时间(keepAliveTime):表示线程没有任务执行时最多保持多久时间会终止。 432 | 433 | 默认情况下只作用于最大线程,直到线程池中的线程数不大于corePoolSize,核心线程将会一直存在。 434 | 但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。 435 | 436 | **Java通过Executors提供四种线程池** 437 | 438 | - newFixedThreadPool:corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue; 439 | 440 | 创建一个线程数量固定的线程池,当有任务来时,有空闲的线程就立即执行,否则就会将任务存入暂存的任务队列中,等待空闲的线程。 441 | 442 | - newSingleThreadExecutor:corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue; 443 | 444 | 创建一个只有一个线程的线程池,若没有空闲线程,则先将任务保存到任务队列中去,等待空闲线程来执行。 445 | 446 | - newCachedThreadPool:将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue。 447 | 448 | 可以根据实际情况调节线程数量的线程池,线程池数量不确定,如有空闲的线程,则优先使用空闲的新城,否则来了任务就创建新的线程,当线程空闲超过60秒,就销毁线程。 449 | 450 | - newScheduledThreadPool:corePoolSize和maximumPoolSize值相等,使用DelayedWorkQueue。 451 | 452 | DelayedWorkQueue是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。可以定期的执行任务。 453 | 454 | ![线程池结构图](https://github.com/chen-eugene/Interview/blob/master/image/20170406230435886.jpg) 455 | 456 | 457 | #### [24、什么时阻塞队列。](https://www.cnblogs.com/dolphin0520/p/3932906.html) 458 | 459 | 当一个线程去获取一个阻塞队列的元素时,若队列为空,那么就会当前队列就会被阻塞,当队列有了元素之后,被阻塞的线程会被自动唤醒。 460 | 461 | 阻塞队列的好处:非阻塞队列不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。如果使用阻塞队列就非常方便。 462 | 463 | Java提供的几种阻塞队列: 464 | - ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。 465 | - LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。 466 | - SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。 467 | - PriorityBlockingQueue:以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。 468 | - DelayedWorkQueue:基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。 469 | 470 | 471 | #### [25、如果提交任务时,线程池队列已满,会发生什么。](https://blog.csdn.net/qq_25806863/article/details/71172823) 472 | 473 | 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略: 474 | - ThreadPoolExecutor.AbortPolicy:拒绝执行新的任务,直接抛出RejectedExecutionException异常。(默认的拒绝策略) 475 | - ThreadPoolExecutor.DiscardPolicy:不会执行新的任务,也不会抛出异常。 476 | - ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。 477 | - ThreadPoolExecutor.CallerRunsPolicy:会调用当前线程池所在的线程去执行被拒绝的任务。 478 | 479 | #### [26、实现Runnable接口和Callable接口的区别。](https://www.cnblogs.com/dolphin0520/p/3949310.html) 480 | 481 | Runnable和Callable都表示线程池要执行的任务,两者的区别在于Runnable不会返回线程的执行结果,而Callable可以返回线程的结果。执行结果使用Futrue来接收。 482 | ``` 483 | public void test(){ 484 | 485 | ExecutorService executor = Executors.newCachedThreadPool(); 486 | Future result = executor.submit(new Callable{ 487 | public Integer call() throw Exception{ 488 | return 1 + 1; 489 | } 490 | }); 491 | } 492 | ``` 493 | Future: 494 | - get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回; 495 | - get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。 496 | 497 | #### 27、执行execute()方法和submit()方法的区别是什么呢? 498 | 499 | - execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行情况。 500 | - submit()方法用于提交有返回值的任务,线程池返回一个Future对象,通过这个Future对象可以判断任务的执行情况。 501 | 502 | 503 | #### 28、线程池需要销毁吗,如何进行销毁。 504 | 505 | 线程池创建之后通常情况下是不需要销毁的,因为无法判断程序的执行过程中还有没有任务需要执行,这个并不是绝对的,需要根据具体的执行任务来判断。Java中在程序退出的时候需要将线程池销毁;而在Android中,线程池的生命周期通常和应用的生命周期相同,所以不需要纪进行销毁。 506 | 507 | 销毁线程池的方法: 508 | - shutDown():调用此方法之后,线程池立即进入SHUTDOWN状态,此时不能向线程池中添加新的任务,否则抛出RejectedExecutionException异常,线程不会立即退出,知道线程池中的任务执行完毕之后才会退出。 509 | - shutDownNow():调用该方法之后,线程池立即进入STOP状态,并尝试停止所有正在执行的线程,不再处理还在等待队列中等待被执行的任务,并将等待被执行的任务返回。 510 | 511 | 它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的(阻塞状态下,调用Thread.interrupt()将会抛出异常)。所以,ShutdownNow()并不代表线程池就一定能立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | --------------------------------------------------------------------------------