一、算法
/**
*链表反转
*/
public ListNode listReversal(ListNode head){
if(null == head || null == head.next){
return 0;
}
ListNode pre = null;
ListNode next = null;
while(head != null){
next = head.next; //记录下一个节点
head.next = pre; //当前节点指向上一个节点
pre = head; //pre 向后移动
head = next;
}
return pre;
}
public class ListNode(){
int val;
ListNode next = null;
public ListNode(int val){
this.val = val;
}
}
/**
* 求出ViewGroup的总view和
*/
public int getViewGroupSum(View view){
int viewCount = 0;
if(null == view){
return 0;
}
if(view instanceof ViewGroup){
for(int i = 0; i < ((ViewGroup)view).getChildCount;i++){
view child = ((ViewGroup))view).getChildAt(i)
if(child instanceof ViewGroup){
viewCount+=getViewGroupSum(child);
}else {
viewCount++;
}
}
}else {
viewCount++;
}
return viewCount;
}
/**
*创建二叉树
*/
public List<TreeNode> createBinarayTree(int[] array){
List<TreeNode> list = new LinkedList<>();
for(int a : array){
list.add(new TreeNode(a));
}
for(int parentIndex = 0; parentIndex < array.length/2 - 1; parentIndex++){
list.get(parentIndex).left = list.get(parentIndex * 2 + 1); //左孩子
list.get(parentIndex).right = list.get(parentIndex * 2 + 2); //右孩子
}
int lastParentIndex = array.length/2 - 1;
list.get(lastParentIndex).left = list.get(lastParentIndex * 2 + 1); //左孩子
//如果数组长度为奇数才有右孩子
if(array.length % 2 == 1){
list.get(lastParentIndex).right = list.get(lastParentIndex * 2 + 2); //右孩子
}
return list;
}
/**
*从上向下打印出二叉树的节点,同层次从左至右打印(广度优先)
* 借助队列
*/
public ArrayList<Integer> breadFirst(TreeNode root){
LinkedList<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
TreeNode current = root;
queue.offer(root);
while(!queue.isEmpty()){
current = queue.poll();
list.add(current.val);
if(current.left != null){
queue.offer(current.left);
}
if(current.right != null){
queue.offer(current.right);
}
}
return list;
}
/**
*先序遍历(非递归)
*/
public ArrayList<Integer> preTravsal(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
ArrayList list = new ArrayList<>();
while(root != null || !stack.isEmpty()){
while(root != null){
list.add(root.val);
stack.push(root);
root = root.left;
}
if(!stack.isEmpty()){
root = stack.pop();
root = root.right;
}
}
}
/**
*中序遍历(非递归)
*/
public ArrayList<Integer> midTravsal(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
ArrayList list = new ArrayList<>();
while(root != null || !stack.isEmpty){
while(root != null){
stack.push(root);
root = root.left;
}
if(!stack.isempty()){
root = stack.pop();
list.add(root.val);
root = root.right;
}
}
}
/**
*二叉树遍历
*先序遍历(深度优先遍历)
*/
public void preorderTravsal(TreeNode root){
if(root != null){
System.out.println(root.val);
preoderTravsal(root.left);
preoderTravsal(root.right);
}
}
/**
*中序遍历
*/
public void midTravsal(TreeNode root){
if(root != null){
midTravsal(root.left);
System.out.println(root.val);
midTravsal(root.right);
}
}
/**
*后序遍历
*/
public void afterTravsal(TreeNode root){
if(root != null){
afterTravsal(root.left);
afterTravsal(root.right);
System.out.println(root.val);
}
}
/**
*二叉树定义
*/
public class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val){
this.val = val;
}
}
/**
*数组已经排序
*/
public boolean searchTarget(int[][] array,target){
for(int[] a : array){
return binarySearch(a,target);
}
}
/**
*二分查找 (要先排序)
*/
public boolean binarySearch(int[] array,int target){
int low = 0;
int high = array.length - 1;
int mid;
while(low <= high){
mid = (low+high)/2;
if(array[mid] == target){
return true;
}else if(target > array[mid]){
low = mid + 1;
}else if(target < array[mid]){
high = mid - 1;
}
}
return false;
}
/**
*递归二分查找
*/
public boolean binarySearch(int[] array,int target,int high,int low){
int mid;
if(low <= high){
mid = (high + low)/2;
if(target = array[mid]){
return true;
}else if(target > array[mid]){
return binarySearch(array,target,high,mid + 1);
}else if(target < array[mid]){
return binarySearch(array,target,mid -1,low);
}
}
return false;
}
/**
*斐波那契数列
*/
public int fibonach(int n){
if(n == 1 || n == 2){
return 1;
}
return fibonach(n-1) + fibonach(n-2);
}
/**
*单例模式
*/
public class SigleInstance{
private static volatile SigleInstance mSigleInstance;
private SigleInstance(){}
public static SigleInstance getInstance(){
if(mSigleInstance == null){
synchronized(SigleInstance.class){
if(mSigleInstance == null){
mSigleInstance = new SigleInstance();
}
}
}
return mSigleInstance;
}
}
/**
*旋转数组
*输入: [1,2,3,4,5,6,7] 和 k = 3
*输出: [5,6,7,1,2,3,4]
*/
public int[] rotate(int[] array, int k){
k = k % array.length;
//先拷贝到前面
int[] rightPart = Arrays.copyofRange(array,array.length-k,array.length);
//再进行旋转
System.arraycopy(array,0,array,k,array.length - k);
System.arraycopy(rightPart,0,array,0,k);
return array;
}
/**
*冒泡排序
*/
public int[] sort(int[] array){
for(int i = 0;i < array.length -1; i++){
for(int j = 0; j < array.length - i - 1; j++){
if(array[j] > array[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
return array;
}
/**
*多线程顺序打印abc(10组)
*/
public class MyThread implements Runnable{
public static AtomicInteger currentCount = new AtomicInteger(0);
public static final Integer MAX_COUNT = 30;
private static String chars = {"a","b","c"};
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run(){
while(currentCount.get() < MAX_COUNT){
if(this.name.equals(chars[currentCount%3])){
printAndPlusOne(this.name + "\t" + currentCount);
}
}
}
public void printAndPlusOne(String content){
System.out.println(content);
currentCount.getAndIncrement();
}
public static main(String[] args){
ThreadPoolExecutor executor = new ThreadPoolExecutor(3,3,20,TimeUnit.MINUTES,new LinkedBlockingQueue<Runable>());
executor.excute(new MyThread("a"));
executor.excute(new MyThread("b"));
executor.excute(new MyThread("c"));
executor.shutdown();
}
}
字典排序
100万个数找最大的100个数
二、 Java基础知识
1. 线程与进程的区别
- 进程可以独立运行,它是系统进行资源分配和调度的独立单位。
- 线程是进程的一个实体,是CPU调度和分派的基本单位,比进程更小的独立单位,它基本上不拥有系统资源。
区别:
- 调度:线程作为CPU调度和分派的基本单位,进程拥有系统资源的独立单位。
- 并发性:进程之间并发运行,同一个进程的多个线程也能并发执行。
- 拥有资源,进程是拥有系统资源的基本单位,线程不拥有资源,但能访问进程的资源。
2. 线程之间通信
- join
- 共享变量(volatile、AtomicInteger)
- notify/wait
- lock/condition
- 管道
- wait/notify 等待;
- CountDownLatch 并发工具
- CyclicBarrier 并发工具
3. 集合
-
Collection接口的子接口包括:Set接口和List接口
-
Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
-
Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
-
List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
List(有序,可重复)
- ArrayList
-
底层数据结构是数组,查询快,增删慢;
-
线程不安全,效率高;(Collections.synchronizedList(list)使之线程安全)
-
Array 和 ArrayList 有什么区别?什么时候该应 Array 而不是 ArrayList 呢?
-
Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
-
Array 大小是固定的,ArrayList 的大小是动态变化的。
-
ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator() 等等。
-
-
- Vector
- 底层数据结构是数组,查询快,增删慢;
- 线程安全,效率低;
- LinkedList(队列)
- 底层数据结构是链表,查询慢,增删快;
- 线程不安全,效率高;
Set(唯一)
-
HashSet (无序,唯一)
- 底层是Hashmap,实现了Set接口,把K作为值,V一直用个虚值保存;
-
TreeSet(有序,唯一)
- 底层数据结构是红黑树;
- 排序(自然排序,比较排序)
- 根据比较的返回值是否为0来确定TreeSet的唯一;
-
LinkedHashSet
-
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
-
1.由链表保证元素有序;
-
2.由哈希表保证元素唯一;
-
Hashmap。
-
数组+链表实现;
-
hashmap和hashtable
- hashmap初始容量为16,hashtable为11,hashmap扩容容量为原来的2倍,hashtable扩容容量为原来的2倍加1;
- hashmap线程不安全(多线程环境下可以采用concurrent并发包下的concurrentHashMap)hashtable线程安全;
- hashmap null可以作为键只有一个,可以有多个为null的值;hashtable不可以用null做键值;
- hash值不同;
- 继承父类不同hashtable继承dictionary,hashmap继承abstractmap;
- hashmap将contions去掉了,仅有containkey,containValue;hashtable有contains、containkey、containValue;
-
简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的:
1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
2. 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
3. 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快;
TreeMap
- 实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器。
LinkedHashMap
- 保存了记录的插入顺序,遍历慢,增删快;
4.抽象类和接口的区别
- 一个类可以实现多个接口,但是只能继承一个类;
- 抽象类用abstract修饰,接口用interface修饰;
- 含抽象方法的类是抽象类,也可以有普通方法,接口中全部是抽象类(public abstract);
- 接口和抽象类都不可以被实例化;
- 抽象类中的成员函数可以是private、protected、default、public,接口只能是public;
- 接口中变量隐式public static final,方法为public abstract;
5. http和https区别?
-
https协议需要到CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(原来网易官网是http,而网易邮箱是https。)
-
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
-
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
http的连接很简单,是无状态的。Https协议是由SSL+Http协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
6. HTTP1.0/1.1/2.0区别
- HTTP1.1默认使用长连接,可有效减少TCP的三次握手开销。
- HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。另外HTTP还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
- HTTP1.0是没有host域的,HTTP1.1才支持这个参数。
- HTTP2.0使用多路复用技术(Multiplexing),多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
“HTTP1.1在同一时间对于同一个域名的请求数量有限制,超过限制就会阻塞请求”。多路复用底层采用"增加二进制分帧层"的方法,使得不改变原来的语义、首部字段的情况下提高传输性能,降低延迟。
二进制分帧将所有传输信息分割为更小的帧,用二进制进行编码,多个请求都在同一个TCP连接上完成,可以承载任意数量的双向数据流。HTTP/2更有效的使用TCP连接,得到性能上的提升。 - HTTP/2新增首部压缩(Header Compression):采用HPACK算法 * HTTP/2新增服务端推送(Header Compression)
7.静态内部类和非静态内部类的区别
- 静态内部类不持有外部类的引用,非静态内部类持有;
- 静态内部类不依赖外部类;
- 非内部类不能声明static的方法和变量;
8. 重载和重写
重载:
- 重载是方法的重载,是一个类多态的体现(编译时多态);
- 重载是具有不同的参数个数和类型(与访问修饰符和返回值无关);
重写:
- 发生在父类与子类之间;
- 运行时的多态体现;
- 方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同;
- 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private);
- 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常;
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
9. 线程池底层原理
优点:
- 降低资源的消耗。线程本身是一种资源,创建和销毁线程会有CPU开销;创建的线程也会占用一定的内存。
- 提高任务执行的响应速度。任务执行时,可以不必等到线程创建完之后再执行。
- 提高线程的可管理性。线程不能无限制地创建,需要进行统一的分配、调优和监控。
缺点:
- 频繁的线程创建和销毁会占用更多的CPU和内存;
- 频繁的线程创建和销毁会对GC产生比较大的压力;
- 线程太多,线程切换带来的开销将不可忽视;
- 线程太少,多核CPU得不到充分利用,是一种浪费;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j6xVaMSq-1594285984958)(/Users/sun/Library/Application Support/typora-user-images/image-20200604121225588.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cZEJiOYD-1594285984959)(/Users/sun/Library/Application Support/typora-user-images/image-20200604121404392.png)]
提交任务的几种方式:
往线程池中提交任务,主要有两种方法,execute()和submit()。
- execute()用于提交不需要返回结果的任务
- submit()用于提交一个需要返回果的任务。该方法返回一个Future对象,通过调用这个对象的get()方法,我们就能获得返回结果。get()方法会一直阻塞,直到返回结果返回。另外,我们也可以使用它的重载方法get(long timeout, TimeUnit unit),这个方法也会阻塞,但是在超时时间内仍然没有返回结果时,将抛出异常TimeoutException。
关闭线程池:
- 调用线程池对象的shutdown()和shutdownNow()方法来关闭线程池。
- shutdown()会将线程池状态置为SHUTDOWN,不再接受新的任务,同时会等待线程池中已有的任务执行完成再结束。
- shutdownNow()会将线程池状态置为SHUTDOWN,对所有线程执行interrupt()操作,清空队列,并将队列中的任务返回回来。
10.Synchronized
-
对象锁
- 包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
-
类锁
- 指定synchronize修饰静态的方法或指定锁为class对象;
11. TCP三次握手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7hq2SVYi-1594285984960)(/Users/sun/Library/Application Support/typora-user-images/image-20200604121647940.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tuFBiN7H-1594285984961)(/Users/sun/Library/Application Support/typora-user-images/image-20200604121727693.png)]
12.可不可以调用父类的super方法
13.POST和GET的区别
- GET在浏览器回退时是无害的,而POST会再次提交请求。
- GET产生的URL地址可以被Bookmark,而POST不可以。
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET请求只能进行url编码,而POST支持多种编码方式。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- GET请求在URL中传送的参数是有长度限制的,而POST么有。
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中。
14. 什么是可见性
可见性:
- 一个线程修改了共享变量的值,其他线程也能看到最新修改的值 。
原子性:
- 要么全完成,要么全部完成;
15. GC
三代:新生代、老年代、永久代;
什么时候触发GC
- 程序调用System.gc时可以触发
- 系统自身来决定GC触发的时机(根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程)
16. yield()、join和sleep区别
Jion :在很多情况,主线程创建并启动子线程,如果子线程中需要进行大量的耗时计算,主线程往往早于子线程结束。这时,如果主线程想等待子线程执行结束之后再结束,比如子线程处理一个数据,主线程要取得这个数据,就要用待jion() 方法。
- sleep(long)方法在睡眠时不释放对象锁;
- join(long)方法在等待的过程中释放对象锁(两个方法,一个是一直阻塞,一个是超过一段时间之后就释放);
- Yield告诉当前正在执行的线程为线程池中具有相同优先级的线程提供机会。
17. 死锁
定义:“死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁检测:Jstack命令、JConsole工具;
在我的理解当中,死锁就是“两个任务以不合理的顺序互相争夺资源”造成,因此为了规避死锁,应用程序需要妥善处理资源获取的顺序。
另外有些时候,死锁并不会马上在应用程序中体现出来,在通常情况下,都是应用在生产环境运行了一段时间后,才开始慢慢显现出来,在实际测试过程中,由于死锁的隐蔽性,很难在测试过程中及时发现死锁的存在,而且在生产环境中,应用出现了死锁,往往都是在应用状况最糟糕的时候——在高负载情况下。因此,开发者在开发过程中要谨慎分析每个系统资源的使用情况,合理规避死锁,另外一旦出现了死锁,也可以尝试使用本文中提到的一些工具,仔细分析,总是能找到问题所在的。
18. final、finally、 finalize
- final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
- finally是异常处理语句结构的一部分,表示总是执行。
- finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。
finalize
- finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
- finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性
- 不建议用finalize方法完成“非内存资源”的清理工作,但建议用于:① 清理本地对象(通过JNI创建的对象);② 作为确保某些非内存资源(如Socket、文件等)释放的一个补充:在finalize方法中显式调用其他资源释放方法。其原因可见下文[finalize的问题]
19. 引用
- 强引用:引用变量时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
- 软引用(SoftReference):内存不足时就会回收;
- 弱引用(WeakReference):弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
- 虚引用:随时都会被回
三、Android基础知识
1.生命周期
Activity: onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy
Fragmet: onAttach->onCreate->onCreateView->onActivityCreateView->onStart->onResume->onPause->onStop->onDestroyView->onDestroy->onDetach;
2. View的绘制流程
- onMesure:根据父 view 传递的 MeasureSpec 进行计算大小。
- onLayout:根据 measure 子 View 所得到的布局大小和布局参数,将子View放在合适的位置上。
- onDraw:把 View 对象绘制到屏幕上。
3. 内存泄漏的情况
内存泄漏的主要原因是长周期引用了短周期;·
- 持有Context对象引用;
- 线程之间的异步通讯,主线程创建Looper,Looper持有handler的引用,handler中message持有activty的引用;
- 持有外部类引用;(非静态内部类,匿名内部类)
- 最小化作用域;
- 资源对象未及时关闭;
- 各类注册没有反注册;
- static滥用;
- webview泄漏;
4. handler的原理
- Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
- Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
- Message Queue(消息队列):用来存放线程放入的消息。
- 线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
5. Looper中死循环,为什么没有导致ANR?
- pipe机制,在没有消息时阻塞线程并进入休眠释放cpu资源,有消息时唤醒线程;
- Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce() 方法里;
6. SP是否线程安全,是否进程安全,有没有办法做成安全的?
-
sp存储基本数据类型+String;
-
线程不安全、进程不安全;
private static final class SharedPreferencesImpl implements SharedPreferences { ... public String getString(String key, String defValue) { synchronized (this) { String v = (String)mMap.get(key); return v != null ? v : defValue; } } ... public final class EditorImpl implements Editor { public Editor putString(String key, String value) { synchronized (this) { mModified.put(key, value); return this; } } ... } }
7. 屏幕适配
- 固定总的dp;
taskAffinity、ThreadLocal
- ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。
8. Activity保存状态的函数
- onSaveInstanceState
- 当我们的Activity处于极易被摧毁的时候,系统会调用 onSaveInstanceState() 方法,如果此时系统杀死了这个Activity的线程,这个Activity对象被destroy后,再打开这个Activity时,又会重新创建这个Activity,这个时候系统会将 onSaveInstanceState 方法中的 Bundle 对象传递给Activity的 onCreate()和 onRestoreInstanceState() 方法,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FLfrJTS-1594285984962)(/Users/sun/Library/Application Support/typora-user-images/image-20200604124125438.png)]
10.Activity 跨进程调用
-
intent隐式调用:A设置好属性,B调用;
-
知道包名调用;
12. Android进程通信方式有哪些?
- Bundle/Intent传递数据(基本类型+String)
- 文件共享;
- AIDL
- 内容提供者
- Socket
13. 事件分发机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rf6kRMXO-1594285984962)(/Users/sun/Library/Application Support/typora-user-images/image-20200604070054057.png)] 当用户点击了屏幕,首先Activity先监测到,事件先传递到Activity中,Activity通过它的dispatchTouchEvent将事件分发到phoneWindow,phonewindow则会调用superdispatchTouchEvent方法的内部是调用了其内部类DecorView的superdispatchTouchEvent,而DecorView又会调用dispatchTouchEvent去进行事件分发,如果不拦截事件,那么就会继续下传到rootview,rootview中的操作是一样的,同样在dispatchTouchEvent内部调用onInterceptTouchEvent去判断是否拦截,不拦截就会把事件分发给下一个viewgroupA,拦截就直接在onTouchEvent返回true,viewgroupA中做的判断也是一样,最后事件传递到view1,view1是最底层控件,不会有onInterceptTouchEvent,它的选择就只有处理后不处理,处理就在onTouchEvent进行处理并返回true,不处理的话事件也不会被销毁,view1这时会把事件回传,经过上述流程后回传给activity,如果Activity还不处理,那么这个事件才会被销毁;
14. Service生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8DTJBobi-1594285984963)(/Users/sun/Library/Application Support/typora-user-images/image-20200604124807990.png)]
15. Broadcast
- 有序广播
- 本地广播
- 标准广播