Skip to content

Commit c98f7ef

Browse files
Create 原理分析.md
1 parent 1df285a commit c98f7ef

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

原理分析.md

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
2+
### 原理
3+
4+
android中最重要的就是Handler机制了,简单来说Handler机制就是在一个死循环内部不断取走阻塞队列头部的Message,这个阻塞队列在主线程中是唯一的,当没有Message时,循环就阻塞,当一旦有Message时就立马被主线程取走并执行Message。
5+
6+
查看android源码可以发现在ActivityThread中main方法(main方法签名 ` public static void main(String[] args){}`,这个main方法是静态的,公有的,可以理解为应用的入口)最后执行了`Looper.loop();`,此方法内部是个死循环(for(;;)循环),所以一般情况下主线程是不会退出的,除非抛出异常。`queue.next();`就是从阻塞队列里取走头部的Message,当没有Message时主线程就会阻塞在这里,一有Message就会继续往下执行。android的view绘制,事件分发,activity启动,activity的生命周期回调等等都是一个个的Message,android会把这些Message插入到主线程中唯一的queue中,所有的消息都排队等待主线程的执行。
7+
8+
9+
ActivityThread的main方法如下:
10+
11+
```java
12+
13+
public static void main(String[] args) {
14+
15+
...
16+
Looper.prepareMainLooper();//创建主线程唯一的阻塞队列queue
17+
...
18+
ActivityThread thread = new ActivityThread();
19+
thread.attach(false);//执行初始化,往queue中添加Message等
20+
...
21+
Looper.loop();//开启死循环,挨个执行Message
22+
23+
throw new RuntimeException("Main thread loop unexpectedly exited");
24+
}
25+
26+
```
27+
28+
`Looper.loop()`关键代码如下:
29+
30+
```java
31+
32+
for (;;) {
33+
Message msg = queue.next(); // might block
34+
...
35+
msg.target.dispatchMessage(msg);//执行Message
36+
...
37+
}
38+
39+
```
40+
41+
42+
android消息机制伪代码如下:
43+
44+
```java
45+
46+
public class ActivityThread {
47+
48+
public static void main(String[]args){
49+
50+
Queue queue=new Queue();// 可以理解为一个加锁的,可以阻塞线程的ArrayList
51+
52+
queue.add(new Message(){
53+
void run(){
54+
...
55+
print("android 启动了,下一步该往queue中插入启动主Activity的Message了");
56+
Message msg=getMessage4LaunchMainActivity();
57+
queue.add(msg);
58+
}
59+
60+
});
61+
62+
for(;;){//开始死循环,for之后的代码永远也得不到执行
63+
Message msg=queue.next();
64+
65+
msg.run();
66+
67+
}
68+
69+
}
70+
71+
72+
73+
}
74+
75+
76+
```
77+
看了上面的分析相信大家对android的消息机制很清楚了。
78+
关于Handler机制更多内容可以看这
79+
[java工程实现Handler机制代码](https://android-notes.github.io/2016/12/03/5%E5%88%86%E9%92%9F%E5%AE%8C%E5%85%A8%E7%90%86%E8%A7%A3android-handler/)
80+
81+
82+
下面我们看一下Cockroach的核心代码
83+
84+
```java
85+
86+
87+
new Handler(Looper.getMainLooper()).post(new Runnable() {
88+
@Override
89+
public void run() {
90+
//主线程异常拦截
91+
while (true) {
92+
try {
93+
Looper.loop();//主线程的异常会从这里抛出
94+
} catch (Throwable e) {
95+
96+
}
97+
}
98+
}
99+
});
100+
101+
sUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
102+
//所有线程异常拦截,由于主线程的异常都被我们catch住了,所以下面的代码拦截到的都是子线程的异常
103+
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
104+
@Override
105+
public void uncaughtException(Thread t, Throwable e) {
106+
107+
}
108+
});
109+
110+
```
111+
112+
原理很简单,就是通过Handler往主线程的queue中添加一个Runnable,当主线程执行到该Runnable时,会进入我们的while死循环,如果while内部是空的就会导致代码卡在这里,最终导致ANR,但我们在while死循环中又调用了`Looper.loop()`,这就导致主线程又开始不断的读取queue中的Message并执行,这样就可以保证以后主线程的所有异常都会从我们手动调用的`Looper.loop()`处抛出,一旦抛出就会被try{}catch捕获,这样主线程就不会crash了,如果没有这个while的话那么主线程下次抛出异常时我们就又捕获不到了,这样APP就又crash了,所以我们要通过while让每次crash发生后都再次进入消息循环,while的作用仅限于每次主线程抛出异常后迫使主线程再次进入消息循环。我们可以用下面的伪代码来表示:
113+
114+
```java
115+
116+
public class ActivityThread {
117+
118+
public static void main(String[]args){
119+
120+
Queue queue=new Queue();// 可以理解为一个加锁的,可以阻塞线程的ArrayList
121+
122+
...
123+
124+
for(;;){//开始死循环,for之后的代码永远也得不到执行
125+
Message msg=queue.next();
126+
127+
//如果msg 是我们post的Runnable就会执行如下代码
128+
//我们post的Runnable中的代码
129+
while (true) {
130+
try {
131+
for(;;){//所有主线程的异常都会从msg.run()中抛出,所以我们加一个try{}catch来捕获所有主线程异常,捕获到后再次强迫进入循环,不断读取queue中消息并执行
132+
Message msg=queue.next();
133+
msg.run();
134+
}
135+
136+
} catch (Throwable e) {
137+
138+
}
139+
//否则执行其他逻辑
140+
141+
142+
}
143+
144+
}
145+
146+
147+
148+
```
149+
150+
为什么要通过new Handler.post方式而不是直接在主线程中任意位置执行
151+
` while (true) {
152+
try {
153+
Looper.loop();
154+
} catch (Throwable e) {}
155+
}`
156+
157+
这是因为该方法是个死循环,若在主线程中,比如在Activity的onCreate中执行时会导致while后面的代码得不到执行,activity的生命周期也就不能完整执行,通过Handler.post方式可以保证不影响该条消息中后面的逻辑。
158+
159+
160+
161+
162+
163+

0 commit comments

Comments
 (0)