├── 地铁60天搞定Android面试 ├── Day14_三方库源码及优化.md ├── Day17_设计模式.md ├── Day19_反射.md ├── Day16_内存泄漏及处理.md ├── Day18_垃圾回收机制.md ├── Day24_多线程.md ├── Day5_Http与Https.md ├── Day11_Java类加载过程.md ├── Day10_MVC、MVP、MVVM.md ├── Day6_线程池的执行机制.md ├── Day7_动画.md ├── Day22_Java基础(三)循环与集合.md ├── Day12_GPU绘制流程及过度绘制.md ├── Day20_Java基础(一)面向对象.md ├── Day4_Handler通信机制.md ├── Day9_JVM相关.md ├── Day8_Activity.md ├── Day23_Java基础(五)其他.md ├── Day21_Java基础(二)内部类、关键字与引用.md ├── Day1_View的绘制流程.md └── Day2_触摸事件分发机制分析.md ├── README.md ├── 面试题总结(其他).md ├── 源码分析总结.md ├── 面试题总结(Java部分).md └── 面试题总结(Android部分).md /地铁60天搞定Android面试/Day14_三方库源码及优化.md: -------------------------------------------------------------------------------- 1 | # Day:三方库源码及优化 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day17_设计模式.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day19_反射.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day16_内存泄漏及处理.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day18_垃圾回收机制.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day24_多线程.md: -------------------------------------------------------------------------------- 1 | # Day:多线程 2 | ## 关键字 synchronized 的作用 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day5_Http与Https.md: -------------------------------------------------------------------------------- 1 | # Day:HTTP与HTTPS 2 | 1、HTTPS是在HTTP上建立SSL加密层,对传输数据进行加密,是HTTP协议的安全版。 3 | 4 | 2、一是对数据进行加密,建立一个信息安全通道来保证传输过程中数据的安全;二是对网站服务器进行真实身份认证 5 | 6 | 3、HTTP存在的问题:a、使用明文通信,内容可能被窃听;b、不验证报文的完整性,可能被篡改;c、不验证通信方身份,可能遇到伪装。 7 | 8 | 4、HTTPS采用非对称加密来传输对称加密的密钥,采用对称加密对通信报文进行加密 9 | 10 | 5、HTTPS采用数字签名来校验数据完整性 11 | 12 | 6、HTTPS使用数字证书来解决身份伪装问题 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day11_Java类加载过程.md: -------------------------------------------------------------------------------- 1 | # Day:Java类加载过程 2 | ## 类加载的时机 3 | 1、加载顺序:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载 4 | 5 | 2、解析有时可以在初始化之后进行 6 | 7 | 3、Java虚拟机没规定什么时候进行加载,但规定以下几种情况需要进行初始化,当然初始化前的那几步操作需要在初始化前完成。 8 | 9 | a、new一个实例对象时,读取或设置一个类的静态字段时,调用一个类的静态方法时 10 | 11 | b、反射调用时,如果没初始化需要触发初始化 12 | 13 | c、初始化一个类时,如果其父类还未初始化,需要先触发父类的初始化 14 | 15 | d、虚拟机启动时,会先初始化主类 16 | 17 | ## Java类加载调用顺序 18 | 1、基类静态代码块、基类静态成员变量(优先级相同,按代码出现顺序依次执行)(只有第一次加载类时执行) 19 | 20 | 2、派生类静态代码块、派生类静态成员变量(优先级相同,按代码出现顺序依次执行)(只有第一次加载类时执行) 21 | 22 | 3、基类普通代码块、基类普通成员变量(优先级相同,按代码出现顺序依次执行) 23 | 24 | 4、基类构造函数 25 | 26 | 5、派生类普通代码块、派生类普通成员变量(优先级相同,按代码出现顺序依次执行) 27 | 28 | 6、派生类构造函数 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本项目为我自己的面试准备项目,共分为如下几个部分: 2 | 3 | * [面试题总结(Java 部分)](https://github.com/xcy396/AndroidInterview/blob/master/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93%EF%BC%88Java%E9%83%A8%E5%88%86%EF%BC%89.md) 4 | * [面试题总结(Android 部分)](https://github.com/xcy396/AndroidInterview/blob/master/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93%EF%BC%88Android%E9%83%A8%E5%88%86%EF%BC%89.md) 5 | * [面试题总结(其他部分)](https://github.com/xcy396/AndroidInterview/blob/master/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93%EF%BC%88%E5%85%B6%E4%BB%96%EF%BC%89.md) 6 | * [源码分析总结](https://github.com/xcy396/AndroidInterview/blob/master/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E6%80%BB%E7%BB%93.md) 7 | 8 | 欢迎 Star 及提 issues! -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day10_MVC、MVP、MVVM.md: -------------------------------------------------------------------------------- 1 | # Day:MVC、MVP、MVVM 2 | 1、Model模型对象中存储着数据和业务逻辑,应用的全部模型对象组成了模型层; 3 | 4 | 2、View视图对象知道如何在屏幕上绘制自己及如何响应用户的输入、触摸,凡是屏幕上能看到的对象就是视图对象,全部视图对象组成了视图层; 5 | 6 | 3、Controller控制对象含有应用的逻辑单元,是视图和模型对象联系的纽带,响应视图对象触发的各类事件,同时管理着模型对象和视图间的数据流动。 7 | 8 | 4、MVC模式的工作流程为:视图对象触发各类事件交由控制对象分发,控制对象选取合适的模型对象处理事件,模型对象处理完事件后再由控制对象通知视图对象进行更新视图。 9 | 10 | 5、Android开发原始写法中,Activity中包含了Controller对象、Model对象及部分View对象的代码,异常臃肿。 11 | 12 | 6、原始写法->MVC模式:将Activity中的业务逻辑代码也就是Model层进行抽离,称为Model层。这样Activity仅负责Controller层和部分View层,业务与视图进行了分离。 13 | 14 | 7、MVC模式->MVP模式:将Activity中的程序逻辑控制代码也就是Controller层进行抽离,称为Presenter层,这样Activity仅负责View层,解耦更彻底。 15 | 16 | 8、MVC模式->MVVM模式:将View层和Presenter层之间的交互做成自动化的,将Presenter层改名为ViewModel层。 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day6_线程池的执行机制.md: -------------------------------------------------------------------------------- 1 | # Day:线程池的执行机制 2 | 1、工作线程数小于核心线程数时,直接新建核心线程执行任务; 3 | 4 | 2、大于核心线程数时,将任务添加进等待队列; 5 | 6 | 3、队列满时,创建非核心线程执行任务; 7 | 8 | 4、工作线程数大于最大线程数时创建失败,拒绝任务。 9 | 10 | 5、新建线程执行任务时,会首先执行firstTask,然后从工作队列中循环取出任务执行。 11 | 12 | 6、ThreadPoolExecutor构造方法参数有: 13 | 14 | * corePoolSize为核心线程池大小 15 | * maximumPoolSize为线程池允许的最大线程数 16 | * keepAliveTime为线程池的工作线程空闲后保持存活的时间 17 | * unit为线程保持存活的时间单位 18 | * workQueue为工作队列,线程池的工作线程都是从这个工作队列源源不断的取出任务执行 19 | * threadFactory为创建新的工作线程时使用的工厂类 20 | * handler为拒绝任务时的饱和策略 21 | 22 | 7、通常通过 Executors 类的如下几个静态方法来创建不同类型的线程池: 23 | 24 | * newCachedThreadPool:可缓存线程池,无限大 25 | * newFixedThreadPool:定长线程池 26 | * newScheduledThreadPool:定长线程池,支持定时及周期性任务执行 27 | * newSingleThreadExecutor:单个线程池 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day7_动画.md: -------------------------------------------------------------------------------- 1 | # Day:动画 2 | 1、Android动画分为两大类:视图动画和属性动画,其中视图动画又分为补间动画和逐帧动画。 3 | 4 | 2、视图动画作用于View本身,属性动画作用于任意Java对象。 5 | 6 | 3、补间动画的原理是,我们确定好开始的视图样式和结束的视图样式,中间过程变化由系统补全,来实现一个动画。 7 | 8 | 4、逐帧动画的原理是,按顺序播放一组预先定义好的图片,每一张图片即为一帧。 9 | 10 | 5、属性动画的原理是,不断的对值进行操作,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。 11 | 12 | 6、补间动画仅能控制整体效果以及显示效果,不能真正改变View的属性,且操作只有那几种;逐帧动画使用大量图片,容易引起OOM;而属性动画可以真正改变View的任何属性,可控制变化速率等。 13 | 14 | 7、ValueAnimator用于实现对一个值的动画变化;ObjectAnimator用于实现对一个对象的动画变化;AnimatorSet用于实现组合动画。 15 | 16 | 8、ObjectAnimator的工作机制是寻找对象中属性名对应的get方法和set方法,并调用。 17 | 18 | 9、估值器TypeEvaluator用来告诉动画系统,Object需要如何从初始值过渡到结束值,也就是动画变化时Object需要怎样变化;插值器(补间器)Interpolator用来控制动画从初始值过渡到结束值变化的速率。 19 | 20 | 10、Android3.1开始,View类新增animate方法,返回一个ViewPropertyAnimator对象,我们可以操作这个对象来实现各种动画效果。 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day22_Java基础(三)循环与集合.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础(循环) 2 | ## Java集合 3 | ### ArrayList和Array的区别: 4 | 5 | * ArrayList长度是动态的;Array长度是静态的,创建后不可变 6 | * ArrayList不可保存基本数据类型,但可保存封装类;Array可直接保存基本数据类型 7 | * ArrayList是Java集合类;而Array是native的程序数据结构 8 | * Array性能上优于ArrayList 9 | * ArrayList类型安全,支持编译时检查;Array不是类型安全,支持运行时检查 10 | * ArrayList显示支持泛型;Array不支持 11 | * ArrayList不支持指定维度;Array支持多维度 12 | 13 | ## do...while循环 14 | 1、do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。如果布尔表达式的值为 true,则语句块一直执行,直到布尔表达式的值为 false。 15 | 16 | 2、break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块;break 跳出最里层的循环,并且继续执行该循环下面的语句。 17 | 18 | 3、continue 适用于任何循环控制结构中,作用是让程序立刻跳转到下一次循环的迭代。 19 | 20 | ## i++和++i 21 | 1、i++会先使用i的值,也就是将i的值加载到数据栈,在给i加1,最后使用数据栈中的值。 22 | 23 | 2、++i会先将i的值加1,再将增加后的值加载到数据栈,再使用数据栈中的值。 24 | 25 | 3、使用值的时候都是从数据栈顶去取值。 26 | 27 | 4、`int i = 0;i = i++;`该代码执行后i的值为0。 28 | 29 | ## int和Integer的区别 30 | 31 | * int 是基本数据类型,Integer 是包装类 32 | * Java中,会对 -128 到 127 的 Integer 对象进行缓存,当创建新的 Integer 对象时,如果符合这个这个范围,并且已有存在的相同值的对象,则返回这个对象,否则创建新的 Integer 对象 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day12_GPU绘制流程及过度绘制.md: -------------------------------------------------------------------------------- 1 | # Day:GPU绘制流程及过度绘制 2 | ## 绘制原理 3 | 1、LayoutInflater将布局中的xml标签转化为对象,CPU经过measure、layout、draw将他们转化为多边形(Polygons)或纹理(Texture),GPU对多边形或纹理进行栅格化操作,栅格化后的数据写入帧缓冲区中等待显示器显示。 4 | 5 | 2、CPU和GPU通过图形驱动层连接。 6 | 7 | 3、GPU负责绘制帧,屏幕负责逐行扫描显示帧,两者速度不一定保持一致。 8 | 9 | 4、因此引入垂直同步机制,由系统每隔16ms发出一次VSync信号后,CPU进行计算,GPU进行绘制,以此来强制GPU的刷新率和屏幕刷新率同步。 10 | 11 | 5、理想状态下屏幕每秒展示60帧时人眼感受不到卡顿,动画会比较流畅,因此为保持应用流畅,我们需要在16ms的时间内完成绘制,尽量减少measure、layout、draw的时间。有以下解决方案: 12 | 13 | * 精简布局层级 14 | * 使用\标签重用布局,使用\标签合并布局 15 | * 使用ViewStub仅在需要时加载 16 | * 删除无用的控件和布局 17 | * 使用性能较好的ViewGroup,如Constraintlayout等 18 | * onDraw方法中不要创建局部对象,会占用过多内存导致频繁gc 19 | * onDraw方法中不要执行耗时操作及循环操作 20 | 21 | ## 过度绘制 22 | 1、过度绘制指:屏幕上的某个像素在同一帧的时间内被绘制了多次。会浪费CPU和GPU资源。 23 | 24 | 2、开发者工具可提高可视化过度绘制情况:真彩色-没有过度绘制;蓝色-过度绘制1次;绿色-过度绘制2次;粉色-过度绘制3次;红色-过度绘制4次或更多 25 | 26 | 3、过度绘制解决方案: 27 | 28 | * 去掉Window的默认背景(在onCreate方法中或者在theme中) 29 | * 去掉视图中不必要的背景 30 | * 使用ViewStub仅在需要时加载 31 | * 使用\标签重用布局,使布局更清晰明了;使用\标签合并布局,减少层级 32 | * 采用性能更好的布局来精简层级 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day20_Java基础(一)面向对象.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | 1、面向对象三大特性:封装、继承、多态。 3 | 4 | * 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据 5 | * 继承可以重用父类代码,实现代码复用和可扩展的效果 6 | * 多态指对象的一个方法在调用时可以有多种状态,即父类引用可以持有子类对象 7 | * 多态实现的两种形式:基于继承重写、基于接口 8 | * 多态存在的三个条件:继承、重写、父类引用持有子类对象 9 | * 非静态成员方法的调用:编译看父类,运行看子类 10 | * 多态优点:不用创建一堆子类对象的引用;多态缺点:不能使用子类特有的成员属性和成员方法 11 | 12 | 2、抽象类和接口区别: 13 | 14 | * 抽象类可以包含抽象方法和具体方法;接口只能包含抽象方法 15 | * 抽象类方法可使用public、protected、private修饰符;接口方法默认使用public static修饰,成员变量默认使用public static final修饰 16 | * 一个抽象类只能继承一个父类;但一个接口可以继承多个父接口 17 | * 相同点:二者都不可以实例化 18 | 19 | 3、`==`用来判断内存地址是否相同;`equals()`用来判断字符串的内容是否相同 20 | 21 | 4、静态绑定和动态绑定 22 | 23 | * 绑定指一个方法的调用与方法所在的类关联起来 24 | * 静态绑定:编译过程中编译器已经准确的知道这个方法属于哪个类了 25 | * 动态绑定:需要在运行时根据对象具体的类型来绑定,来选择调用哪个类的方法 26 | * private方法、static方法、final方法、构造器方法都是静态绑定的;其他方法全部为动态绑定 27 | 28 | 5、方法的重载与重写 29 | 30 | * 重载指在一个里面,方法名字相同,参数不同,返回类型可相同也可不同。常用于构造器重载 31 | * 重写指子类对父类允许访问的方法的实现过程进行重新编写,返回值和形参都不能变 32 | 33 | 6、本地变量、实例变量以及类变量之间的区别 34 | 35 | * 本地变量就是局部变量,定义在方法内部或者方法的形参中 36 | * 实例变量为为非静态变量,隶属于对象,每一个对象的实例变量都不同 37 | * 类变量为静态变量,隶属于类 38 | 39 | 7、形参与实参 40 | 41 | * 形参是定义方法时使用的参数,出现在方法定义中,在整个方法内可以使用,离开该方法不能使用。 42 | * 实参是用于调用方法时传递给方法的参数。 43 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day4_Handler通信机制.md: -------------------------------------------------------------------------------- 1 | # Day:Handler通信机制 2 | ## 关键流程 3 | 1、Looper不断的从MessageQueue中取出Message,然后交给Message对应的Handler处理。同时Handler还执行发送Message到MessageQueue中的动作。 4 | 5 | 2、Handler对象持有Looper的目的是为了拿到Looper对应的MessageQueue,并往其中插入消息;Looper从MessageQueue中取出Message后,交给Message的target也就是Handler对象处理。 6 | 7 | 3、Looper对象存储在ThreadLocal中,属于线程局部变量,只能被当前线程访问。 8 | 9 | ## 详细介绍 10 | 1、Handler调用sendMessage、post方法发送Message,并插入到MessageQueue中,MessaQueue采用单链表结构。 11 | 12 | 2、Handler类有一个Looper成员变量,Looper属于线程局部变量,每个线程有且只能有一个Looper;Looper类有一个MessageQueue成员变量;Handler持有Looper主要是为了拿到Looper对应的MessageQueue,并往其中插入消息。 13 | 14 | 3、子线程需要先调用Looper.prepare方法,来创建一个Looper对象存储到ThreadLocal中。然后创建Handler时会调用Looper.myLooper方法获取当前线程的Looper。 15 | 16 | 4、Looper.loop方法开启消息循环,Looper会循环从其MessageQueue中提取消息,并调用消息的target(也就是Handler)进行分发处理 17 | 18 | 5、Handler拿到Message后,先判断Message的Callback是否为空,不为空直接执行,消息处理结束;为空则判断Handler的Callback是否为空,不为空则执行,并决定是否进行拦截,拦截则消息处理结束;不拦截则执行Handler的handleMessage方法。 19 | 20 | ## ThreadLocal介绍 21 | 1、ThreadLocal是一个创建线程局部变量的类。 22 | 23 | 2、一般情况下,我们创建的线程可以被任何一个线程访问并修改,而使用ThreadLocal创建的变量只能被当前线程访问,其他线程无法访问和修改。 24 | 25 | 3、ThreadLocal的set方法存入的值,实际是放到当前线程的ThreadLocalMap实例中,key是当前ThreadLocal对象,value是存入的值。 26 | 27 | 4、get方法直接从当前线程的ThreadLocalMap中,根据key获取对象 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day9_JVM相关.md: -------------------------------------------------------------------------------- 1 | # Day:JVM相关 2 | ## Java垃圾回收机制分析 3 | 1、JVM栈记录了当前线程的方法调用,栈帧存储有方法参数、局部变量、返回地址。但引用的对象存储在堆中,所以方法调用结束后,对象会依旧存在于堆中,易造成内存泄漏。 4 | 5 | 2、Java的垃圾回收机制可以自动回收不再使用的对象占用的内存空间。 6 | 7 | 3、垃圾回收算法 8 | 9 | 有:引用计数算法、可达性分析算法、标记清除算法、复制算法、标记整理算法、分代回收算法。 10 | 11 | a、引用计数算法中,每个对象包含一个计数器,有新的引用指向该对象时计数器加1,引用移除时计数器减1,计数器为0时回收对象,引用计数法有个缺陷是无法检测出循环引用。 12 | 13 | b、可达性分析算法中,每个对象都有一个用于标示该对象是否可到达的标记信息。从根出发跟随所有的引用,就可以找到所有的可到达对象,不可到达对象就是需要垃圾回收的对象。 14 | 15 | c、标记清除算法流程为,垃圾回收启动时,Java程序暂停运行,JVM从根出发找到所有的可达对象并标记,然后扫描整个堆找到不可达对象并清空。缺点是标记、清除的效率不高;会产生内存碎片。 16 | 17 | d、复制算法为,将内存分为大小相等的两块,每次使用一块,使用完时将存活的对象复制到另一块,清空原来那块。在对象存活率较低时回收效率高,缺点是内存空间使用率只有一半。 18 | 19 | e、标记整理算法下,找到所有的可达对象并标记后,将所有存活对象都向内存区域的一端移动,然后清理掉边界以外的内存区域。 20 | 21 | d-1、分代回收算法下,堆分为三代:永久代、老年代、新生代,新生代又分为三个区域:eden区、from区和to区。 22 | 23 | d-3、永久世代主要存放静态文件如Java静态类、方法等,对于垃圾回收没有显著影响。 24 | 25 | d-4、分代回收流程为,上次垃圾回收后创建的对象叫新生对象,存放于eden区,eden区没有空间存放新生对象时,触发一次MinorGC,采用标记整理算法将eden区和from区的可达对象复制到to区,清空eden区和from区空间,此时from区和to区已交换;当to区也放不下eden和from区的可达对象时,将部分对象放到成熟世代;当成熟世代也已满时触发MajorGC,采用标记清除算法进行垃圾回收。 26 | 27 | d-5、其中,即使to区没有满,JVM也会移动生命周期足够久远的对象到成熟世代;MinorGC发送频率较高,MajorGC发生频率较低。 28 | 29 | ## Java内存结构 30 | 1、堆是用来存储对象本身和数组的,在JVM中只有一个堆,因此堆是被所有线程共享的。 31 | 32 | 2、栈中存放着栈帧,每个栈帧分别对应一个被调用的方法,栈是线程私有的。 33 | 34 | 3、方法区存放着已经加载的类信息、常量池、静态变量以及方法代码的内存区域 35 | 36 | 4、方法的调用过程对应栈帧在虚拟机中入栈到出栈的过程。 37 | 38 | 5、栈帧中存放着局部变量表、指向运行时常量池的引用、方法返回地址等。 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day8_Activity.md: -------------------------------------------------------------------------------- 1 | # Day:Activity 2 | 1、Task是Activity的集合,采用栈的方式来管理其中的Activity,称为返回栈或任务栈。 3 | 4 | 2、任务栈中的Activity采用后进先出的方式。 5 | 6 | 3、Activity的taskAffinity用于指定Activity更愿意依附于哪一个任务,默认情况下同一个应用的所有Activity的taskAffinity都相同,就是应用包名。 7 | 8 | 4、Activity A启动Activity B,B可以自己定义如何与当前任务关联(使用AndroidManifest定义),也可以由A来决定B如何与当前任务关联(使用Intent的flag)。 9 | 10 | 5、二者同时设置时Intent的flag级别更高。 11 | 12 | ## Activity的启动模式 13 | 1、standard模式是默认模式,每次启动都会创建新的Activity实例。 14 | 15 | 2、singleTop模式先判断任务栈栈顶是否有目标Activity实例,有则直接回调onNewIntent,无则创建新实例。 16 | 17 | 3-1、singleTask模式下,先根据taskAffinity检查Activity所需的任务栈是否存在,不存在就创建任务栈创建新实例; 18 | 19 | 3-2、所需任务栈存在就看任务栈中有没有目标实例,有则直接回调onNewIntent并把实例之上所有的Activity出栈,无则创建新实例; 20 | 21 | 3-3、如果目标任务栈位于后台会直接把整个任务栈移到前台来。 22 | 23 | 4、singleInstance模式下,先判断是否存在实例,有则直接回调onNewIntent。无则创建一个新的任务栈和新的实例,该任务栈中不允许再有其他实例。 24 | 25 | ## Activity启动模式应用场景 26 | 1、standard模式为默认模式,适用于大多数场景。 27 | 28 | 2、singleTop模式适用于登录页面等。 29 | 30 | 3、singleTask模式适用于主页面等。 31 | 32 | 4、singleInstance模式适用于多个程序使用一个Activity,如系统launcher、来电界面等。 33 | 34 | ## Intent的Flag 35 | 1-1、FLAG_ACTIVITY_NEW_TASK最为常用,目的是让Activity在它的目标taskAffinity对应的任务栈中启动。 36 | 37 | 1-2、如果目标任务栈在后台会移到前台,目标任务栈不存在会新建任务栈? 38 | 39 | 1-3、非Activity(如Service)启动Activity时,需要显示设置FLAG_ACTIVITY_NEW_TASK。 40 | 41 | 1-4、Activity启动Activity时,singleTask模式和singleInstance模式隐式的设置了FLAG_ACTIVITY_NEW_TASK,而standard模式和singleTop模式不会设置。 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day23_Java基础(五)其他.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础(其他) 2 | ## 序列化 3 | 1、序列化指把应用层的对象或数据结构转换成一段连续的二进制串,反序列化指把二进制串转换成应用层的对象或数据结构 4 | 5 | 2、序列化按可读性可分为两类:二进制序列化、文本序列化(如 XML、JSON)以及半文本序列化。 6 | 7 | 3、文本序列化可读性、可编辑性好;二进制序列化不具有可读性,但解析速度更有优势 8 | 9 | 4、Java 中,有两种方式可以实现二进制序列化,既可以实现 Serializable 接口,也可以实现 Parcelable 接口。 10 | 11 | 5、在 Android 中,我们不应该使用 Serializable 接口。因为 Serializable 接口使用了反射机制,这个过程相对缓慢,而且往往会产生出很多临时对象,这样可能会触发垃圾回收器频繁地进行垃圾回收。相比而言,Parcelable 接口比 Serializable 接口效率更高,性能方面要高出 10x 多倍。 12 | 13 | ## Java类型转换 14 | 1、Java中的类型转换分为基本数据类型转换和引用数据类型转换 15 | 16 | 2、基本数据类型转换分为自动转换和强制转换,自动转换指从位数低的类型向位数高的类型转换,强制类型转换指从位数高的类型向位数低的类型转换,会导致精度缺失 17 | 18 | 3、引用数据类型转换时,子类可以转换成父类,父类不一定可以转换为子类,父类引用引用的是子类对象时可以强制转换为子类,但是父类引用引用的是父类对象时,强制转换会抛出异常 19 | 20 | ## 泛型和类型擦除(未完待续) 21 | 1、泛型就是指宽泛的数据类型,允许在定义类、接口和方法的时候使用类型参数(type parameter) 22 | 23 | 2、声明的类型参数在使用时用具体的类型来替换 24 | 25 | 3、使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,这个过程就称为类型擦除 26 | 27 | ## String、StringBuffer 和StringBuilder 的区别 28 | 1、String 是不可变对象,每次对 String 对象进行更改时,都会生成一个新的对象,然后将指针指向新的对象 29 | 30 | 2、StringBuffer 线程安全,使用时,每次都是直接对 StringBuffer 对象本身进行操作,并不生成新的对象 31 | 32 | 3、StringBuilder 非线程安全,和 StringBuffer 类似,单线程中性能比 StringBuffer 高 33 | 34 | 4、StringBuilder 如何避免不可变字符串分配的问题 35 | 36 | ## 自动装箱和自动拆箱 37 | 1、自动装箱指 Java 自动将原始数据类型转换为对应的对象类型,如 int 变量转为 Integer 对象;将 Integer 对象自动转换为 int 类型值,叫做自动拆箱 38 | 39 | 2、自动装箱时编译器调用 valueOf 将原始类型值转换成对象 40 | 41 | 3、自动拆箱时,编译器通过调用类似 intValue(), doubleValue() 这类的方法将对象转换成原始类型值 42 | 43 | 4、自动装箱、拆箱主要发生在赋值时、方法调用时 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day21_Java基础(二)内部类、关键字与引用.md: -------------------------------------------------------------------------------- 1 | # Day:Java基础 2 | ## final关键字的理解 3 | 1、final的意思是【无法改变的】、【最终的】,可以修饰非抽象类、非抽象类的成员变量和成员方法 4 | 5 | 2、final类不能被继承,没有子类,其中的方法默认是final的 6 | 7 | 3、final方法不能被重写,但可以被继承(final不能用于修饰构造方法) 8 | 9 | 4、final成员变量表示常量,只能被赋值一次,赋值后不再改变。 10 | 11 | 5、使用final声明基础数据类型时,数值恒定不变;使用final声明对象引用时,引用的对象恒定不变,但对象的数据可变;使用final声明数组类型时,引用的数组恒定不变,但数组内的数据可变。 12 | 13 | 优点: 14 | 15 | 1、编译器遇到final方法时会转入内嵌机制,提高效率。 16 | 17 | 2、可以安全的在多线程环境下共享,不需要额外的同步开销。 18 | 19 | ## 什么是匿名内部类 20 | 1、匿名内部类即没有名字的内部类,只可被使用一次 21 | 22 | 2、使用匿名内部类前提条件:必须继承自一个父类或实现一个接口 23 | 24 | 3、语法格式为 new 父类构造器(参数列表)|实现接口(){} 25 | 26 | 4、直接将抽象类的方法或者接口方法在大括号中实现,可以省略一个类的书写 27 | 28 | 5、匿名内部类中不能定义构造方法,但可以使用初始化语块代替构造方法 29 | 30 | ## 为什么匿名内部类使用局部引用要用final 31 | 1、匿名内部类属于一种局部内部类。 32 | 33 | 2、编译后局部内部类中会有一个成员变量,是对外部局部变量的引用的拷贝,在局部内部类使用外部局部变量值时都是通过这个引用进行的。 34 | 35 | 3、为避免这个成员变量的值(引用的对象)被外部类的方法修改,导致内部类在使用时得到的值不一样,需要使用final让该成员变量不可变。 36 | 37 | 4、局部变量位于方法内部,在虚拟机的栈上,意味着这个变量无法进行共享,匿名内部类无法直接访问,只能通过值传递的方式,传递给匿名内部类。 38 | 39 | 5、而类的成员变量在虚拟机的堆上,内部类可以直接获取这个变量,故类的成员变量不需要声明为final内部类就可访问。 40 | 41 | ## 值传递与引用传递 42 | 1、Java总是采用按值调用,方法得到的是参数值的一个拷贝。 43 | 44 | 2、一个方法不能改变其参数的值,如果是基本类型参数不能改变参数的值,如果是对象参数不能改变参数的引用(但可以改变) 45 | 46 | ## 引用分类 47 | 1、强引用:是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。 48 | 49 | 2、软引用:如果内存空间足够,垃圾回收器就不会回收软引用对象;如果内存空间不足,就会回收软引用对象。 50 | 51 | 3、弱引用:弱引用对象比软引用对象生命周期更短,垃圾回收器扫描时一旦发现弱引用对象,就会回收它。垃圾回收器优先级不高,所以不一定很快发现弱引用对象。 52 | 53 | 4、虚引用:虚引用对象就和没有被引用一样,任何时候都可能被垃圾回收器回收。一般用于跟踪对象被垃圾回收器回收的过程,如开源库LeakCanary。 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day1_View的绘制流程.md: -------------------------------------------------------------------------------- 1 | # Day:View的绘制流程 2 | ## View的测量 3 | 1、View的measure方法,根据父布局传递来的MeasureSpec,来确定自己的测量宽、高的值。 4 | 5 | 2、measure方法为final方法,不可重写,但measure方法有调用onMeasure方法,我们可以重写onMeasure来手动改变View的测量宽、高的值(可参考ImageView、TextView)。 6 | 7 | 3、onMeasure方法的默认实现中,当SpecMode为AT_MOST模式或EXACTLY模式时,测量宽、高就是其SpecSize中的值。 8 | 9 | 4、View的MeasureSpec是由父布局传递而来,父布局又是如何为每一个子View生成MeasureSpec的呢? 10 | 11 | 5、ViewGroup提供了measureChildren方法,我们可以在重写ViewGroup的onMeasure方法时,调用measureChildren方法来测量每一个子View(因为ViewGroup首先它也是一个View,可在onMeasure方法中拿到ViewGroup的MeasureSpec)。 12 | 13 | 6、在measureChildren方法中,会根据ViewGroup自己的MeasureSpec和子View自己设置的宽高,来为每一个子View设置MeasureSpec。最后调用childView的measure方法,并把给childView生成的MeasureSpec传递过去。 14 | 15 | ## View的布局 16 | 1、View的layout方法中,会为调用setFrame方法为View自己指定位置,如果是ViewGroup还会调用onLayout方法为所有的childView指定位置。 17 | 18 | 2、View调用setFrame方法为mLeft、mTop、mRight、mBottom几个属性赋值,来确定自己相对于父布局的位置。 19 | 20 | 3、ViewGroup的onLayout方法是一个抽象方法,子类需要实现此方法,并在实现中根据ViewGroup的布局逻辑计算出每个childView的位置,然后调用childView的layout方法进行布局。 21 | 22 | ## View的绘制 23 | 1、View的draw方法会按流程分别绘制背景、content、childView、前景、默认焦点等。其中调用onDraw方法绘制View的内容;如果是ViewGroup还会调用dispatchDraw方法绘制childView。 24 | 25 | 2、ViewGroup的dispatchDraw方法中,会调用drawChild方法来绘制所有的childView,drawChild方法则直接调用childView的draw方法进行绘制。 26 | 27 | 3、就这样按视图结构层次依次绘制下去。 28 | 29 | 4、我们可以通过重写View的onDraw方法,拿到Canvas来绘制我们想要绘制的内容。 30 | 31 | ## 附 32 | ViewGroup为childView生成MeasureSpec的规则: 33 | 34 | * View配置固定宽高时,View的SpecMode总是EXCATLY模式,SpecSize总是LayoutParams中设置的值 35 | * View配置match\_parent时,ViewGroup是什么模式,View就是什么模式,SpecSize是ViewGroup剩余的空间 36 | * View配置wrap\_content时,View的SpecMode总是AT_MOST模式,SpecSize总是ViewGroup的剩余空间 37 | 38 | 注意: 39 | 40 | 当View配置wrap\_content时,View的SpecMode总是为AT_MOST,SpecSize总是为ViewGroup的剩余空间。View在根据MeasureSpec设置测量宽高时,就会设置成ViewGroup的剩余空间,与期望的wrap\_content不符。 41 | 42 | 解决方案为自定义View时重写onMeasure方法,在View的SpecMode为AT_MOST时,为View指定一个宽高。ImageView、TextView等都是如此操作,可参考其源码。 -------------------------------------------------------------------------------- /地铁60天搞定Android面试/Day2_触摸事件分发机制分析.md: -------------------------------------------------------------------------------- 1 | # Day:触摸事件分发机制分析 2 | ## Activity的分发 3 | 1、事件先分发给PhoneWindow,PhoneWindow不消费则传给Activity的onTouchEvent方法。 4 | 5 | 2、PhoneWindow传给DecorView,DecorView传给根布局ViewGroup。 6 | ## ViewGroup的分发 7 | 1、首先调用onInterCeptTouchEvent方法,判断ViewGroup自己是否需要,需要则拦截,拦截后就不会传递给childView。 8 | 9 | 2、拦截后,调用ViewGroup自己的onTouchEvent方法。 10 | 11 | 3、不拦截则传递给childView,按视图层次结构依次传递下去。 12 | 13 | 4、最终的那个childView也不需要时,事件会进行回传,依次调用前面每一层的onTouchEvent方法。 14 | ## View的分发 15 | 1、View会依次调用onTouchListener、onTouchEvent、onLongClickListener、onClickListener。 16 | 17 | 2、在onTouchListener、onTouchEvent中可决定是否消费事件,不消费,事件则开始回传。 18 | 19 | 3、注册了onLongClickListener、onClickListener及设置了Clickable等,View就会消费事件。 20 | ## 伪代码 21 | 1、ViewGroup分发的伪代码 22 | 23 | ```java 24 | // 返回值代表是否将事件消费掉 25 | public boolean dispatchTouchEvent(MotionEvent event) { 26 | // 默认状态为未消费过 27 | boolean result = false; 28 | 29 | // 如果没有拦截(ViewGroup一般不会进行拦截,可点击的ViewGroup除外) 30 | if (!onInterceptTouchEvent(event)) { 31 | // 则交给childView 32 | result = child.dispatchTouchEvent(event); 33 | } 34 | 35 | // 如果事件没有被childView消费 36 | if (!result) { 37 | // 则调用自身 onTouchEvent() 38 | result = onTouchEvent(event); 39 | } 40 | 41 | // 返回事件消费状态 42 | return result; 43 | } 44 | ``` 45 | 46 | 2、View分发的伪代码 47 | 48 | ```java 49 | // 返回值代表是否将事件消费掉 50 | public boolean dispatchTouchEvent(MotionEvent event) { 51 | if(mOnTouchListener.onTouch(this, event)) { 52 | return true; 53 | } else if (onTouchEvent(event)) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | ``` 59 | 60 | ## 注意事项 61 | * 拦截事件指事件不再往下传递,使用事件指从事件中获取想要的信息,消费事件指把事件吃了 62 | * 只要给 View 注册了 onClickListener、onLongClickListener、OnContextClickListener 其中的任何一个监听器或者设置了 android:clickable=”true” 就代表这个 View 是可点击的,可点击的 View 就会消费事件 63 | * ViewGroup 和 ChildView 同时注册了点击事件监听器时,事件优先给 ChildView 消费 64 | * 同一次点击事件只能被一个 View 消费,防止事件混乱 -------------------------------------------------------------------------------- /面试题总结(其他).md: -------------------------------------------------------------------------------- 1 | # 设计模式 2 | ### 介绍工厂模式([参考链接](http://wiki.xuchongyang.com/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F.html)) 3 | 工厂模式分为:简单工厂、工厂方法、抽象工厂三种。 4 | 5 | * 简单工厂模式有一个单独的工厂角色类,根据传入的不同参数,负责创建不同的实例 6 | * 工厂方法模式中工厂角色类不再负责创建实例,它自己只负责声明接口,把创建实例的任务给了它的工厂子类 7 | * 抽象工厂模式的工厂会创建出一组具体产品,构成一个产品族 8 | 9 | ### 介绍单例模式 10 | * 单例模式确保一个类只有一个实例 11 | * 自行实例化 12 | * 自行向整个系统提供这个实例 13 | 14 | 写法:[参考链接](http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/) 15 | 16 | * 懒汉式,线程不安全 17 | * 懒汉式,线程安全,效率不高 18 | * 双重校验锁(推荐) 19 | * 饿汉式,不是懒加载 20 | * 静态内部类(推荐) 21 | * 枚举 Enum 22 | 23 | ### 介绍观察者模式 24 | * 定义了对象间的一种一对多的依赖关系 25 | * 观察目标发生改变时,会自动通知观察者,观察者做出相应的反应 26 | * 观察目标会将观察者的引用保存在一个集合中,通知观察者时循环调用 27 | 28 | # 数据结构与算法 29 | # 其他 30 | ### 最近使用过的开源库有什么,讲讲实现原理(源码) 31 | ### HTTP 和 HTTPS 的区别 32 | 1、HTTP 是 HTTP 协议运行在 TCP 之上,信息是明文传输的,客户端和服务端也都无法验证对方身份 33 | 34 | 2、HTTPS 是 HTTP 协议运行在 SSL/TLS 之上,SSL/TLS 运行在 TCP 之上,所有传输内容经过了加密,客户端和服务端也可以验证对方身份 35 | 36 | ### Gradle 的优势在哪 37 | Gradle 的功能:依赖管理、多模块构建、 38 | 39 | * Maven 基于 XML 配置繁琐,阅读性差,Gradle 基于 Groovy,简化了构建代码的行数,易于阅读 40 | 41 | 1、依赖管理方面:Gradle 支持依赖动态版本管理,解决依赖冲突机制更明确 42 | 43 | 2、多模块构建方面:Gradle 使用 allprojects 和 subprojects 来定义里面的配置是应用于所有项目还是子项目,更加灵活 44 | 45 | 3、构建周期方面:Gradle 本身与项目构建周期是解耦的,可以灵活的增删 task 46 | 47 | ### 对网络通信协议的理解 48 | 1、网络由下往上分为物理层、链路层、网络层、传输层、会话层、表示层、应用层 49 | 50 | 2、IP 协议位于网络层,TCP 协议位于传输层、HTTP 协议位于应用层 51 | 52 | 3、Socket 是对 TCP/IP 协议的封装,是一个调用接口,通过 Socket 才能使用 TCP/IP 协议 53 | 54 | 4、TCP 是面向连接的,属于可靠连接;UDP 不是面向连接的,属于不可靠连接。 55 | 56 | 5、UDP 发送数据前不与对方建立连接,收到数据后也不发送确认信号,所以开销低,实时性好 57 | 58 | ### 对 TCP 和 HTTP 的理解 59 | 1、TCP 为传输层协议,解决数据如何在网络中传输 60 | 61 | 2、HTTP 为应用层协议,解决如何包装数据 62 | 63 | 2、HTTP 协议需要依靠 TCP 协议来传输数据 64 | 65 | 3、HTTP 对 TCP 的使用分为两种:短连接和长连接,HTTP 1.0 默认短连接,1.1 默认长连接 66 | 67 | # 其他面试题 68 | ### 对 Android 哪一方面的研究比较深入 69 | ### 介绍下你之前所做的这些项目 70 | ### 如何把控一个项目,架构如何设计 71 | ### 医疗的架构是如何的 72 | ### 如何优化医疗这个产品 73 | ### 医疗的通讯机制 74 | ### 门禁机运作过程 75 | ### 云门禁这个项目你负责了哪些和 Android 有关的 76 | ### 为什么转行 -------------------------------------------------------------------------------- /源码分析总结.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 本节主要对 Android 平台主要框架、开源库源码分析进行总结,方便笔试、面试前进行系统复习,并给出了完整源码分析的链接。 3 | 4 | # 目录 5 | * EventBus 源码分析 6 | * Handler 源码分析 7 | * AsyncTask 源码分析 8 | * View 的工作机制源码分析 9 | * Android 触摸事件分发机制源码分析 10 | * Android 按键事件分发机制源码分析 11 | * OkHttp 源码分析 12 | * Retrofit 源码分析 13 | * ButterKnife 源码分析 14 | * Dagger2 源码分析 15 | 16 | # EventBus 源码分析 17 | 1、register 方法注册:首先会根据观察者的类型找出它声明要订阅的所有事件(订阅方法),然后一一订阅 18 | 19 | 2、订阅过程:首先根据事件类型获取到订阅该事件类型的订阅关系集合,(为观察者和订阅方法生成订阅关系)并把这个订阅关系对象存入到该集合中;然后根据观察者获取到该观察者订阅的事件集合,并把当前订阅的事件放入到事件集合中。 20 | 21 | EventBus 有两个 Map 类型的成员变量,分别为: 22 | 23 | * Map1 用于根据事件类型通过反射调用观察者的方法 -- Key:事件类型,Value:订阅了该事件的订阅关系(观察者、订阅方法)集合 24 | 25 | * Map2 用于取消订阅 -- Key:观察者,Value:该观察者的订阅事件集合 26 | 27 | 3、观察目标 post 事件:首先获得当前线程的待发送事件队列,并把当前事件对象添加进去;接着依次发送当前队列中的事件对象。 28 | 29 | 4、事件对象的发送:首先从刚才的 Map 中,根据事件类型取得订阅关系集合;然后遍历订阅关系集合,先进行线程判断,再分别通过反射调用观察者的订阅方法 30 | 31 | 5、unregister 解除注册:首先从 Map2 中根据观察者取得该观察者订阅事件集合,然后一一解除该观察者和每个事件的订阅关系,最后再把该观察者从 Map2 中删除 32 | 33 | 6、解除观察者和每个事件的订阅关系:从 Map1 中根据事件类型获得订阅了该事件的订阅关系集合,将该观察者相关的订阅关系进行删除 34 | 35 | 补充: 36 | 37 | * 订阅方法是对订阅事件做的一层封装 38 | * 订阅关系是对观察者和订阅方法做的一层封装 39 | 40 | [EventBus 源码分析完整版地址](http://xuchongyang.com/2017/07/16/EventBus-3-0-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%80%EF%BC%89%E6%95%B4%E4%BD%93%E5%88%86%E6%9E%90/) 41 | 42 | # Handler 源码分析 43 | 一句话总结:Looper 不断从 MessageQueue 中取出一个 Message,然后交给其对应的 Handler 处理 44 | 45 | 1、Handler 的构造方法会将 Handler 对象和当前线程的 Looper 对象绑定到一起 46 | 47 | 2、Handler 的一系列 sendMessage 和 post 方法最终会调用 enqueueMessage 方法将消息放入消息队列 48 | 49 | 3、Handler 在 dispatchMessage 方法中拿到消息后,首先检查 Message 的 callback 是否为 null,不为 null 直接调用其 run 方法;否则检查 Handler.mCallback 是否为 null,不为 null 直接调用其 handleMessage 方法;否则调用 Handler 的 handleMessage 方法 50 | 51 | 4、ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,ThreadLocal 以线程为作用域。 52 | 53 | Looper 的作用域是线程,且不同的线程具有不同的 Looper,因此可以通过 ThreadLocal 来存储线程的 Looper 对象。 54 | 55 | [Handler 源码分析完整版地址](http://xuchongyang.com/2017/03/10/Android-%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 56 | 57 | # AsyncTask 源码分析 58 | 1、首先调用 AsyncTask 的构造方法,构造时对 Handler、WorkerRunnable(Callable) 和 FutureTask 进行初始化 59 | 60 | 2、然后调用 AsyncTask 的 execute 方法(可以手动设置 Executor,不设置则使用系统默认的 SerialExecutor) 61 | 62 | 3、首先判断当前 AsyncTask 状态,正在运行或者已经运行过就退出 63 | 64 | 4、调用 onPreExecute 执行准备工作 65 | 66 | 5、由 Executor 调用 FutureTask 的 run 方法,在 WorkerRunnable 中执行了 doInBackground 67 | 68 | 6、依旧是在 WorkerRunnable 中,调用 postResult,将执行结果通过 Handler 发送给主线程;调用 publishProgress 时,也是通过 Handler 将消息发送到主线程的消息队列中 69 | 70 | [AsyncTask 源码分析完整版地址](http://xuchongyang.com/2017/05/07/AsyncTask-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 71 | 72 | # View 的工作机制源码分析 73 | 1、ViewGroup 首先根据自己的 MeasureSpec 和子 View 自己设定的宽高,来为每个子 View 生成 MeasureSpec 74 | 75 | 2、ViewGroup 循环调用每个子 View 的 measure 方法,同时把子 View 的 MeasureSpec 传递过去 76 | 77 | 3、子 View 根据父视图给它的 MeasureSpec 确定最终的测量大小 78 | 79 | 可以看出,决定子 View 测量宽高的因素有:父视图的 MeasureSpec、子 View 自身指定的大小 80 | 81 | [View 的工作机制源码分析完整版地址](http://xuchongyang.com/2017/08/20/View%20%E7%9A%84%E4%B8%89%E5%A4%A7%E6%B5%81%E7%A8%8B%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 82 | 83 | # Android 触摸事件分发机制源码分析 84 | 85 | 86 | [Android 触摸事件分发机制源码分析完整版地址](http://xuchongyang.com/2017/07/30/Android%20%E4%BA%8B%E4%BB%B6%E5%88%86%E5%8F%91%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%80%EF%BC%89%E8%A7%A6%E6%91%B8%E4%BA%8B%E4%BB%B6/) 87 | 88 | 触摸事件分发流程: 89 | 90 | 1、ViewGroup 的事件分发 91 | 92 | * 首先判断自身是否需要,需要则进行拦截,并调用自身的 onTouchEvent 方法进行使用并决定是否消费,子 View 对事件无感。 93 | * 自己不需要则不进行拦截,事件分发给子 View(事件会分发给手指触摸区域的子 View,有重叠时传递给最上层 View) 94 | * 子 View 不消费时,事件会回传,调用自身的 onTouchEvent 方法进行使用并决定是否消费;子 View 消费时事件不回传 95 | 96 | 伪代码: 97 | 98 | ```java 99 | public boolean dispatchTouchEvent(MotionEvent event) { 100 | // 默认状态为未消费过 101 | boolean result = false; 102 | // 如果没有拦截 103 | if (!onInterceptTouchEvent(event)) { 104 | // 则交给子 view 105 | result = child.dispatchTouchEvent(event); 106 | } 107 | // 如果事件没有被子 View 消费 108 | if (!result) { 109 | // 则询问自身 onTouchEvent() 110 | result = onTouchEvent(event); 111 | } 112 | // 返回事件消费状态 113 | return result; 114 | } 115 | ``` 116 | 117 | 2、View 的事件分发 118 | 119 | dispatchTouchEvent 方法对 View 的监听器和方法进行分发,顺序如下: 120 | 121 | onTouchListener -- > onTouchEvent -- > onLongClickListener -- > onClickListener 122 | 123 | # Android 按键事件分发机制源码分析 124 | 125 | [Android 按键事件分发机制源码分析完整版地址](http://xuchongyang.com/2017/08/03/Android%20%E4%BA%8B%E4%BB%B6%E5%88%86%E5%8F%91%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%BA%8C%EF%BC%89%E6%8C%89%E9%94%AE%E4%BA%8B%E4%BB%B6/) 126 | 127 | 分发流程: 128 | 129 | 1、Activity 的分发 130 | 131 | * Activity 接收到事件,为 Menu 键直接消费掉。否则分发给 PhoneWindow,PhoneWindow 直接传递给它的 DecorView 132 | * DecorView 对 Back 键进行判断是否消费,不消费则分发给 ViewGroup 133 | * ViewGroup 不消费时,事件分发给 KeyEvent 134 | 135 | 2、ViewGroup 的分发 136 | 137 | * ViewGroup 有焦点且大小确定时,首先自己处理 138 | * ViewGroup 的子 View 有焦点且大小确定时,分发给子 View 139 | * 层层向下分发,直到最底层 View 140 | 141 | 3、View 的分发 142 | 143 | View 首先将事件传递给 onKeyListner 监听器,不消费时传递给 KeyEvent 144 | 145 | 4、KeyEvent 的分发 146 | 147 | KeyEvent 会根据类型来分别决定回调 Activity 或 View 的 onKeyDown、onKeyUp、onKeyLongPress 方法 148 | 149 | 5、事件回传 150 | 151 | 当 Activity、View 的 onKeyDown、onKeyUp 也没有消费事件时,事件会进行回传 152 | 153 | # OkHttp 源码分析 154 | 示例: 155 | 156 | ```java 157 | mOkHttpClient.newCall(request).enqueue(callback); 158 | ``` 159 | ## 概述版 160 | * OkHttp 将请求信息封装在 Request 对象中,并创建一个 Call 对象 161 | * 调用 Call 对象的方法发起网络请求,采用责任链模式,将请求信息依次传递给每个拦截器,最终发起网络请求并返回结果 162 | 163 | ## 分析版 164 | 1、OkHttpClient 的构建采用了 Builder 模式,Builder 模式易写易读且线程安全,适用于类的构造器有多个参数的情况 165 | 166 | 2、OkHttpClient 实现了 Call.Factory 接口(newCall 方法),通过 newCall 方法可以得到一个 RealCall 对象,可以操作 RealCall 对象执行请求、取消等一系列操作 167 | 168 | 3、RealCall 执行 execute 方法时,首先会在 client 的同步执行任务队列中记录当前 Call,然后在当前线程通过拦截器链条获取执行结果,最后对列中移除 Call 169 | 170 | 4、拦截器链条采用责任链模式,会依次执行每一个拦截器的 proceed 方法,直到最终的 CallServerInterceptor 完成网络请求 171 | 172 | 5、RealCall 的 enqueue 方法会直接将 Call 添加到 dispatcher 的异步执行队列,执行网络请求时也是通过拦截器链条完成 173 | 174 | 6、请求相关的所有信息封装在 Request 对象中,对于 Post 请求体 RequestBody 可以由 Byte 数组、ByteString、File、String 生成 175 | 176 | [OkHttp 源码分析完整版地址](http://wiki.xuchongyang.com/1237005) 177 | 178 | # Retrofit 源码分析 179 | 示例: 180 | 181 | ```java 182 | mRetrofit.create(RequestService.class).doGet("params").enqueue(callback); 183 | ``` 184 | ## 概述版 185 | * Retrofit 接口层实际是对 OkHttp 中 Request 的封装,采用注解的形式来描述网络请求参数 186 | * 采用动态代理模式创建请求接口对象,请求执行调用接口方法时,Retrofit 会根据注解创建相应的Call对象,接下来使用OkHttp发起请求 187 | 188 | ## 分析版 189 | 1、首先需要使用注解的形式定义请求接口 190 | 191 | 2、然后通过建造者模式构造出 Retrofit 对象,Retrofit 对象的 create 方法采用动态代理模式,创建出请求接口的实例 192 | 193 | 3、发起请求时调用请求接口实例的相应方法,被调用处理器(InvocationHandler)拦截,最终返回一个 OkHttpCall 对象,OkHttpCall 有实现 Call 接口 194 | 195 | 4、接下来就是 OkHttp 的工作,调用 OkHttpCall 的 enqueue 或 execute 方法发起网络请求 196 | 197 | [Retrofit 源码分析完整版地址](http://wiki.xuchongyang.com/1253933) 198 | 199 | # ButterKnife 源码分析 200 | ## 概述版 201 | * 写代码时对目标元素进行注解,编译时注解处理器会扫描注解,并通过 JavaPoet 自动生成 Java 文件 202 | * 调用 ButterKnife.bind()方法时,通过反射拿到编译时生成的类,调用其构造方法完成目标类和绑定类的绑定 203 | 204 | ## 分析版 205 | 1、注解处理器 ButterKnifeProcessor 在编译时会扫描和处理所有注解,通过 JavaPoet 自动生成 .java 文件 206 | 207 | 2、调用 ButterKnife 的 bind 方法,通过反射拿到目标类类名,后面拼接`_ViewBinding`找到刚刚自动生成的 Java 类,并根据参数找到相匹配的构造器 208 | 209 | 3、调用 ViewBinding 类构造器的构造方法,将 ViewBinding 类和 目标类进行绑定 210 | 211 | 4、ViewBinding 类的构造方法中会对目标类的成员变量进行初始化及点击事件配置 212 | 213 | [ButterKnife 源码分析完整版地址](http://wiki.xuchongyang.com/1253934) 214 | 215 | # Dagger2 源码分析 -------------------------------------------------------------------------------- /面试题总结(Java部分).md: -------------------------------------------------------------------------------- 1 | ### 解释一下 OOP(Object-oriented programming) 的概念 2 | 3 | 1、什么是对象 4 | 5 | - 万物皆对象,对象是程序的基本单元 6 | - 类的定义包含数据的形式(成员变量)以及对数据的操作(成员方法),对象是类的实例 7 | - 每个对象都有自己的空间,可以容纳其他对象(成员变量) 8 | 9 | 2、面向对象三大特性 10 | 11 | - 面向对象编程三大特性:封装、继承、多态 12 | - 类可以封装自己的属性和方法(设置为私有)可参考[链接](http://www.cnblogs.com/chenssy/p/3351835.html) 13 | - 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据 14 | - 继承可以重用父类代码,达到代码复用和可扩展的效果 15 | - 多态就是对象的一个方法在执行时可以有多种状态,即父类引用可以持有子类对象。 16 | - 非静态成员方法的调用:编译时看父类,运行时看子类。即父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在于子类中的方法和属性无法调用 17 | - 多态优点:不用创建一堆子类对象的引用;多态缺点:不能使用子类特有的成员属性和子类特有的成员方法 18 | 19 | ### 抽象类和接口的区别?[Link](https://arjun-sna.github.io/java/2017/02/02/abstractvsinterface/) 20 | 21 | 区别: 22 | 23 | - 关键字不同:接口使用 `implements` 来实现;抽象类使用 `extends` 来继承 24 | - 抽象类可以包含具体方法和抽象方法;接口只可以包含抽象方法(抽象方法指没有实现的方法) 25 | - 抽象类的方法可以使用 public、protected、private 等修饰符;接口的方法默认使用 public abstract 修饰,成员变量默认使用 public static final 修饰 26 | - 一个接口可以继承(extends)多个父接口;一个抽象类只能继承(extends)一个父类 27 | - 接口可以理解为抽象方法的集合;抽象类可以理解为一个没有包含足够的信息去描述具体对象的类,终归还是类 28 | 29 | 相同点: 30 | 31 | - 接口和抽象类都不可以实例化 32 | 33 | ### 序列化是什么?如何实现它?[参考链接1](https://my.oschina.net/andot/blog/784235)、[参考链接2](https://tech.meituan.com/serialization_vs_deserialization.html)、[参考链接3](https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/) 34 | - 序列化指把应用层的对象或数据结构转换成一段连续的二进制串 35 | - 反序列化指把二进制串转换成应用层的对象或数据结构 36 | 37 | - 序列化按可读性可分为两类:二进制序列化、文本序列化(如 XML、JSON)以及半文本序列化 38 | - 文本序列化:可读性、可编辑性好 39 | - 二进制序列化:不具有可读性、但解析速度更有优势 40 | 41 | - 按跨语言能力可分为:特定语言专有序列化和跨语言序列化 42 | - 大部分语言内置的序列化都属于特定语言专有序列化,如 Java 的序列化 43 | - 文本序列化通常为跨语言序列化,如 JSON、XML等 44 | 45 | 46 | - Java 序列化是一种将对象转换为字节流的过程,目的是为了将该对象存储到内存中,等后面再次构建该对象时可以获取到该对象先前的状态及数据信息。 47 | - Java 中,有两种方式可以实现序列化,既可以实现 Serializable 接口,也可以实现 Parcelable 接口。 48 | - 然而,在 Android 中,我们不应该使用 Serializable 接口。因为 Serializable 接口使用了反射机制,这个过程相对缓慢,而且往往会产生出很多临时对象,这样可能会触发垃圾回收器频繁地进行垃圾回收。相比而言,Parcelable 接口比 Serializable 接口效率更高,性能方面要高出 10x 多倍。 49 | 50 | ### 什么是单例?[几种写法](http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/) 51 | 52 | - 单例模式是一种对象创建型模式,指的是一个类只能被初始化一次,即只有一个实例。 53 | - 这个类只能有一个实例 54 | - 它必须自行创建这个实例 55 | - 它必须自行向整个系统提供这个实例 56 | 57 | 单例模式几种写法对比: 58 | 59 | 1、懒汉式:非线程安全 60 | 61 | 2、懒汉式(加锁):线程安全了,但效率低,任何时候只能有一个线程调用方法 62 | 63 | 3、恶汉式:非懒加载,单例对象依赖参数或配置文件时就无法使用了 64 | 65 | 4、双重校验锁:推荐 66 | 67 | 5、匿名内部类:推荐!静态内部类是私有的,除 getInstance 没有其他方法可访问它,所以是懒加载;读取实例时不会进行同步,没有性能缺陷 68 | 69 | 6、枚举:Android 中不推荐 70 | 71 | ### 什么是匿名内部类?[参考链接](http://wiki.xuchongyang.com/Java/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9.html) 72 | 73 | * 匿名内部类即没有名字的内部类,只可被使用一次 74 | * 使用匿名内部类前提条件:必须继承自一个父类或实现一个接口 75 | * 语法格式为 new SuperType(construction parameters){} 76 | * 语法格式为 new 父类构造器(参数列表)|实现接口(){} 77 | * 直接将抽象类的方法或者接口方法在大括号中实现,可以省略一个类的书写 78 | * 匿名内部类中不能定义构造方法,但可以使用初始化语块代替构造方法 79 | 80 | ### 对字符串进行 `==` 和 `equals()` 操作时有什么区别? 81 | 82 | * == 用来判断内存地址是否相同 83 | * equals()用来判断字符串的内容是否相同 84 | 85 | ### `hashCode()` 和 `equals()` 何时使用? 86 | 87 | * 相等(相同)的对象必须具有相等的哈希码(或者散列码) 88 | * 如果两个对象的 hashCode 相同,它们并不一定相同 89 | 90 | * 往 Set 集合中添加元素时,要保证元素不重复,先调用该元素的 hasCode 方法,定位到它应该放置的物理位置。 如果这个位置上没有元素,就可以直接存储在这个位置上,不用再进行任何比较;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。 91 | * 集合查找时,hashcode 能大大降低对象比较次数,提高查找效率 92 | 93 | ### Java 中的 `final`, `finally` 和 `finalize`? 94 | 95 | * final:修饰基本数据类型时,代表常量(不可修改);修饰对象引用时,引用不可重新赋值,而对象依然可以修改;修饰方法时,代表该方法不可被重写;修饰类时,代表该类不可被继承 96 | * finally:异常处理时 try/catch/finally 语句中的 finally 语句块无论有没有异常都会执行 finally 语句块,当然也有特殊情况不执行 finally 97 | * finalize:垃圾回收器要回收对象的时候,首先会调用这个类的 finalize 方法,可以在该方法中释放调用本地方法时分配的内存 98 | 99 | ### 什么是内存泄露,Java 是如何处理它的? 100 | 101 | * 存在下面的这种对象,这些对象不会被 GC 回收,却占用着内存,即为内存泄漏(简单说:存在已申请的无用内存无法被回收) 102 | 103 | * 该对象是可达的,即还在被引用着 104 | * 该对象是无用的,即程序以后不会再使用该对象 105 | 106 | * 长生命周期的对象持有短生命周期的引用,就很可能会出现内存泄露 107 | * 内存泄漏可能的情况: 108 | 109 | * 静态集合类引起的 110 | * 非静态内部类 111 | * 单例模式 112 | * HashSet 集合元素的属性被修改 113 | * 各种连接问题 114 | 115 | ### 垃圾收集器是什么?它是如何工作的 116 | 117 | - 垃圾收集是一种自动的内存管理机制 118 | 119 | - 所有的对象实例都在 JVM 管理的堆区域分配内存 120 | - 只要对象被引用,JVM 就会认为它还存活于进程中 121 | - 一旦对象不再被引用,垃圾收集器将删除它并重新声明为未使用的内存 122 | 123 | ### 比较 `Array` 和 `ArrayList`[链接](http://blog.qianlicao.cn/translate/2016/03/09/array-vs-arraylist/) 124 | 125 | 不同点: 126 | 127 | 1、必答: 128 | 129 | * 灵活性:ArrayList 优于 Array,ArrayList 是动态的;Array 是静态的,创建了数组就无法更改它的大小 130 | * 元素类型:Array 既可以保存基本类型,也可以保存对象;ArrayList 不可以保存基本类型,可以保存封装类 131 | * 实现上:Array 是本地的程序设计组件或者数据结构,ArrayList 是一个 Java 集合类的类 132 | * 性能上:Array 会优于 ArrayList 133 | 134 | 2、其他 135 | 136 | * 类型安全方面:ArrayList 是类型安全的,支持编译时检查;Array 不是类型安全的,支持运行时检查 137 | * 泛型:ArrayList 可以显示的支持泛型,Array 不可以 138 | * 维度:Array 可以是多维度的,ArrayList 并不支持指定维度 139 | 140 | 相同点: 141 | 142 | * 数据结构:都是基于 index 的数据结构 143 | * 空值:都可以存储空值(null),但只有 Object 的数组可以这样,基本类型会存储他们的默认值 144 | * 重复:都允许元素重复 145 | 146 | ### 比较 `HashSet` 和 `TreeSet` 147 | 148 | * TreeSet 会自动按自然排序法给元素排序,HashSet 不会 149 | * 如果不需要使用排序功能,应该使用 HashSet,因为其性能优于 TreeSet 150 | * 如果 TreeSet 传入的是自定义的对象,必须让该对象实现 Comparable 接口 151 | 152 | ### Java 中的类型转换 153 | Java 中的类型转换分为基本数据类型和引用数据类型 154 | 155 | * 基本数据类型 156 | 157 | 基本数据类型的转换分为自动转换和强制转换 158 | 159 | * 自动转换是从位数低的类型向位数高的类型转换 160 | * 强制类型转是从位数高的类型向位数低的类型转换,需要转型的数据前加上“()”,然后在括号内加入需要转化的数据类型,转换会导致精度丢失 161 | 162 | * 引用数据类型 163 | 164 | * 子类可以转换成父类 165 | * 父类转换为子类不一定可行:当父类(引用)引用的为子类对象时,此时父类可以正常转换为子类;而当父类(引用)引用的确实为父类对象时,此时无法转换为子类,会抛出 ClassCastException 异常 166 | 167 | ### 静态绑定和动态绑定的区别 168 | 1、什么是绑定? 169 | 170 | 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。 171 | 172 | 2、定义 173 | 174 | * 静态绑定:编译过程中编译器已经准确的知道这个方法是属于哪个类的了 175 | * 动态绑定:需要在运行时根据对象具体的类型来绑定,来选择调用哪个类的方法 176 | 177 | 3、区分 178 | 179 | * private 方法、static 方法、final 方法、构造器方法都是静态绑定的 180 | * 其他方法全部为动态绑定 181 | 182 | ### 方法重载和重写的区别[链接](http://droidyue.com/blog/2014/12/28/static-biding-and-dynamic-binding-in-java/index.html) 183 | 184 | ![](https://github.com/stormzhang/android-interview-questions-cn/blob/master/assets/overloading-vs-overriding.png) 185 | 186 | * 重载是在同一个类中完成的,重写父类需要有子类。 187 | * 方法重载时返回值类型、参数列表可以不同;方法重写时返回值类型、参数列表必须相同 188 | * 重载的方法使用静态绑定完成,发生在编译时;重写的方法使用动态绑定完成,发生在运行时。性能:重载比重写更有效率 189 | * private 方法、static 方法、final 方法都是可以重载,但不可以重写 190 | 191 | ### 什么是访问修饰符?它们能做什么? 192 | 193 | * 访问修饰符是指能够控制类、成员变量、方法的使用权限的关键字 194 | * 可以使用它们来保护对类、变量、方法和构造方法的访问 195 | * public:共有的,对所有类可见 196 | * protected:受保护的,对同一包内的类和所有子类可见 197 | * private:私有的,在同一类内可见 198 | * 默认:在同一包内可见。默认不使用任何修饰符。 199 | 200 | ### 接口可以继承另一个接口吗? 201 | 202 | 可以,Java 允许一个接口继承多个父接口。 203 | 204 | ### Java 中 `static` 关键字是什么意思?[链接](http://www.cnblogs.com/chenssy/p/3386721.html) 205 | 206 | * static 表示“全局”或者“静态”的意思,用来修饰成员变量、成员方法或者内部类,也可以修饰代码块 207 | 208 | * static 所蕴含“静态”的概念表示着它是不可恢复的,即在那个地方,你修改了,他是不会变回原样的,你清理了,他就不会回来了 209 | * 被 static 修饰的成员变量和成员方法是独立于该类的,它不依赖于某个特定的实例变量,也就是说它被该类的所有实例共享 210 | * 所有实例的引用都指向同一个地方,任何一个实例对其的修改都会导致其他实例的变化 211 | * 静态变量是随着类加载时被完成初始化的;静态方法也是类加载时就存在了,所以不可为 abstract,必须实现;静态代码块也会随着类的加载一块执行 212 | 213 | ### Java 中静态方法可以被重写吗? 214 | 215 | * 不可以被重写,静态方法隶属于某个类,只和类相关 216 | 217 | ### 什么是多态?什么是继承?[链接](http://www.cnblogs.com/chenssy/p/3354884.html) 218 | 219 | 多态: 220 | 221 | - 多态就是父类引用可以持有子类对象。非静态成员方法的调用:编译时看父类,运行时看子类 222 | - 多态优点:不用创建一堆子类对象的引用 223 | - 多态缺点:不能使用子类特有的成员属性和子类特有的成员方法 224 | 225 | 继承: 226 | 227 | * 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类 228 | * 通过使用继承我们能够非常方便地复用以前的代码,大大提高开发的效率 229 | 230 | ### `Integer` 和 `int` 之间的区别[自动装箱、自动拆箱](http://droidyue.com/blog/2015/04/07/autoboxing-and-autounboxing-in-java/index.html) 231 | 232 | * int 是基本数据类型,Integer 是包装类 233 | * Java中,会对 -128 到 127 的 Integer 对象进行缓存,当创建新的 Integer 对象时,如果符合这个这个范围,并且已有存在的相同值的对象,则返回这个对象,否则创建新的 Integer 对象 234 | 235 | ### Java 中的对象是否会以引用传递或者值传递?详细说明。[参考链接](http://xuchongyang.com/2017/10/11/Java-%E5%BC%95%E7%94%A8%E5%AD%A6%E4%B9%A0/) 236 | 237 | Java 中的对象以按值传递的形式进行调用,所有方法得到的都是参数值的拷贝 238 | 239 | * 对于基本数据类型的参数,方法得到的是其值的拷贝 240 | * 对于引用数据类型的参数,方法得到的也是其值(所指向对象的内存地址)的拷贝 241 | * 方法均不可以修改参数变量的值,对于引用类型参数来说,就是不能让引用类型参数指向新的对象 242 | * 但是方法可以修改引用类型参数所指向对象的内容,因为方法得到的是该对象地址的拷贝,可以根据地址获取到该对象 243 | 244 | ### 什么是 ThreadPoolExecutor? [Link](https://blog.mindorks.com/threadpoolexecutor-in-android-8e9d22330ee3) 245 | 246 | * 线程池的目的是实现线程复用,减少频繁创建和销毁线程,提高系统性能 247 | * ThreadPoolExecutor 是线程池的核心类,用于创建线程池 248 | * ThreadPoolExecutor 的构造方法包括如下参数: 249 | 250 | * corePoolSize 核心线程池大小 251 | * maximumPoolSize 线程池最大容量 252 | * keepAliveTime 线程池空闲时,线程存活时间 253 | * TimeUnit 时间单位 254 | * ThreadFactory 线程工厂 255 | * BlockingQueue 任务队列 256 | * RejectedExecutionHandler 线程拒绝策略 257 | 258 | 我们通常通过 Executors 类的如下几个静态方法来创建不同类型的线程池: 259 | 260 | * newCachedThreadPool:可缓存线程池,无限大 261 | * newFixedThreadPool:定长线程池 262 | * newScheduledThreadPool:定长线程池,支持定时及周期性任务执行 263 | * newSingleThreadExecutor:单个线程池 264 | 265 | ### 本地变量、实例变量以及类变量之间的区别? 266 | 267 | * 类体由两部分组成,变量定义和方法定义 268 | * 本地变量即局部变量,定义在方法内部或者方法的形参中 269 | * 实例变量为为非静态变量,每一个对象的实例变量都不同 270 | * 类变量为静态变量,属于类所有 271 | 272 | ### 什么是反射? [Link](http://tutorials.jenkov.com/java-reflection/index.html) 273 | 274 | ### 在 Java 中什么是强引用、软引用、弱引用以及虚引用?[参考链接1](http://xuchongyang.com/2017/10/11/Java-%E5%BC%95%E7%94%A8%E5%AD%A6%E4%B9%A0/)[参考链接2](http://blog.csdn.net/mazhimazh/article/details/19752475) 275 | 276 | * 强引用:是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。 277 | * 软引用:如果一个对象只具有软引用,且内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。 278 | * 弱引用:只具有弱引用的对象拥有比软引用更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。 279 | * 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。一般用于跟踪对象被垃圾回收器回收过程。 280 | 281 | ### 什么是依赖注入?能说几个依赖注入的库么?你使用过哪些? 282 | 283 | ### 关键字 `synchronized` 的作用是什么?[参考链接](http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html#!comments) 284 | 285 | * 当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。 286 | * 当两个并发线程访问同一个对象 object 中的这个 synchronized(this) 同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。 287 | * 然而,当一个线程访问 object 的一个 synchronized(this) 同步代码块时,另一个线程仍然可以访问该 object 中的非 synchronized(this) 同步代码块。 288 | * 尤其关键的是,当一个线程访问 object 的一个 synchronized(this) 同步代码块时,其他线程对 object 中 *所有其它 synchronized(this) 同步代码块* 的访问将被阻塞。 289 | * 上一个例子同样适用其它同步代码块。也就是说,当一个线程访问 object 的一个 synchronized(this) 同步代码块时,它就获得了这个 object 的对象锁。结果,其它线程对该 object 对象 *所有同步代码部分* 的访问都被暂时阻塞。 290 | 291 | ### 为什么说 `String` 不可变的?[参考链接](http://blog.csdn.net/zhangjg_blog/article/details/18319521) 292 | 293 | * 什么是不可变对象 294 | 295 | * 如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。 296 | * 不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。 297 | 298 | 299 | * 在 Java 中 String 类其实就是对字符数组的封装 300 | * String 类中有一个 char 数组的 value 引用变量,value 指向真正的数组对象 301 | * JDK 1.6 及以前还有 offset 和 count 两个变量 302 | * 这三个变量都是 private final 的,即一旦这三个值被初始化,就不能再改变了 303 | * 但是可以通过反射改变 value 变量引用的对象,进而改变 String 对象 304 | 305 | ### 修饰符 `transient` 和 `volatile` 的作用是什么? 306 | 307 | ### `finalize()` 方法的作用是什么? 308 | 309 | ### 异常捕获中的 `try{} finally{}` 块儿是如何工作的? 310 | 311 | ### 类的实例化和类的初始化之间的区别是什么?[参考链接1](https://www.caoqq.net/java-class-initialization.html)、[参考链接2](http://blog.csdn.net/czhpxl007/article/details/50558319)、[参考链接3](http://blog.csdn.net/justloveyou_/article/details/72466416) 312 | 313 | * 1.执行顺序不同: 314 | 315 | * (类)初始化执行顺序:静态成员变量/静态代码块 316 | * (对象)实例化执行顺序:成员变量/构造代码块 -> 构造方法 317 | * 以上,静态成员变量/静态代码块的执行顺序为代码中的定义顺序,成员变量/构造代码块的执行顺序也为代码中的定义顺序 318 | 319 | * 2.执行时机不同 320 | 321 | 类的初始化时机: 322 | 323 | * 类的一个实例被创建时 324 | * 类的一个静态方法被调用时 325 | * 类声明的一个静态变量被赋值时 326 | * 类声明的一个静态变量被使用且这个变量不是常量时 327 | * 一个类初始化时,如果父类没初始化则需要先初始化父类 328 | 329 | 类的实例化时机 330 | 331 | * 当创建一个类的新的实例时(可以通过 new 关键字、反射机制、clone 方法、反序列化方式等) 332 | 333 | ### 静态块何时运行?[链接](http://www.jianshu.com/p/8a3d0699a923) 334 | 335 | * 静态块在 JVM 加载类时执行,也就是在类进行初始化时执行,仅执行一次 336 | * 一个类中可以有多个静态代码块 337 | * 类调用时,先执行静态代码块,然后才执行主函数 338 | * 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的 339 | * 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别 340 | 341 | ### 解释一下 Java 中的泛型?[参考链接](http://www.infoq.com/cn/articles/cf-java-generics) 342 | 343 | * 泛型即宽泛的数据类型 344 | * 允许在定义类和接口的时候使用类型参数(type parameter) 345 | * 声明的类型参数在使用时用具体的类型来替换 346 | * 使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,这个过程就称为类型擦除 347 | 348 | ### `String`、`StringBuffer` 和`StringBuilder` 的区别在哪里? 349 | 350 | * String 是不可变对象,因此每次对 String 对象进行更改时,都会生成一个新的对象,然后将指针指向新的对象。 351 | * 使用 StringBuffer 类时,每次都是对 StringBuffer 对象本身进行操作,并不是生成新的对象并改变引用。线程安全! 352 | * StringBuilder 和 StringBuffer 非常类似,但是是非线程安全的,在单线程中性能比 StringBuffer 高。非线程安全! 353 | 354 | ### `StringBuilder` 是怎么避免不可变字符串分配的问题? 355 | 356 | ### 什么是自动装箱和拆箱? 357 | 358 | * 自动装箱就是 Java 自动将原始类型值转换成对应的对象,比如将 int 的变量转换成 Integer 对象 359 | * 反之将 Integer 对象自动转换成 int 类型值,这个过程叫做拆箱 360 | * 自动装箱时编译器调用 valueOf 将原始类型值转换成对象 361 | * 自动拆箱时,编译器通过调用类似 intValue(), doubleValue() 这类的方法将对象转换成原始类型值 362 | * 自动装箱、拆箱主要发生在 363 | * 赋值时 364 | * 方法调用时 365 | 366 | ### 枚举和迭代器有什么区别? 367 | 368 | ### Java 中 *fail-fast* 和 *fail-safe* 的区别? 369 | 370 | ### 什么是 Java 优先级队列? 371 | 372 | ### 什么是注解? 373 | 374 | ### 多进程和多线程的区别[(参考!经常看!)](http://markxu.coding.me/wiki/%E5%A4%9A%E7%BA%BF%E7%A8%8B/Java%E5%9F%BA%E7%A1%80%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html) 375 | * 本质区别在于:每个进程拥有自己的一整套变量,而线程则共享数据。 376 | 377 | * 多进程: 378 | 379 | 进程是程序的一次执行。计算机在同一刻运行多个程序,每个程序称为一个进程(计算机将 CPU 的时间片分配给每一个进程) 380 | 381 | * 多线程: 382 | 383 | 线程是 CPU 的基本调度单位。一个程序同时执行多个任务,每个任务称为一个线程 384 | 385 | 补充:线程的状态切换 386 | 387 | ![](http://oj1xifth5.bkt.clouddn.com/thread_1.png) 388 | 389 | ### 进程间通讯方法,线程间通讯方法 390 | 1、进程间通讯 391 | 392 | * Broadcast 393 | * Intent/Bundle 394 | * File 共享 395 | * Messenger 396 | * AIDL 397 | * ContentProvider 398 | * Socket 399 | 400 | 2、线程间通讯 401 | 402 | * Handler(AsyncTask、Message、runOnUiThread 等) 403 | * EventBus 404 | * LocalBroadcast 405 | 406 | ### sleep 和 wait 的区别 407 | 二者都可以暂停当前线程,释放 CPU 控制权,区别在于: 408 | 409 | 作用于谁和是否释放锁? 410 | 411 | * wait 方法作用于 Object,sleep 方法作用于 Thread 412 | * Object.wait 方法在释放 CPU 的同时,释放了对象锁的控制,使得其他线程可以使用同步控制块或方法;Thread.sleep 方法没有释放锁 413 | 414 | ### JNI 是什么 415 | Java native interface,Java 本地接口 416 | 417 | ### 概述 Java 垃圾回收机制,如何更有效的管理内存,减少 OOM 的概率[参考](http://www.cnblogs.com/vamei/archive/2013/04/28/3048353.html) 418 | 1、内存结构: 419 | 420 | * 内存分为栈(satck)和堆(heap)两部分 421 | * JVM 中栈记录了方法调用,每个线程拥有一个栈(栈的每一帧中保存有该方法调用的参数、局部变量和返回地址) 422 | * 栈中被调用方法运行结束时,相应的帧也会删除,参数和局部变量占用的空间也会释放 423 | * 堆是 JVM 中可以自由分配给对象的区域,堆区由所有线程共享 424 | 425 | 2、垃圾回收 426 | 427 | * JVM 自动清空堆中无用对象占用的空间就是垃圾回收 428 | * 当一个对象没有引用指向它时,为不可达对象,此时会被回收 429 | 430 | 3、对象是否回收的依据 431 | 432 | * 引用计数算法 433 | * 可达性分析算法 434 | 435 | 4、回收基础 436 | 437 | * Mark and sweep 机制:每个对象都有用于表示该对象是否可达的标记信息。垃圾回收启动时,Java 程序暂停运行,JVM 从根出发,找到所有可达对象并标记。然后扫描整个堆找到不可达对象,清空它们占用的内存 438 | * Copy and sweep 机制:堆被分为两个区域,对象存活于两个堆中的一个。垃圾回收启动时,Java 程序暂停运行,JVM 从根出发找到所有可到达对象,把所有可到达对象复制到空白区域中并紧密排列(并修改由于对象地址变化引起的引用变化)。最后直接清空对象原先所存活的区域,使其成为新的空白区域。 439 | * 对象比较长寿适用于 mark and sweep 机制;对象比较活跃则适用于 copy and sweep 机制,避免出现空隙 440 | * 世代指对象经历过的垃圾回收的次数,堆分为三个世代:永久世代、成熟世代、年轻世代 441 | * 永久世代的对象不会被垃圾回收 442 | * 年轻世代进一步分为三个区域:eden 区、from 区、to 区 443 | * 新生对象指从上次垃圾回收后创建的对象,存在于 eden 区。from 区和 to 区相当于 copy and sweep 中的两个区 444 | 445 | 5、分代回收 446 | 447 | * 新建对象无法放入 eden 区时,会触发 minor collection,JVM 会采用 copy and sweep 机制将 eden 区和 from 区的可到达对象复制到 to 区,进行一次垃圾回收清空 eden 区和 from 区,to 区则存放着紧密排列的对象。接着 from 区成为了新的 to 区,原来的 to 区成为了新的 from 区 448 | * 当 minor collection 时发现 to 区也放不下时,会将部分对象放入成熟世代 449 | * 即使 to 区没有满,JVM 也会移动世代足够久远的对象到成熟世代 450 | * 如果成熟世代放满对象无法放入新的对象,会触发 minor collection,JVM 采用 mark and sweep 机制对成熟世代进行垃圾回收 -------------------------------------------------------------------------------- /面试题总结(Android部分).md: -------------------------------------------------------------------------------- 1 | ### 阐述一下 Activity 的生命周期。[参考链接](https://developer.android.com/guide/components/activities.html?hl=zh-cn) 2 | 3 | ![](http://oj1xifth5.bkt.clouddn.com/activity_lifecycle.png) 4 | 5 | 1、Activity 生命周期的顺序为: 6 | 7 | onCreate() -> onContentChanged() -> onStart() -> onPostCreate()-> onResume() -> onPostResume -> onPause() -> onStop() -> onDestroy() 8 | 9 | * onCreate() 方法:首次创建 Activity 时调用,应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 10 | * onRestart() 方法:在 Activity 已停止(调用 onStop())并即将再次启动前调用,始终后接 onStart() 11 | * onStart() 方法:在 Activity 即将对用户可见之前调用,可后接 onStop()进入隐藏状态 12 | * onResume() 方法:在 Activity 即将开始与用户进行交互之前调用,始终后接 onPause() 13 | * onPause() 方法:当系统即将开始继续另一个 Activity 时调用。用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容。应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。 14 | * onStop() 方法:在 Activity 对用户不再可见时调用。可后接 onRestart()。 15 | * onDestroy() 方法:在 Activity 被销毁前调用。 16 | 17 | 2、方法调用后进程是否可终止问题: 18 | 19 | * onCreate(),onRestart(),onStart(),onResume() 调用后,系统不能随时终止 Activity 进程。 20 | * 而 onPause(),onStop(),onDestroy() 调用后,可以终止。 21 | * Activity 创建后,必须要走到 onPause() 方法,然后才能终止进程 — 如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop() 和 onDestroy()。 22 | * 所以,在 onPause() 调用时,向存储设备写入至关重要的持久性数据(例如用户编辑)。 23 | 24 | 3、其他几个关键点 25 | 26 | * onPause 必须先执行完,新 Activity 的 onResume 才会执行 27 | * 新 Activity 采用透明主题或为 dialog 样式,当前 Activity 不会回调 onStop 28 | * 异常终止问题 29 | 30 | * Activity 异常终止时,会调用 onSaveInstanceState 来保存当前 Activity 状态,调用时机在 onStop 之前,和 onPause 没有固定顺序 31 | * onRestoreInstanceState 方法调用时机在 onStart 之后 32 | * onRestoreInstanceState 一旦被调用,其参数 Bundle savedInstanceState 一定是有值的,不用判断非空;而 onCreate 的 Bundle savedInstanceState 在正常启动时为 null 33 | 34 | ### 谈谈 Android 的四大组件。[参考链接1](http://wiki.xuchongyang.com/Android/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9.html)、[参考链接2](http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html) 35 | 36 | 四大组件:Activity、Service、BroadcastReceiver、ContentProvider 37 | 38 | * Activity: 39 | 40 | * 一个 Activity 通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应 41 | * Activity 之间通过 Intent 进行通信,应用内使用显式 Intent,跨应用使用隐式 Intent 42 | * 在隐式 Intent 中,有三个最重要的部分:动作(Action)、类别(category)以及动作对应的数据(data) 43 | * Action、category 和 Uri 分别对应着 intent-filter 中的 action、category 和 data 44 | 45 | * Service: 46 | 47 | * Service 是实现程序后台运行的解决方案,非常适合去执行不需要和用户交互(即没有界面)并且还要求长期执行的任务 48 | * 通过 Context 的 startService 和 stopService 可以启动、停止服务,启动服务后启动者和服务就没有关系了,启动者结束生命周期,服务依然在 49 | * 通过 Context 的 bindService 和 unBindService 可以绑定、解绑服务,绑定服务后服务会返回给启动者一个 IBind 接口实例,启动者可以通过这个接口实例回调服务的方法。此时 Context 退出,服务也会跟着解绑退出 50 | 51 | * BroadcastReceiver: 52 | 53 | * 应用可以使用广播接收器对感兴趣的外部事件或内部事件进行注册监听,可以静态注册、动态注册 54 | * 广播接收器没有用户界面。但它们可以启动一个 Activity 或 Service 来响应它们收到的信息,或者用 NotificationManager 来通知用户 55 | * 广播分为普通广播、有序广播、粘性广播。注册有序广播接收器可以设定接收优先级按序接收或拦截;发送粘性广播后注册广播接收器也可以接收到该粘性广播 56 | 57 | * ContentProvider:可参考《第二行代码》Page 270 58 | 59 | * 内容提供器用于提供在不同程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性 60 | * 每个 ContentProvider 都用一个 Uri 作为独立的标识 61 | 62 | * 访问其他程序中的数据 63 | 64 | * 可以通过 Context 的 getContentResolver 方法获取到 ContentResolver 的实例,通过该实例的一系列方法对目标数据进行 CRUD 操作 65 | * 通过内容 Uri 表明想要访问的目标程序 66 | 67 | * 创建自己的内容提供器 68 | 69 | * 继承 ContentProvider,实现抽象方法 70 | * 通过 UriMacther 类的 match 方法判断其他程序想要访问的数据地址 71 | 72 | ### Service 与 IntentService 的区别。[Link](https://stackoverflow.com/a/15772151/5153275) 73 | 74 | * 相同点: 75 | 76 | * 都是在后台运行,不可见 77 | * 都可在任何线程、系统组件进行开启 78 | 79 | * 不同点: 80 | 81 | * Service 默认运行在主线程,不可直接做耗时操作; 82 | * IntentService 默认运行在子线程,可直接执行耗时操作 83 | * Service 不会自动退出,需要手动 stopSelf、stopService、unbindService 或者内存不足被动回收; 84 | * IntentService 的 onHandleIntent 方法执行完毕后会自动退出。当有多次请求(即多次调用 startService)时,因只有一个工作线程,所以会一个一个进行处理,处理完毕后退出 85 | 86 | ### Android 应用中如何保存数据。 87 | 88 | 分为保存键值集(SharedPreferences)、保存文件(分为内部存储和外部存储)、在 SQL 数据库中保存数据以及在网络中使用网络存储和 ContentProvider。 89 | 90 | 其中: 91 | 92 | * 键值集适合保存私有原始轻量化数据 93 | * 内部存储适合保存私有数据 94 | * 外部存储适合保存公共数据 95 | * 私有数据库中适合保存结构化数据 96 | * 网络存储需使用自己的网络服务器存储数据 97 | 98 | ### 如何在 Android 应用中执行耗时操作。 99 | 100 | 将耗时操作放到非 UI 线程执行,常用的包括:AsyncTask、Handler 配合 Thread 等。 101 | 102 | ### 两个 Fragment 之间如何通信。[参考链接](http://blog.csdn.net/u012702547/article/details/49786417) 103 | 104 | 1、 拿到另一个 Fragment 的实例 105 | 106 | * 通过宿主 Activity 拿到 FragmentManager,进而再 findFragmentById 获取到另一个 Fragment,调用其公开方法 107 | * 使用 Fragment 的 setArguments 获取到另一个 Fragment 的实例 108 | 109 | 2、通过 Activity 来操作 110 | 111 | * 使用接口:在一个 Fragment 中定义接口,并在 onAttach 方法中拿到 Activity 初始化接口对象。Activity 来实现该接口,并在实现中调用另一个 Fragment 的相应方法 112 | * 使用 Activity 的公共方法:直接 getActivity 并调用 Activity 的公共方法 113 | 114 | 3、使用广播 115 | 116 | * 使用广播 117 | * 使用 EventBus 118 | 119 | ### Activity 之间如何通信,Activity 与 Service 之间如何通信 120 | #### Activity 之间通信方式 121 | 1、基于消息的通讯机制:Intent。缺点:只能传递基本数据类型或序列化数据,不可序列化的数据无法传递 122 | 123 | 2、广播 124 | 125 | 3、EventBus 126 | 127 | 4、数据存储(File、SharedPreferences、SQLite、ContentProvider) 128 | 129 | 5、利用 static 静态数据 130 | 131 | #### Activity 与 Service 之间 132 | 1、Binder 对象 133 | 134 | 2、广播 135 | 136 | 3、EventBus 137 | 138 | 4、Intent 139 | 140 | ### 两个不同的 app 之间如何交互。 141 | 142 | * Activity(Intent) 143 | * Broadcast 144 | * ContentProvider 145 | * Service(AIDL)跨进程通信 146 | * 利用 ShareUserId 共享数据:两个应用的 ShareUserId 相同,则共享对方 data 目录下的文件 147 | 148 | ### 什么是 Fragment? 149 | 150 | * 碎片是一种可以嵌入在活动中的 UI 片段 151 | * 可以将多个碎片组合在一个 Activity 中,也可以在多个 Activity 中重复使用一个碎片 152 | * 可以在 Activity 运行时动态添加、删除碎片 153 | * 可以把碎片当作 Activity 的模块化组成部分,具有自己的生命周期,能接收自己的输入事件 154 | 155 | ### 为什么建议只使用默认的构造方法来创建 Fragment?[Link](https://stackoverflow.com/a/16042750/2809326) 156 | 157 | * 应当使用默认构造方法创建 Fragment,然后使用 setArguments 添加 Bundle 来传递参数 158 | * 因为系统在储存 Fragment 的状态时(比如说屏幕旋转导致的重新加载),系统会为我们自动存储 Bundle 159 | 160 | ### 为什么 Bundle 被用来传递数据,为什么不能使用简单的 Map 数据结构?[链接1](https://github.com/android-cn/android-discuss/issues/142),[链接2](http://www.codes51.com/article/detail_163576.html),[链接3](http://droidyue.com/blog/2017/02/12/dive-into-arraymap-in-android/index.html) 161 | 162 | 原因一:速度和内存占用问题 163 | 164 | * 使用 Bundle 传递数据一般用于启动 Activity,启动 Fragment 时,数据量一般都不大 165 | * Bundle 内部由 ArrayMap 实现。ArrayMap 的内部实现是两个数组,一个数组存放 key 的 hashCode 值,一个数组存放 key 与 value 值。在查找、添加、删除数据时,会根据二分查找法查找 key 的 hashCode 值所对应的 index 值,再根据 index 去查找 key 所对应的 value 值 166 | * HashMap 的实现则是采用数组加链表的结构,默认使用一个容量为 16 的数组来存储,数组中每一个元素又是一个链表的头节点 167 | * 所以在小数据量的情况下,ArrayMap(Bundle) 比 HashMap 在速度和内存上都更占优势 168 | 169 | 原因二:序列化问题 170 | 171 | * Android 中使用 Intent 来传递数据时,需要保证数据是基本数据类型或可序列化类型 172 | * Bundle 使用 Parcelable 序列化,HashMap 使用 Serialable 序列化 173 | * 在 Android 中,更推荐使用 Parcelable 序列化,因为 Serializable 接口使用了反射机制,这个过程相对缓慢,而且往往会产生出很多临时对象,可能会触发垃圾回收器频繁地进行垃圾回收。相比而言,Parcelable 接口比 Serializable 接口效率更高,性能方面要高出 10x 多倍。 174 | 175 | ### 阐述一下 Fragment 的生命周期。[参考链接](https://developer.android.com/guide/components/fragments.html?hl=zh-cn) 176 | 177 | ![](http://oj1pajfyu.bkt.clouddn.com/fragment_lifecycle.png) 178 | 179 | * Fragment 的生命周期与 Activity 的生命周期协调一致 180 | * Activity 的每次生命周期回调都会引发每个片段的类似回调 181 | * 片段还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,以执行构建和销毁片段 UI 等操作,包括: 182 | * onAttach:在片段已与 Activity 关联时调用 183 | * onCreateView:在这可创建片段的视图层次结构 184 | * onActivityCreated:在 Activity 的 onCreate() 方法已返回时调用 185 | * onDestroyView:在移除与片段关联的视图层次结构时调用 186 | * onDetach:在取消片段与 Activity 的关联时调用 187 | 188 | 下图为 Activity 的每个连续状态如何决定片段可以收到的回调方法 189 | 190 | ![](http://oj1pajfyu.bkt.clouddn.com/activity_fragment_lifecycle.png) 191 | 192 | ### 解释下 Android 的 View 。 193 | 194 | * View 是 UI 组件最基本的构建块 195 | * 一个 View 占据屏幕的一块区域,负责绘制和事件处理 196 | 197 | ### Activity、Window、View 关系[参考](https://www.jianshu.com/p/a533467f5af5) 198 | * 每个 Activity 包含了唯一的一个 PhoneWindow(Window 的子类),在 attach 方法里进行初始化 199 | * Activity 实现了 Window 的接口,Window 接收到外界的改变时就会回调 Activity 相应的方法 200 | * View 是 Android 中的视图单元,必须依附于 Window 而存在 201 | * View 和 Window 之间通过 ViewRootImpl 进行关联到一起,ViewRootImpl 可以看作 ViewTree 的管理者(Activity 被创建完成后,会将 DecorView 添加到 Window 中,同时创建 ViewRootImpl 并与 DecorView 关联) 202 | 203 | ### 你能创建自定义 View 吗?具体是如何创建的?工作原理?[参考链接](http://www.gcssloop.com/customview/CustomViewProcess) 204 | 205 | * 自定义 View 包括自定义 View 和自定义 ViewGroup 206 | * 自定义 View 一般继承自 View、SurfaceView 或其他 View,不包含子 View 207 | * 自定义 ViewGroup 是利用现有的组件,根据特定的布局方式,来组成新的组件。一般继承自 ViewGroup 或各种 Layout,包含有子 View 208 | * 自定义 View 的流程: 209 | 210 | * 构造函数:初始化一些内容,获取自定义属性 211 | * 测量 View 的大小(onMeasure):View 大小不仅由自身决定,也会受父控件的影响,我们自己进行测量可以适应各种情况 212 | * 确定 View 的大小(onSizeChanged):在视图大小发生改变时调用 213 | * 确定子 View 布局(onLayout):在自定义 ViewGroup 时用到,用于确定子 View 的位置。循环取出子 View,计算出各个子 View 的位置,调用子 View 的 layout 方法设置其位置 214 | * 绘制内容(onDraw) 215 | * 对外提供操作方法和监听回调:控制 View 的状态,监听 View 的变化等 216 | 217 | 注:onMeasure 中 View 的测量模式与 View 绘制流程中的测量时使用的测量模式一样,可参见[View 三大流程源码分析](http://xuchongyang.com/2017/08/20/View%20%E7%9A%84%E4%B8%89%E5%A4%A7%E6%B5%81%E7%A8%8B%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 218 | 219 | ![](https://ws1.sinaimg.cn/large/c14636dely1fl58olhwpjj20fc0heaay.jpg) 220 | 221 | ### 什么是 ViewGroup ,它与 View 的区别在哪里? 222 | 223 | * ViewGroup 是一个放置 View 的容器,继承自 View,是一种特殊的 View 224 | * ViewGroup 的职能为:给 childView 计算出建议的宽、高和测量模式;决定 childView 的位置; 225 | * View 的职能为:根据测量模式和 ViewGroup 建议的宽高计算出自己的宽和高;在 ViewGroup 为其指定的区域内绘制自己的形态 226 | 227 | ### Fragment 和 Activity 有什么区别?它们之间又有什么关系? 228 | 229 | * Fragment 在 Android 3.0 后引入,可以翻译为碎片、片段 230 | * Activity 为活动,代表着当前屏幕显示的一个窗口 231 | * Fragment 可以重用,必须嵌套在 Activity 中使用,由 Activity 的 FragmentManager 管理,生命周期受 Activity 影响 232 | 233 | ### 谈谈 Serializable 接口和 Parcelable 接口的区别。在 Android 中最好使用哪种接口? 234 | 235 | ### Activity 的启动模式有哪些?[参考链接](http://wiki.xuchongyang.com/Android/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9.html) 236 | 237 | * standard 模式:创建新实例,放入当前任务中 238 | * singleTop (栈顶复用)模式: 239 | 240 | * 当前任务中已存在,且在栈顶,不创建 241 | * 当前任务中已存在,不在栈顶,创建新实例 242 | * 当前任务中不存在,创建新实例 243 | 244 | * singleTask (栈内复用)模式: 245 | 246 | * 首先检测要启动 Activity 所需任务栈是否存在(根据 affinity 判断) 247 | * 不存在:创建新任务栈,创建新实例 248 | * 存在:如果为后台任务栈就切换为前台任务栈 -> 有实例则 clearTop,没有实例就创建新实例 249 | 250 | * SingleInstance 模式: 251 | 252 | * 启动一个新的任务,创建新实例 253 | * 并且新的任务中不会再添加其他 Activity 254 | 255 | ### Activity 启动模式的应用场景? 256 | ### 解释一下 Android 中的 Intent 。[Link](https://stackoverflow.com/questions/6578051/what-is-an-intent-in-android) 257 | 258 | 1、Intent 是一个消息传递对象,可以启动 Activity、Service,发送广播,同时携带必要的数据。 259 | 260 | 2、分为显式 Intent 和隐式 Intent,显式 Intent 按名称指定要启动的组件,隐式 Intent 则是声明要执行的操作,允许其他的应用组件来处理。 261 | 262 | 3、Intent 的实现是基于 Binder 机制;Intent 可以通过 Bundle 携带可序列化的对象,Bundle 使用 Parcelable 进行序列化数据。 263 | 264 | ### 什么是隐式 Intent ? 265 | 266 | * 不指定特定的组件,而是声明要执行的常规操作(Action),从而允许其他应用中的组件来处理它 267 | * 如声明发送功能,那么拥有发送 Action 的组件都可进行响应 268 | 269 | ### 什么是显式 Intent ? 270 | 271 | * 按名称指定要启动的组件,完全限定类名 272 | * 通常会在自己的应用中使用显式 Intent 来启动组件,因为知道想要启动的 Activity 或服务的类名 273 | 274 | ### 解释一下 AsyncTask。[参考链接](http://markxu.coding.me/wiki/%E5%A4%9A%E7%BA%BF%E7%A8%8B/Android%E5%9F%BA%E7%A1%80%EF%BC%9AAsyncTask%E7%9A%84%E5%BA%94%E7%94%A8.html) 275 | 276 | * AsyncTask 是 Android 提供的一个助手类,对 Thread 和 Handler 进行了封装,方便我们在后台线程执行耗时操作,在主线程更新 UI 等 277 | * AsyncTask 有四个回调方法:onPreExecute、doInBackground、onProgressUpdate、onPostExecute。 278 | * onPreExecute 在执行后台操作之前调用,运行在主线程 279 | * doInBackground 是必须实现的一个核心方法,可以执行后台耗时操作,运行在自子线程 280 | * onProgressUpdate 在 doInBackground 方法中调用publishProgress 方法时会进行回调,可以进行后台操作状态的展示更新,运行在主线程 281 | * onPostExecute 在后台操作完成后调用,运行在主线程 282 | 283 | ### AsyncTask 的原理? 284 | 1、首先调用 AsyncTask 的构造方法,构造时对 Handler、WorkerRunnable(Callable) 和 FutureTask 进行初始化 285 | 286 | 2、然后调用 AsyncTask 的 execute 方法(可以手动设置 Executor,不设置则使用系统默认的 SerialExecutor) 287 | 288 | 3、首先判断当前 AsyncTask 状态,正在运行或者已经运行过就退出 289 | 290 | 4、调用 onPreExecute 执行准备工作 291 | 292 | 5、由 Executor 调用 FutureTask 的 run 方法,在 WorkerRunnable 中执行了 doInBackground 293 | 294 | 6、依旧是在 WorkerRunnable 中,调用 postResult,将执行结果通过 Handler 发送给主线程;调用 publishProgress 时,也是通过 Handler 将消息发送到主线程的消息队列中 295 | 296 | ### 如何理解 Android 中的广播。[Link](https://stackoverflow.com/questions/5296987/what-is-broadcastreceiver-and-when-we-use-it) 297 | 298 | * 广播接收器是一种用于响应系统范围广播通知的组件 299 | * 广播接收器不会显示用户界面,但可以创建状态栏通知,在发生广播事件时提醒用户 300 | * 广播接收器更常见的用途是作为通向其他组件的“通道”,比如开启一个服务,发起一个通知等,设计用于执行极少量的工作 301 | 302 | ### 如何理解 Android 的 LocalBroadcastManager 。[参考链接](https://developer.android.com/guide/components/fundamentals.html) 303 | 304 | * LocalBroadcastManager 为本地广播 305 | * 使用这个机制发出的广播仅在应用内部传递 306 | * 广播接收器也只能接收本应用发出的广播 307 | * 本地广播比系统全局广播更加安全、有效 308 | 309 | ### 广播和 EventBus 的区别[参考1](https://www.jianshu.com/p/fe377b82f146)、[参考2](http://www.cnblogs.com/lwbqqyumidi/p/4168017.html) 310 | 1、全局广播是重量级别的,消耗资源比较多,优点是可以跨进程; 311 | 312 | 2、本地广播会比全局广播轻量一些,不能跨进程 313 | 314 | 3、EventBus 不能跨进程,比广播更轻量 315 | 316 | 4、广播基于 Android 提供的 Binder 机制,而 EventBus 基于反射,但二者都符合观察者模式,观察者和观察目标低耦合 317 | 318 | 补充,广播的大概工作机制如下: 319 | 320 | * 广播接收者 BroadcastReceiver 通过 Binder 机制向 AMS(Activity Manager Service)进行注册; 321 | * 广播发送者通过 Binder 机制向 AMS 发送广播; 322 | * AMS 查找符合相应条件(IntentFilter/Permission 等)的 BroadcastReceiver,将广播发送到 BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中; 323 | * 消息循环执行拿到此广播,回调 BroadcastReceiver 中的 onReceive()方法。 324 | 325 | ### 什么是 JobScheduler ?[参考链接](http://blog.csdn.net/bboyfeiyu/article/details/44809395) 326 | 327 | * 当一定的条件(网络、时间、电量等)被满足时,JobScheduler 会为应用执行某项任务 328 | * 与 AlarmManager 不同,JobScheduler 的执行时间是不确定的 329 | * JobScheduler 允许同时执行多个任务 330 | 331 | ### 什么是 DDMS ?你可以用它来做什么?[参考链接](https://developer.android.com/studio/profile/monitor.html#usage) 332 | 333 | * DDMS 是 Android 应用程序调试和分析工具,可以截屏、查看进程、线程、堆栈信息、Logcat、广播状态信息、模拟呼叫、短信、位置等 334 | * 但是现在已经不推荐使用 335 | * Android Studio 3.0 以后推荐使用 Android Profile 来分析网络、内存、cpu 使用状态 336 | * 推荐使用 ADB 来发送命令、传文件、截图等 337 | 338 | ### 解释一下什么是 support libary ,以及为什么要引入 support library ?[Link](http://martiancraft.com/blog/2015/06/android-support-library/) 339 | 340 | Android 支持库提供了许多没有内置到框架中的功能。 341 | 342 | 1、向下兼容。保证使用了新系统功能的 App 可以兼容旧的系统,保证了高版本 SDK 开发的向下兼容。 343 | 344 | 2、提供了布局模式、界面元素,可以避免再做一些重复性的工作 345 | 346 | 3、支持不同形态的设备,可以通过支持库为手机、电视、手表等提供功能 347 | 348 | ### 如何理解 Android 中的 ContentProvider 。它通常用来干什么?[参考链接](https://developer.android.com/guide/components/fundamentals.html) 349 | 350 | * 内容提供器管理一组共享的应用数据 351 | * 其他应用可以通过我们应用程序的内容提供程序查询、甚至修改我们应用的数据 352 | * 我们也可以通过其他应用程序的内容提供器查询、修改该应用的数据 353 | * 也适用于读取和写入应用不共享的私有数据 354 | * 定义内容提供器需要继承自 ContentProvider,并实现相应的方法 355 | 356 | ### 什么是 Data Binding ?[Link](https://developer.android.com/topic/libraries/data-binding/index.html)、[参考链接2](https://tech.meituan.com/android_mvvm.html) 357 | 358 | 数据绑定指:将数据源和 UI 进行绑定,并使其同步 359 | 360 | 在 Android 中: 361 | 362 | * 使用 Android 官方提供的 Data binding 库,将 xml 布局和数据源进行绑定 363 | * UI 布局接收用户事件并同步更新数据源,修改数据源也可同步展示到 UI 上 364 | 365 | 数据绑定和 MVVM 的区别: 366 | 367 | * MVVM 是一种架构模式 368 | * Data Binding 是一种实现数据和 UI 绑定的框架 369 | * Data Binding 是构建 MVVM 模式的一个工具 370 | 371 | ### Android 的核心组件具体都有什么? 372 | * Activity 373 | * Service 374 | * BroadcastReceiver 375 | * ContentProvider 376 | 377 | ### 什么是 ADB ?[参考链接](https://developer.android.com/studio/command-line/adb.html?hl=zh-cn#howadbworks) 378 | 379 | ADB 是 Android Debug Bridge 的简称,即 Android 调试桥。是一个通用命令行工具,允许与模拟器或连接的 Android 设备进行通信。包括三个组件: 380 | 381 | * 客户端:运行在开发计算机上。用来发送命令,可以通过 adb 命令在命令行终端调用到客户端 382 | * 服务器:以后台进程形式运行在开发计算机上。用来管理客户端(开发计算机)和后台程序(Android 终端)之间的通信 383 | * 后台程序(adbd):以后台进程形式运行在模拟器或 Android 设备上。在设备上运行命令 384 | 385 | ### 什么是 ANR ?如何避免发生 ANR ?[参考链接1](http://droidyue.com/blog/2015/07/18/anr-in-android/index.html)、[参考链接2](http://droidyue.com/blog/2015/09/05/android-process-and-thread-schedule-nice/)、[参考链接3](http://www.jianshu.com/p/124f3b75e164) 386 | 387 | * ANR 是 Application Not Responding,即应用程序未响应 388 | * 以下情况会引起 ANR: 389 | 390 | * 主线程被 IO 操作堵塞 391 | * 主线程存在耗时计算 392 | * 主线程存在错误操作,如 Thread.sleep 或 Thread.wait 等 393 | 394 | * 以下情况会弹出 ANR 对话框 395 | 396 | * 应用在 5 秒内未响应用户的输入事件(触摸、按键) 397 | * Activity、Application 回调方法超时时间:5 秒 398 | * BroadcastReceiver 回调方法超时时间:10 秒(前台 App 10 秒,后台 App 60 秒) 399 | * Service 回调方法超时时间:20 秒 400 | 401 | * 如何避免发生 ANR: 402 | 403 | 归为一句话:将 IO 操作放在工作线程来处理,并减少其他耗时操作和错误操作 404 | 405 | * 使用 AsyncTask 处理耗时 IO 操作 406 | * 使用 Thread 或 HandlerThread 时,要调用 Process.setThreadPriority(PROCESS_THREAD_PRIORITY_BACKGROUND)设置线程优先级,降低与主线程竞争的能力,因为默认 Thread 优先级和主线程相同 407 | * 使用 Handler 处理工作线程结果,不要使用 Thread.sleep,Thread.wait 408 | * Activity 的 onCreate 和 onResume 回调中,避免耗时代码 409 | * BroadcastReceiver 的 onReceive 方法也应避免耗时代码,可以开启 IntentService 代替 410 | * 可以通过查看 `/data/anr/traces.txt` 文件定位 ANR 情况 411 | 412 | ### AndroidManifest.xml 是什么?[参考链接](https://developer.android.com/guide/topics/manifest/manifest-intro.html?hl=zh-cn) 413 | 414 | * AndroidManifest.xml 是应用清单,用来向 Android 系统提供必要的应用信息,系统必须得有这些信息才可以运行应用的代码 415 | * 具体功能如下: 416 | 417 | * 为应用的 Java 软件包命名,软件包名称充当应用的唯一标志符 418 | * 描述四大组件,以及它们可以处理的 Intent 信息,启动这些组件的条件信息等 419 | * 确定托管应用组件的进程 420 | * 声明权限,权限是用于限制对部分代码或设备上数据的访问,为了保护可能被误用以致破坏或损害用户体验的关键数据和代码 421 | * (声明应用所需的最低 Android API 级别) 422 | * (列出应用必须链接到的库) 423 | 424 | ### 解释一下 broadcast 和 intent 在 app 内传递消息的工作流程。 425 | 426 | ### Bitmap 如何优化,三级缓存的思想与逻辑 427 | 优化策略: 428 | 429 | * 对图片质量进行压缩,通过 Bitmap.compress() 430 | * 对图片尺寸进行压缩,通过设置 BitmapFactory.Options.inSampleSize 值 431 | * 对图片进行复用:内存缓存、磁盘缓存、网络获取 432 | * 及时释放 Bitmap 内存 433 | * 捕获 OutOfMemory 异常 434 | * 将图片放置于合适的 drawable 文件夹下 435 | 436 | 三级缓存: 437 | 438 | * 最近最少使用原则 439 | * 缓存策略包括缓存的添加、获取和删除操作 440 | * LruCache 内部采用一个 LinkedHashMap 以强引用的方式来存储缓存对象,提供了 get、put、remove 方法来操作缓存对象 441 | * DiskLruCache 使用 open 方法来创建,创建时指定缓存存储路径。缓存添加通过 Editor 来完成,通过图片 url 的 key 可以获取到相应的 Editor 对象,从而获取到文件输出流写入到文件系统。DiskLruCache 提供了 get 方法可以得到一个 Snapshot 对象,通过 Snapshot 对象可以得到缓存的文件输入流从而拿到缓存对象 442 | 443 | 补充: 444 | 445 | 知名的图片加载框架有 Glide、Picasso、Fresco 等 446 | 447 | 一个优秀的图片加载框架应该有如下功能: 448 | 449 | * 图片的同步加载、异步加载(向调用者提供所加载的图片) 450 | * 图片压缩 451 | * 内存缓存、磁盘缓存、网络拉取 452 | 453 | ### Android 应用有哪些不同的存储数据的方式? 454 | 455 | * 键值集(SharedPreferences) 456 | * SQL 数据库 457 | * 文件 458 | * 网络存储 459 | 460 | ### 什么是 Dalvik 虚拟机? 461 | 462 | * Dalvik 虚拟机是 Google 设计用于 Android 平台的虚拟机 463 | * 支持已转换为 .dex 格式的 Java 程序的运行,.dex 格式是专门为 Dalvik 虚拟机设计的一种压缩格式 464 | * 允许在有限内存中运行多个虚拟机实例,并且每一个都作为独立的 Linux 进程运行 465 | 466 | Dalvik 与 JVM 的关系: 467 | 468 | * Dalvik 基于寄存器,JVM 基于堆栈 469 | * Dalvik 有自己的字节码,并不使用 Java 字节码 470 | * 从 Android 2.2 开始,Dalvik 开始支持即时编译 471 | * Dalvik 会通过 Zygote 进行类的预加载和资源的预加载,完成虚拟机的初始化 472 | 473 | ### Dalvik 虚拟机模式和 ART(Android Runtime)虚拟机模式的区别。[即时编译](https://zh.wikipedia.org/wiki/%E5%8D%B3%E6%99%82%E7%B7%A8%E8%AD%AF)、[ART](https://zh.wikipedia.org/wiki/Android_Runtime) 474 | 475 | * Dalvik 采用 JIT 即时编译技术,ART 采用 Ahead-of-time AOT 预编译技术 476 | * Dalvik 虚拟机在应用程序启动时,JIT 通过进行连续的性能分析来优化程序代码的执行,在程序运行的过程中,Dalvik 虚拟机在不断的将字节码编译成机器码 477 | * ART 虚拟机在应用程序安装的过程中,ART 就已经将所有的字节码重新编译成了机器码。应用程序运行过程中无需进行实时的编译工作,只需要进行直接调用 478 | * 所以 ART 虚拟机提高了程序运行效率,减少手机电量消耗 479 | * 机器码占用空间更大,所以 ART 下应用占用内部空间更大,首次安装因需要预编译所以时间相比 Dalvik 会略长 480 | 481 | ### AsyncTask 的生命周期和(它所属的) Activity 的生命周期有什么关系?这种关系可能会导致什么样的问题? 如何避免这些问题发生?[参考链接](http://droidyue.com/blog/2014/11/08/bad-smell-of-asynctask-in-android/index.html) 482 | 483 | * AsyncTask 并不会随着 Activity 的销毁而销毁,而是会一直执行 doInBackground 方法直到方法结束 484 | * doInBackground 方法执行结束后,如果 cancel(boolean) 方法调用了,执行 onCancelled 方法;cancel(boolean) 没调用时,执行 onPostExecute 方法 485 | 486 | 导致的问题: 487 | 488 | * 当使用非静态内部 AsyncTask 类时,AsyncTask 类会持有外部类 Activity 的引用,当 Activity 销毁时,如果 AsyncTask 还在执行,会造成 Activity 对象无法回收,内存泄漏 489 | 490 | 解决方案: 491 | 492 | * 如果 doInBackground 方法内有循环操作时,应使用 isCancelled 来进行判断,避免后续无用的循环操作 493 | * 销毁 Activity 实例时,调用 AsyncTask 的 cancel 方法 494 | * 使用静态内部类,持有外部类的弱引用 495 | 496 | ### Intent filter 是用来做什么的? 497 | 498 | * Intent filter 即 Intent 过滤器,是应用清单文件中的表达式,用来指定该组件要接收的 Intent 类型 499 | * 比如给 Activity 声明了 Intent filter 后,可以使其他应用使用某一特定类型的 Intent 启动该 Activity 500 | * Intent filter 可以包括 action(必须有一个)、category 和 data 501 | 502 | ### 什么是 Sticky Intent?[Link](http://www.androidinterview.com/what-is-a-sticky-intent/) 503 | 504 | * Sticky Intent 是发送粘性广播时的消息传递对象 505 | 506 | ### 什么是 AIDL ?列举一下通过 AIDL 创建被绑定的服务(bounded service)的步骤。[参考链接](https://xuchongyang.com/2017/08/14/Binder-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/) 507 | 508 | ### Android 的权限有多少个不同的保护等级?[参考链接](https://developer.android.com/guide/topics/security/permissions.html?hl=zh-cn) 509 | 510 | * 正常权限:应用需要访问沙盒外部数据或资源,但对用户隐私或其他应用风险很小的区域。系统会自动授予应用正常权限 511 | * 危险权限:应用需要涉及用户的隐私信息或资源,或对用户数据、其他应用产生影响。用户需手动向应用授予此类权限 512 | * 特殊权限:SYSTEM_ALERT_WINDOW、WRITE_SETTINGS 等,大多数应用不该使用它们,需要的话需要发送请求用户授予的 Intent 513 | 514 | ### 在转屏时你如何保存 Activity 的状态?参考《Android 开发艺术探索》Page 8 515 | 516 | * 资源相关的系统配置发生改变时会导致 Activity 被杀死并重新创建 517 | 518 | 保存 Activity 状态的方法: 519 | 520 | * 系统会在 Activity 即将被销毁且有机会重新显示的情况下,会在 onStop 方法之前调用 onSaveInstanceState 方法 521 | * 系统会默认帮我们保存当前 Activity 的视图结构,通过查看每个 View 的 onSaveInstanceState 和 onRestoreInstanceState 方法可以查看系统会自动为 View 保存哪些状态 522 | * 我们可以手动在 onSaveInstanceState 方法中保存数据 523 | 524 | 恢复 Activity 状态: 525 | 526 | * Activity 启动时,会回调 onCreate 和 onRestoreInstanceState 方法(onStart 方法之后调用),我们可以在这两个方法里拿到 Activity 销毁时保存的数据 527 | * 在 onCreate 方法中取 Bundle 对象时,需要先判断是否为空,因为正常启动的 Activity 时系统无保存状态 528 | 529 | ### 常用布局有几种,区别 530 | LinearLayout、RelativeLayout、FrameLayout、ConstraintLayout 531 | 532 | #### 区别 533 | 1、RelativeLayout 子 View 的排列方式是基于彼此的依赖关系,所以在测量时会在横向和纵向上分别测量一次 534 | 535 | 2、LinearLayout 为线性排列,在没有设置 weight 属性时只测量一次,设置了 weight 属性也是测量两次。 536 | 537 | 3、当布局较简单时使用 LinearLayout,布局复杂时使用 RelativeLayout 以降低布局层级深度 538 | 539 | 4、帧布局会默认把所有控件摆放在布局的左上角,并覆盖在前一控件的上层,当然可以通过 layout_gravity 属性来指定对齐方式 540 | 541 | 5、ConstraintLayout 使用约束的方式来指定各个控件的位置和关系,View 的位置受到三类约束:其他 View、父容器、基准线,并且支持设置比例。可以使布局完全扁平化,性能更高 542 | 543 | ### 如何实现 XML 命名空间?[参考链接1](http://www.w3school.com.cn/xml/xml_namespaces.asp)、[参考链接2](http://blog.qiji.tech/archives/3744) 544 | 545 | * 命名空间里存放的是特定属性的集合,可以避免元素命名冲突 546 | * 为布局文件的根元素增加 xmlns 属性,即可通过不同的命名空间调用相应的属性 547 | 548 | ### View.GONE 和 View.INVISIBLE 之间的区别。 549 | 550 | * View.GONE:控件不可见,在布局中不占据空间 551 | * View.INVISIBLE:控件不可见,但在布局中还占据空间 552 | 553 | ### Bitmap 和 .9(nine-patch)图片之间有什么区别? 554 | 555 | * 点 9 图是一种可伸缩的位图,可以指定图片哪些部分可以拉伸,哪些不可以拉伸。避免图片拉伸变形,在不同分辨率下可以达到较好的适配效果。 556 | * 而 Bitmap 展示出来后在不同的屏幕分辨率下可能会被异常拉伸 557 | 558 | ### 谈谈位图池。[Link](https://blog.mindorks.com/how-to-use-bitmap-pool-in-android-56c71a55533c) 559 | 560 | ### 内存泄漏是什么,如何发现,为什么引起,如何解决([参考链接](http://xuchongyang.com/2017/10/16/Java-%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E5%AD%A6%E4%B9%A0/)) 561 | #### 是什么 562 | 存在还被引用着,但已经无用的对象,造成该对象占用的空间无法被垃圾回收器回收 563 | #### 如何发现 564 | 1、静态代码分析工具:Lint 565 | 566 | 2、严格模式:StrictMode 567 | 568 | 3、Android Profiler(Android Memory Monitor) 569 | 570 | 4、LeakCanary 571 | 572 | 5、Memory Analyzer(MAT) 573 | 574 | 6、adb shell dumpsys meminfo [PackageName] 575 | 576 | #### 为什么引起 577 | 一般都是由于长生命周期对象持有短生命周期对象引用造成: 578 | 579 | 1、非静态内部类 580 | 581 | 2、单例模式 582 | 583 | 3、静态集合类 584 | 585 | 4、资源未及时释放 586 | 587 | 5、HashSet 集合中对象的属性被修改 588 | 589 | #### 如何解决 590 | 1、改用静态内部类 591 | 592 | 2、在单例模式中使用生命周期更长的 Context 593 | 594 | 3、通过程序逻辑切段非静态内部类所持有的外部类引用 595 | 596 | 4、及时关闭各种连接(数据库、网络、cursor 等),释放资源 597 | 598 | ### Android 桌面的小部件是什么? 599 | 600 | * AppWidgetProvider 的本质是一个广播接收器,继承自 BroadcastReceiver 601 | * AppWidgetProvider 能接收 Widget 的相关广播,如 Widget 的更新、删除、开启、禁用等 602 | * 桌面小部件的加载以及更新采用的都是 RemoteViews 603 | 604 | ### 什么是 AAPT ?[参考链接](https://tech.meituan.com/mt-android-resource-obfuscation.html) 605 | 606 | * AAPT 是 Android Asset Packaging Tool 的缩写,为 SDK 自带的工具 607 | * 可以查看、创建、更新 zip 格式的文档附件(zip、jar、apk),也可以将资源文件编译成二进制文件 608 | 609 | ### 你如何排查应用崩溃的原因? 610 | 611 | * Java Crash: 612 | 613 | * 通过 Logcat 查看堆栈信息 614 | * 通过 UncaughtExceptionHandler 来记录异常日志并查看 615 | * ANR 可通过查看系统 /data/anr/ 文件夹下的 traces.txt 查看分析 616 | * 第三方 SDK 如腾讯的 Bugly,友盟等 617 | 618 | * Native Crash: 619 | 620 | * 通过 Logcat 分析 621 | * 通过第三方 SDK 622 | 623 | ### 为什么你应该避免在主线程上运行非用户界面相关的代码? 624 | 625 | * 如果主线程执行耗时操作的话,当 UI 事件发生时,让用户等待时间超过 5 秒而未处理就会出现 ANR 626 | * 主线程主要负责把 UI 事件分发给合适的 View 或 Widget 627 | 628 | ### 你是如何做应用适配的?[参考链接](http://www.cocoachina.com/android/20151030/13971.html) 629 | 1、屏幕适配 630 | 631 | * 使用 wrap_content 和 match_parent 而非硬编码尺寸,以适应各种屏幕尺寸和方向 632 | * 使用相对布局,禁用绝对布局 633 | * 使用限定符(尺寸限定符、最小宽度限定符、屏幕方向限定符) 634 | * 使用点九图 635 | * 使用密度无关像素单位 636 | * 图片文件放置到合适的 drawable 目录下 637 | 638 | 2、系统适配 639 | 640 | * 同一 API 在不同版本下拥有不同的接口方法时,动态判断系统版本号 641 | * 使用支持库提供的 API 642 | 643 | ### px、dp、sp、ppi、dpi 有什么区别,如何换算,给出公式 644 | 1、px 为像素点数 645 | 646 | 2、ppi 与 dpi 均代表像素密度,即每英寸上的像素点数 647 | 648 | 3、dp 为像素无关密度,以 160 ppi 为基准,1dp = 1px 649 | 650 | 4、sp 与 dp 类似,用于描述字体大小,以 160 ppi 为基准,当字体大小为 100% 时,1sp = 1px 651 | 652 | 换算公式(待定?): 653 | 654 | ```plain 655 | ppi = √(屏幕高度像素数² + 屏幕宽度像素数²) / 屏幕对角线英寸数 656 | 657 | px = dp * ppi / 160 658 | px = sp * ppi / 160 659 | ``` 660 | 661 | ### 如何理解 Doze 模式。如何理解应用程序待机模式(App Standby)。 662 | 663 | ### 在 Android 中,你可以使用什么来进行后台操作? 664 | 665 | * 采用 Service 666 | * 采用子线程搭配 Handler 667 | 668 | ### 什么是 ORM ?它是如何工作的? 669 | 670 | * ORM 即 Object Relational Mapping,对象关系映射 671 | * 目前的数据库是关系型数据库,ORM 的工作就是把数据库中的关系数据映射为程序中的对象 672 | 673 | ### 什么是 Loader ?[参考链接](https://developer.android.com/guide/components/loaders.html?hl=zh-cn) 674 | 675 | * Loader 即加载器,可以在 Activity 和 Fragment 中轻松实现异步加载数据 676 | * 对数据源变化进行监听,实时更新数据 677 | 678 | ### 什么是 NDK ,为什么它是有用的?[参考链接](https://developer.android.com/ndk/guides/concepts.html?hl=zh-cn#hiw) 679 | 680 | * NDK 即原生开发工具包,是一组允许在 Android 应用使用原生代码语言(C、C++)的工具 681 | 682 | 优势: 683 | 684 | * 可以从设备获取卓越的性能用于计算密集型应用,如游戏或物理模拟 685 | * 可以复用 C 或 C++ 库 686 | * 可以在平台之间移植应用 687 | 688 | ### 如何理解严格模式(StrictMode)。 [参考链接](http://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/index.html) 689 | 690 | StrictMode 是用来检测程序中违例情况的开发者工具。最常用的场景是检查主线程中文件读写和网络读写等耗时操作。 691 | 692 | StrictMode 主要检测两大问题:线程策略(ThreadPolicy)和 VM 策略(VMPolicy) 693 | 694 | 线程策略检查的内容有: 695 | 696 | * 自定义的耗时调用 697 | * 磁盘读取 698 | * 磁盘写入 699 | * 网络操作 700 | 701 | VM 策略检查内容有: 702 | 703 | * Activity 泄漏 704 | * 未关闭的 Closable 对象泄漏 705 | * Sqlite 对象泄漏 706 | * 检测实例数量 707 | 708 | 709 | ### 什么是 Lint ?它的用途是什么? 710 | 711 | * Lint 是 Android Studio 提供的一个静态代码检查工具 712 | * 帮助我们发现代码中的潜在 bug、安全、性能、国际化、辅助性、错误拼写等问题 713 | 714 | ### 什么是 SurfaceView ?[参考链接](http://blog.csdn.net/luoshengyang/article/details/8661317) 715 | 716 | * SurfaceView 拥有独立的绘图表面,即不与宿主窗口共享一个绘图表面 717 | * 由于拥有独立的绘图表面,SurfaceView 的 UI 就可以在一个独立的线程中进行绘制 718 | * 由于不占用主线程资源,SurfaceView 一方面可以实现复杂而高效的 UI,另一方面又会不会导致用户输入不能及时响应 719 | 720 | ### ListView 和 RecyclerView 有什么区别?[参考链接1](http://www.jianshu.com/p/f592f3715ae2),[参考链接2](https://www.kymjs.com/code/2016/07/10/01/) 721 | 722 | 效率上: 723 | 724 | * RecyclerView 效率较于 ListView 更高 725 | * RecyclerView 的 ViewHolder 模式更加规范 726 | 727 | 功能上: 728 | 729 | * RecyclerView 新增了 LayoutManager,布局效果更丰富,包括线性布局、网格布局、瀑布流布局等 730 | * RecyclerView 高度解耦,LayoutManager 负责布局显示,ItemDecoration 负责 item 分割线,ItemAnimator 负责 item 增删动画。 731 | 732 | 使用上: 733 | 734 | * ListView 自带空数据处理:setEmptyView 方法 735 | * ListView 自带 HeaderView、FooterView 736 | * RecyclerView 提供局部刷新方法:notifyItemChanged 737 | * RecylerView 默认封装了一些动画 738 | 739 | ### 什么是 ViewHolder 模式?为什么我们应该使用它? 740 | 741 | * 使用 ListView 或 RecyclerView 时,每个 item 在进入用户视野时会从 item 的 xml 文件中创建 view 对象,这一步可以利用 ConvertView 对创建好的布局进行缓存,直接从 ConvertView 中取即可。 742 | * 拿到 item 布局后,再 findViewById 找到 item 的子 view 的控件对象,这一步是树查找操作极其耗时。 743 | * 可以创建一个 ViewHolder 对象,将 item 子控件的实例都存放在 ViewHolder 对象中,这样就不用每次 findViewById 了,提高效率 744 | 745 | ### ListView 如何优化 746 | 1、对布局进行缓存,可以避免在滚动时每次都将布局加载一遍 747 | 748 | 2、使用 ViewHolder 对布局控件的实例进行缓存,并把 ViewHolder 对象存储在刚缓存的 View 中。可以避免每次显示时都执行 finViewById 操作 749 | 750 | 3、分批加载与分页加载相结合 751 | 752 | 4、Adapter 的 getView 方法中减少耗时逻辑或加载图片等 753 | 754 | 5、减少 item 布局的层次 755 | 756 | 6、快速滑动时不要加载图片 757 | 758 | ### 什么是 PendingIntent ? 759 | 760 | PendingIntent 可以理解为延迟执行的 Intent,在某个特定条件下才会执行该 Intent。 761 | 762 | Flags 的类型: 763 | 764 | * FLAG_ONE_SHOT:得到的 PendingIntent 只能使用一次,使用一次后自动调用 cancel 方法解除 PendingIntent 和 Intent 的关联 765 | * FLAG_NO_CREATE:当 PendingIntent 不存在时,不进行创建,直接返回 null 766 | * FLAG_CANCEL_CURRENT:当 PendingIntent 已存在时,执行 cancel 进行解除,再创建一个新的 767 | * FLAG_UPDATE_CURRENT:不存在时就进行创建,创建后每次使用会对数据进行更新 768 | * FLAG_IMMUTABLE:创建好 PendingIntent 后就保持一成不变 769 | 770 | send 方法: 771 | 772 | 调用 send 方法时会启动包装的 Intent 773 | 774 | cancel 方法: 775 | 776 | 调用 cancel 方法时会解除 PendingIntent 和被包装 Intent 之间的关联,只有创建该 PengdingIntent 的程序才有权解除 777 | 778 | ### 你能手动调用垃圾回收吗? 779 | 780 | ### 周期地更新页面的最好方式是什么? 781 | * 使用 Alarm 机制 782 | * 使用定时器 Timer 类 783 | 784 | Alarm 机制简介:参考《第一行代码》Page469 785 | 786 | * AlarmManager 设置定时任务的常用方法包括: 787 | 788 | * set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) 789 | * setRepeating(@AlarmType int type, long triggerAtMillis, 790 | long intervalMillis, PendingIntent operation) 791 | * setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) 792 | 793 | * 顾名思义,set 方法设置的是执行一次的定时任务,setRepeating 方法设置的是循环执行的定时任务 794 | * 从 Android 4.4 开始,系统会对 Alarm 唤醒对齐,所以 set 方法设置的 Alarm 任务执行可能会有一段时间的误差;而 setExact 方法可以保证唤醒时间的精准。 795 | * 第一个参数 AlarmType,有以下值可选: 796 | 797 | * ELAPSED_REALTIME:定时任务的触发时间从系统开机算起,不会唤醒 CPU 798 | * ELAPSED_REALTIME_WAKEUP:会唤醒 CPU 799 | * RTC:定时任务的出发时间从 1970 年 1 月 1 日 0 点算起,不会唤醒 CPU 800 | * RTC_WAKEUP:会唤醒 CPU 801 | * 系统开机时间可通过 SystemClock.elapsedRealtime 获得 802 | * 从 1970 年 1 月 1 日 0 点起的时间可通过 System.currentTimeMillis 获得 intervalMillis 参数代表 803 | 804 | * 第二个参数 triggerAtMillis 代表定时任务触发的时间 805 | * 第三个参数是一个 PendingIntent 代表任务触发时的执行动作 806 | * setRepeating 方法中的 intervalMillis 代表定时任务的执行时间间隔 807 | * 另外有 setInexactRepeating 方法用于设定不精确时间的循环定时任务,比 setRepeating 方法更加节能 808 | 809 | ### 有哪些类型的广播? 810 | 811 | 按执行顺序分为: 812 | 813 | * 标准广播:完全异步执行,所有广播接收器一起接到,无法被某个接收器截断 814 | * 有序广播:同步执行,优先级高的广播接收器先接收,并可以截断该条广播 815 | 816 | 按广播定义类型分为: 817 | 818 | * 系统广播:Android 系统发出的广播,比如电池电量改变、时区改变、网络状态改变等 819 | * 自定义广播:自己定义的广播 820 | 821 | 按广播范围分为: 822 | 823 | * 全局广播:可以被任何应用程序接收到的广播 824 | * 本地广播:只能在本应用程序内部传递的广播 825 | 826 | 按注册后是否可接收分为: 827 | 828 | * 粘性广播:粘性广播发送后,再进行注册的广播接收器也可接收到最后发出的一条该广播(注:粘性广播在 API 21 中已被 deprecated) 829 | * 非粘性广播:广播发送后注册的广播接收器无法接收到该广播 830 | 831 | ### 如何理解上下文(Context)。怎么使用它?[参考链接1](http://blog.csdn.net/guolin_blog/article/details/47028975)、[参考链接2](http://blog.csdn.net/lmj623565791/article/details/40481055) 832 | 833 | * Android 程序要有一个完整的 Android 工程环境,这个工程环境下有四大组件,四大组件需要有各自的上下文 834 | * 因此 Context 是维持 Android 程序中各个组件正常工作的一个重要功能类 835 | 836 | 使用中注意点: 837 | 838 | ![](https://ws1.sinaimg.cn/mw690/c14636degy1flj3pmr4dqj20s20fdab9.jpg) 839 | 840 | * getApplication 和 getApplicationContext 都可以获取到 Application 实例,并且获取的是同一个对象 841 | * getApplication 只可以在 Activity 和 Service 中调用,而任何一个 Context 实例都可以调用 getApplicationContext 842 | * 和 UI 相关的不建议使用 Application Context,单例模式下需要考虑内存泄漏问题 843 | * 上图数字1:启动 Activity 在这些类中是可以的,但是需要创建一个新的 task。一般情况不推荐。 844 | * 上图数字2:在这些类中去 layout inflate 是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。 845 | * 注:ContentProvider、BroadcastReceiver 之所以在上述表格中,是因为在其内部方法中都有一个 context 用于使用 846 | 847 | Context 的继承关系: 848 | 849 | ![](https://ws1.sinaimg.cn/mw690/c14636degy1flj37qvq3xj20fh0cp0ss.jpg) 850 | 851 | 由图可见: 852 | 853 | * ContextWrapper 是 Context 的封装类,ContextImpl 是 Context 的实现类 854 | * 我们平时需要 Context 调用的方法都在 ContextWrapper 中可以看到声明,均是调用了成员变量 mBase 的相应方法 855 | * mBase 为 ContextImpl 对象,在合适的时机由系统调用 attachBaseContext 方法传递进来 856 | 857 | ### 你知道什么是视图树(View Tree)吗?怎样优化它的深度? 858 | 859 | ### onTrimMemory() 方法是什么?[参考链接1](http://androidperformance.com/2015/07/20/Android-Performance-Memory-onTrimMemory.html)、[参考链接2](http://www.jianshu.com/p/63aafe3c12af) 860 | 1、onTrimMemory() 方法定义 861 | 862 | * Android 4.0 之后加入的一个回调,任何实现了 CompomentCallbacks2 接口的类都可以重写实现该方法 863 | * 主要作用是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉 864 | * 系统会根据不同等级的内存使用情况,调用这个回调方法,并传入相应的等级 865 | 866 | 等级分类如下: 867 | 868 | * TRIM_MEMORY_UI_HIDDEN(20):常用的一个等级,在 UI 界面被隐藏时回调。此时应释放一些 UI 占用的大块内存 869 | 870 | 程序正常运行时的回调: 871 | 872 | * TRIM_MEMORY_RUNNING_MODERATE(5):应用程序正常运行,进程不会被杀掉。但内存已经有点低了,系统可能会开始通过 LRU 缓存规则去杀死缓存进程了 873 | * TRIM_MEMORY_RUNNING_LOW(10):应用程序正常运行,进程不会被杀掉。但手机内存已经非常低了,此时应该释放不需要的资源 874 | * TRIM_MEMORY_RUNNING_CRITICAL(15):应用程序正常运行,但系统已经根据 LRU 缓存规则杀掉了大部分缓存进程。此时应尽可能释放不必要的资源,否则系统会继续杀死缓存进程,并可能开始杀死后台运行服务了 875 | 876 | 程序是缓存时的回调: 877 | 878 | * TRIM_MEMORY_BACKGROUND(40):手机内存已经很低了,系统准备开始根据 LRU 缓存规则来杀死进程了。此时我们的进程已经被加入 LRU 列表中了,此时释放一些资源可以使手机内存保持充足,从而使我们程序更长时间保存在缓存中 879 | * TRIM_MEMORY_MODERATE(60):手机内存已经很低了,此时我们的程序进程处于 LRU 缓存列表的中间位置,如果手机内存资源不能得到释放,我们的缓存进程就有可能被杀死 880 | * TRIM_MEMORY_COMPLETE(80):手机内存已经很低了,此时我们的程序进程处于 LRU 缓存列表的最边缘位置,系统将会优先考虑杀死我们的程序进程,此时应该释放所有能释放的资源 881 | 882 | 2、可以实现 onTrimMemory() 方法的组件包括: 883 | 884 | * Application 885 | * Activity 886 | * Fragment 887 | * Service 888 | * ContentProvider 889 | 890 | ### Android 应用可以使用多进程吗?怎样使用?[参考链接](http://droidyue.com/blog/2017/01/15/android-multiple-processes-summary/index.html) 891 | 892 | 如何使用: 893 | 894 | * 依赖于 android:process 属性 895 | * 如果该属性值以 `:` 开头,代表这个进程是应用私有的,无法跨应用共用 896 | * 如果该属性值以小写字母开头,代表这个进程是全局进程,可以被多个应用共用 897 | * 适用于:Application、Activity、Service、Broadcast、ContentProvider 898 | 899 | 多进程的好处: 900 | 901 | * 增加 App 可用内存 902 | * 独立于主进程,确保某些任务的执行与完成 903 | 904 | 多进程的缺点: 905 | 906 | * 数据共享问题,跨进程共享数据可以通过 Intent、Messenger、AIDL 实现 907 | * 由于每个进程可能会使用自己的 SQLOpenHelper 实例,当两个进程同时对数据库操作时,会造成 SQLite 被锁 908 | * Application.onCreate() 不必要的初始化,因为每个进程都会执行自己的 Application.onCreate() 方法 909 | 910 | ### 内存溢出(OutOfMemory)是怎么发生的?[参考链接](http://www.jianshu.com/p/1e3032c743be) 911 | 912 | 内存溢出指程序在申请内存空间时,系统没法提供足够的内存空间供其使用 913 | 914 | * 内存泄漏导致 915 | * 保存了多个占用内存过大的对象(如 bitmap)或加载单个超大图片 916 | 917 | 加载 bitmap 导致的内存溢出解决方案: 918 | 919 | * 加载多图使用软引用、弱引用 920 | * 图不再使用时,使用 Bitmap.recycle 加速回收 921 | * 使用文件缓存 922 | 923 | ### 文本样式接口(Spannable)是什么? 924 | 925 | ### 什么是过度绘制(overdraw)?[参考链接](https://jaeger.itscoder.com/android/2016/09/29/android-performance-overdraw.html)、[参考链接2](http://androidperformance.com/2014/10/20/android-performance-optimization-overdraw-1.html) 926 | 927 | * GPU 过度绘制指:屏幕上的某个像素,在同一帧时间内,被绘制了多次 928 | 929 | 产生过度绘制原因: 930 | 931 | * 多层 View 叠加绘制导致 932 | 933 | 解决过度绘制方法: 934 | 935 | * 移除默认的 Window 背景 936 | * 移除不必要的背景 937 | * 写合理且高效的布局,减少层级嵌套 938 | * 使用 ViewStub 来加载一些不常用的布局 939 | * 使用 merge 标签减少布局嵌套层次 940 | * 可复用的组件抽取出来使用 include 引入 941 | * 自定义控件进行优化(clipRect、quickReject) 942 | 943 | ### FlatBuffers 和 JSON 的区别。[Link](https://blog.mindorks.com/why-consider-flatbuffer-over-json-2e4aa8d4ed07) 944 | 945 | ### 阐述一下 Android 中的 HashMap , ArrayMap 和 SparseArray 。[Link](https://blog.mindorks.com/android-app-optimization-using-arraymap-and-sparsearray-f2b4e2e3dc47) 946 | 947 | ### 阐述一下 Looper, Handler 和 HandlerThread 。[参考链接](http://markxu.coding.me/wiki/%E5%A4%9A%E7%BA%BF%E7%A8%8B/Android%E5%9F%BA%E7%A1%80%EF%BC%9AHandler%E3%80%81Looper%E4%B8%8EMessage.html) 948 | 949 | 一句话:Looper 不断从 MessageQueue 中取出 Message 交给相应的 Handler 处理。 950 | 951 | 以上称为消息处理机制(消息循环)。 952 | 953 | * Android 中消息循环和消息队列都是针对具体线程的,除了 UI 线程之外,默认创建的工作线程是没有消息循环的 954 | * Handler 用来将消息压入消息队列以及处理消息 955 | * 普通工作线程想具有消息循环机制的话,先调用 Looper.prepare 创建消息队列、构造 Looper,再调用 Looper.loop 开启消息循环。此时该线程为 LooperThread 956 | * 在 LooperThread 中创建 Handler 对象,此时 Handler 对象会自动关联到当前线程的 Looper 对象 957 | * (构造 Handler 时如果不传 Looper,则会自动调用 mLooper = Looper.myLooper(),Handler 对象会自动关联到当前线程的 Looper 对象) 958 | * 使用 HandlerThread 可以很方便的开启一个包含 Looper 的子线程,也就是 HandlerThread 自动帮我们 Looper.prepare,Looper.loop。我们只要调用 HandlerThread.start 开启线程后,通过该线程的 Looper 对象去构建相应的 Handler 对象即可。 959 | * HandlerThread 提供了 quit 和 quitSafely 方法,可以很方便的终止线程消息队列 960 | 961 | 关于如何将 Message 压入 MessageQueue? 962 | 963 | * 调用 Handler 的 send(Message message) 方法发送一个 Message,最终会调用到 MessageQueue 的 enqueueuMessage 方法,将消息放入消息队列 964 | * 调用 Handler 的 post(Runnable r) 方法发送一个 Runnable,Runnable 先被封装为 Message 的 callback,再发送该 Message 965 | * 调用 View 的 post(Runnable r) 方法发送一个 Runnable,和上一条类似。不过调用的是 UI 线程的 Handler 发送的 Message 966 | * 调用 Activity 的 runOnUiThread(Runnable r) 方法,在 UI 线程调用时,直接执行 Runnable,在非 UI 线程调用时,调用 UI 线程的 Handler 将该 Runnable 发送出去 967 | 968 | 关于 Looper 从 MessageQueue 中取出消息后的分发? 969 | 970 | * Looper 从消息队列取出消息后 971 | * 首先调用 Handler 的 dispatchMessage 进行分发,我们可以重写此方法更改逻辑。 972 | * dispatchMessage 的默认策略如下: 973 | * Message 的 callback 不为空时,优先调用 Message 的 callback 974 | * Handler 的 mCallback 不为空时,调用 Handler 的 mCallback 975 | * 上面俩都为空时,才调用 handleMessage,也就是我们经常重写的那个方法 976 | 977 | #### Handler 的作用 978 | 1、负责消息的发送:通过调用 post 方法或 sendMessage 方法最终向消息队列中插入一条消息 979 | 980 | 2、负责消息的处理:首先判断 Message 的 callback 是否为 null,不为 null 直接执行其 run 方法;否则再判断 mCallback 是否为 null,不为 null 直接执行其 handleMessage 方法;否则调用 Handler 对象的 handleMessage 方法 981 | 982 | ### Handler 是什么,原理,使用方法 983 | #### 是什么 984 | Handler 用于线程间通信,主要负责 Android 消息机制中消息的发送和接收。 985 | 986 | #### 发送 987 | 通过向消息队列插入一条消息实现发送, 988 | 989 | 使用方法: 990 | 991 | 发送消息可以通过 Handler 对象的 sendMessage 的一系列方法和 post 一系列方法来实现。 992 | 993 | #### 接收 994 | 1、首先检查 Message 的 callback 是否为空,不为空则调用其 run 方法 995 | 996 | 2、如果为空,则检查 mCallback 是否为空,不为空调用其 handleMessage 方法。通过 mCallback 来创建 Handler 对象,可以实现不用派生 Handler 的子类就可以使用 Handler 997 | 998 | 3、如果为空,则调用 handleMessage 方法 999 | 1000 | 接收的使用方法: 1001 | 1002 | 1、派生 Handler 的子类重写 handleMessage 方法 1003 | 1004 | 2、构造 Handler 对象时传入 Handler.Callback 参数 1005 | 1006 | #### Handler 和 AsyncTask 的关系 1007 | AsyncTask 对 Handler 进行了封装,我们在使用时只需要派生 AsyncTask 的子类,并重写 onPreExecute、doInBackground、onProgressUpdate、onPostExecute 这几个方法即可。 1008 | 1009 | ### 谈谈对 RxJava 的理解 1010 | * Rxjava 是一个实现异步操作的库 1011 | * RxJava 基于事件,事件在整个过程中进行流动,流动的同时可以进行线程切换、事件转换等等 1012 | * 最常见的使用情景是:在子线程进行数据计算、网络请求,然后回到主线程展示结果 1013 | 1014 | ### LayoutInflater 的 inflate 方法的几个参数分别代表什么?[参考链接](https://xuchongyang.com/2017/09/10/LayoutInflater-%E7%9A%84-inflate-%E6%96%B9%E6%B3%95%E5%88%86%E6%9E%90/) 1015 | * 获取到 LayoutInflater 对象可以通过 `LayoutInflater.from(Context context)`方法或者 `activity.getLayoutInflater()`方法 1016 | * resouse(int)代表:欲加载的 xml 布局文件的 ID 1017 | * root(ViewGroup)代表:当 attachToRoot 为 true 时,root 为 inflate 方法加载出来的 view 的根布局;attach 为 false 时,root 仅仅为将要加载出来的 View 提供一组 LayoutParams 参数 1018 | * attachToRoot(boolean)代表:是否要将加载出来的 View 附加到上面的 root 中 1019 | * return(View)代表:inflate 方法的返回值是一个 View,为即将加载出来的视图结构的根布局。如果 attachToRoot 为 true,则返回的 View 为刚才的 root;否则返回加载出来 View 的根布局 1020 | 1021 | ### Maven 和 Gradle 的区别 1022 | #### Gradle 的优势在哪 1023 | Gradle 的功能:依赖管理、多模块构建、 1024 | 1025 | * Maven 基于 XML 配置繁琐,阅读性差,Gradle 基于 Groovy,简化了构建代码的行数,易于阅读 1026 | 1027 | 1、依赖管理方面:Gradle 支持依赖动态版本管理,解决依赖冲突机制更明确 1028 | 1029 | 2、多模块构建方面:Gradle 使用 allprojects 和 subprojects 来定义里面的配置是应用于所有项目还是子项目,更加灵活 1030 | 1031 | 3、构建周期方面:Gradle 本身与项目构建周期是解耦的,可以灵活的增删 task 1032 | 1033 | ### OkHttp、Retrofit 的区别 1034 | 1035 | ### 如何做性能优化?[参考1](https://juejin.im/post/5a0d30e151882546d71ee49e#heading-16) 1036 | #### 内存优化 1037 | 1、避免内存泄漏 1038 | 1039 | * 使用静态内部类加弱饮用的方式 1040 | * 单例模式使用生命周期更长的 Context 1041 | * 通过程序逻辑切段引用(关闭子线程、清除消息队列的所有消息) 1042 | * 静态集合中的无用对象及时移除 1043 | * 及时关闭无用的连接 1044 | 1045 | 2、图片加载进行优化,防止瞬间申请过大内存 1046 | 1047 | * 按需加载(质量压缩、尺寸压缩) 1048 | * 图片的复用(三级缓存:内存缓存、磁盘缓存、网络拉取) 1049 | * 使用合适的颜色模式 1050 | * ListView、RecyclerView 滑动时不进行加载图片 1051 | 1052 | #### UI 优化(布局优化、绘制优化) 1053 | 1、布局优化 1054 | 1055 | * 减少过度绘制 1056 | * 简单布局使用 LinearLayout,复杂布局使用 RelativeLayout,以减少布局嵌套,或者使用谷歌最新推出的 ConstraintLayout 1057 | 1058 | 2、绘制优化 1059 | 1060 | 在 View 的 onDraw 方法中: 1061 | 1062 | * 避免创建新的局部对象,onDraw 方法是实时执行的,频繁创建临时对象会造成系统不断 gc,降低效率 1063 | * 避免执行耗时操作 1064 | * 避免使用循环操作 1065 | 1066 | #### 速度优化(线程优化、网络优化) 1067 | 1068 | 1、线程优化 1069 | 1070 | * 解决 ANR 问题 1071 | * 避免在 UI 线程执行耗时操作,在子线程执行并使用 AsyncTask 或 Handler 来协助 1072 | 1073 | 2、网络优化 1074 | 1075 | 图片加载时 1076 | 1077 | * 采用谷歌的 WebP 格式的图片,可大幅节省流量 1078 | * 使用缩略图 1079 | 1080 | 其他方面 1081 | 1082 | * 对服务端返回的数据进行缓存 1083 | * 尽可能使用断点下载 1084 | * 刷新数据时进行局部刷新而不是全部刷新 1085 | 1086 | #### 电量优化 1087 | * 进行网络请求时,先判断网络状态 1088 | * 同时有 Wi-Fi 和移动网络的情况下,优先使用 Wi-Fi 网络请求 1089 | * 后台任务尽可能少的唤醒 CPU 1090 | 1091 | #### 启动优化 1092 | * Application 的创建过程减少耗时操作 1093 | * 减少布局层次,提高首次加载速度 1094 | * Activity 生命周期的回调方法中尽量减少耗时操作 1095 | 1096 | ### MVC、MVP、MVVM 区别([参考](https://tech.meituan.com/android_mvvm.html)) 1097 | 1、在 MVC 中,View 为 XML 布局文件,Controller 对应于 Activity,处理数据、业务和 UI,Model 为实体模型(数据的获取、存储、状态变化等)。在 Android 中作为 View 的 XML 功能太弱,大量 View 的逻辑写在 Activity 中,所以 Activity 同时充当了 View 和 Controller 的角色,相当臃肿 1098 | 1099 | 2、MVP 模式中,View 对应于 Activity 和 XML,Model 依然是实体模型,Presenter 负责 View 与 Model 间的交互和业务逻辑。通过一个抽象的 View 接口将 Presenter 与 View 层进行解耦,Presenter 持有该 View 接口,对该接口进行操作,而不是直接操作 View 层,视图操作和业务逻辑进行了解耦。 1100 | 1101 | 缺点: 1102 | 1103 | a、接口粒度不好控制 1104 | 1105 | b、MVP 以 UI 为驱动,更新 UI 时需要考虑线程问题和 Activity 生命周期问题 1106 | 1107 | c、View 和 Presenter 还是有一定的耦合,View 层某个元素发生变化,对应接口还是得改 1108 | 1109 | 3、MVVM 中 View 对应于 Activity 和 XML,Model 依然是实体模型,ViewModel 负责 View 和 Model 间的交互及业务逻辑。MVVM 与 MVP 的区别是 1110 | 1111 | a、MVVM 是数据驱动的,数据变化后会自动更新 UI,UI 变化也能反馈到数据层 1112 | 1113 | b、低耦合,ViewModel 只关注数据和业务逻辑,不持有 UI 控件引用,完全不和 UI 打交道,UI 控件变化时,ViewModel 也无需改变,和 MVP 相比,View 和 ViewModel 高度解耦 1114 | 1115 | c、更新 UI 时无需考虑线程问题 1116 | 1117 | d、可复用性强,一个 ViewModel 可以复用到多个 View 中 1118 | 1119 | 缺点: 1120 | 1121 | 调试比较麻烦:数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方变得不那么容易 1122 | 1123 | ### 介绍 Android 的内存管理机制 1124 | 1125 | ### 创建线程的几种方式,优缺点? 1126 | 1、继承自 Thread 1127 | 1128 | 2、Runnable 1129 | 1130 | 3、Callable 1131 | 1132 | 4、FutureTask 1133 | 1134 | ### Layout_gravity 和 gravity 区别,paddingLeft 和 Layout_marginLeft 区别 1135 | #### Layout_gravity 和 gravity 区别 1136 | * layout_gravity 用于指定控件在父布局中的对齐方式 1137 | * gravity 用于指定文字在控件中的对齐方式 1138 | 1139 | #### paddingLeft 和 Layout_marginLeft 区别 1140 | * padding 指内边距,View 内部的内容到 View 的边的距离值 1141 | * margin 指外边距,View 的边到父视图的距离值 1142 | 1143 | ### 动画的种类有什么,有啥区别 1144 | 动画包括 View 动画(补间动画)、帧动画(Drawable 动画)、属性动画。 1145 | 1146 | 1、View 动画仅能作用于 View,只改变了 View 的绘制效果,实际属性值未变 1147 | 1148 | 2、帧动画通过加载一系列的 Drawable 资源来创建动画,就像放电影 1149 | 1150 | 3、属性动画作用于 View 的属性,通过改变 View 对象的实际属性来实现动画 1151 | 1152 | ### 介绍下 Android 中的 IPC 机制([参考](http://xuchongyang.com/2017/08/14/Binder-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/)) 1153 | Android 中的 IPC 方式有: 1154 | 1155 | * AIDL(Binder 机制) 1156 | * Intent/Bundle 1157 | * 文件共享 1158 | * Messenger(底层实现为 AIDL) 1159 | * ContentProvider 1160 | * Socket 1161 | 1162 | Binder 机制: 1163 | 1164 | 1、一个进程向驱动申请成为 ServiceManager,来管理 Service 1165 | 1166 | 2、各个 Service 依次向 ServiceManager 进行注册 1167 | 1168 | 3、Client 想同 Service 进行通信,向 ServiceManager 进行请求对象 1169 | 1170 | 4、ServiceManager 给 Client 返回代理对象 1171 | 1172 | 5、Client 调用代理对象的方法,传给 ServiceManager 1173 | 1174 | 6、ServiceManager 再传递给 Service,拿到 Service 的返回值给 Client 1175 | 1176 | ### 介绍下 AIDL 的原理([参考](http://xuchongyang.com/2017/08/14/Binder-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/)) 1177 | 1、IBinder 是一个接口,代表一种跨进程传输的能力,实现了这个接口的对象就拥有了跨进程传输的能力 1178 | 1179 | 2、IInterface 接口代表了远程 Server 对象所具有的能力 1180 | 1181 | 3、静态抽象类 Stub 类为 Binder 本地对象,Stub 类实现了 IBinder、IInterface 接口,说明 Stub 类为 Binder 本地对象,且拥有 Client 需要的能力。 1182 | 1183 | 4、Stub 类的 onTransact 方法会处理 Client 传递来的方法:先通过 code 确定是哪个方法,然后从 data 中取出目标方法所需的参数,执行方法,最后向 reply 中写入值。 1184 | 1185 | 5、Stub 类有一个静态内部类 Proxy,为 Binder 代理对象。代理对象对于接口方法的处理如下:首先用 Parcel 把数据序列化,然后发起远程调用,调用 Server 端本地对象的 onTransact 方法,远程调用结束后,读取执行结果。 --------------------------------------------------------------------------------