This repository has been archived by the owner on Sep 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
android-xiao-xi-ji-zhi-yi-xiao-xi-dui-lie-de-chuang-jian-yu-xun-huan-de-kai-shi-looperyu-messagequeue.html
590 lines (497 loc) · 62 KB
/
android-xiao-xi-ji-zhi-yi-xiao-xi-dui-lie-de-chuang-jian-yu-xun-huan-de-kai-shi-looperyu-messagequeue.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue</title>
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="华科美团点评技术俱乐部 Full Atom Feed" />
<link href="/feeds/android.atom.xml" type="application/atom+xml" rel="alternate" title="华科美团点评技术俱乐部 Categories Atom Feed" />
<!-- Bootstrap Core CSS -->
<link href="/theme/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="/theme/css/clean-blog.min.css" rel="stylesheet">
<!-- Code highlight color scheme -->
<link href="/theme/css/code_blocks/darkly.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<meta name="description" content="写在前面 本文基于Android 7.1.1 (API 25)的源码分析编写...">
<meta name="author" content="Di Wu">
<meta name="tags" content="android">
<meta name="tags" content="event">
<meta property="og:locale" content="zh_CN.UTF-8">
<meta property="og:site_name" content="华科美团点评技术俱乐部">
<meta property="og:type" content="article">
<meta property="article:author" content="/author/di-wu.html">
<meta property="og:url" content="/android-xiao-xi-ji-zhi-yi-xiao-xi-dui-lie-de-chuang-jian-yu-xun-huan-de-kai-shi-looperyu-messagequeue.html">
<meta property="og:title" content="Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue">
<meta property="article:published_time" content="2017-10-22 15:32:19+08:00">
<meta property="og:description" content="写在前面 本文基于Android 7.1.1 (API 25)的源码分析编写...">
<meta property="og:image" content="//images/bg.jpg">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">华科美团点评技术俱乐部</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li><a href="/categories.html">分类</a></li>
<li><a href="/archives.html">归档</a></li>
<li><a href="/authors.html">作者</a></li>
<li><a href="/tags.html">标签</a></li>
<li><a href="/pages/about/index.html">关于</a></li>
<li><a href="/pages/friendlinks/index.html">友链</a></li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Page Header -->
<header class="intro-header" style="background-image: url('/images/bg.jpg')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="post-heading">
<h1>Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue</h1>
<span class="meta">Posted by
<a href="/author/di-wu.html">Di Wu</a>
on 2017年10月22日 周日
</span>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<!-- Post Content -->
<article>
<h2>写在前面</h2>
<p><strong>本文基于<code>Android 7.1.1 (API 25)</code>的源码分析编写</strong> </p>
<p>与之前的<a href="http://www.viseator.com/2017/09/14/android_view_event_1/">触摸事件分发机制分析</a>的文章一样,<code>Android</code>系统机制的分析中关键的一环就是事件消息的处理。之前也说过,<code>Android</code>本质上是一个事件驱动的模型,通过各式各样不断产生事件消息的来推动UI、数据的更新与对我们交互的反馈,没有事件消息的产生,就不会有直观的界面的变化,也就不会有应用丰富的功能。</p>
<p>所以<code>Android</code>的消息机制与其他过程的关系是极其紧密的,例如启动<code>Activity</code>的过程就涉及到<code>ActivityManagerService</code>与应用主进程的通信,产生的通知消息通过<code>Binder</code>机制送入应用主进程的消息队列,再由主进程的消息循环来读取这一消息来进行处理。之前触摸事件分发中也是利用了应用主进程的消息队列来读取我们的触摸事件再进行后续的分发处理。可以说消息队列在各种通信过程中无处不在。</p>
<p>消息队列的存在为异步处理提供了一个非常好的基础,有了消息队列之后,我们就可以在新的线程中处理计算、IO密集、阻塞的任务而不会影响UI的更新,在处理过程中可以通过向消息队列中放入消息来进行UI的更新操作,而发送消息的行为也避免了工作线程为了等待返回而造成的阻塞。</p>
<p>可以说,想要了解其他基于事件的过程,对主线程消息机制的了解是必不可少的基础,在触摸事件分发机制分析的文章中我对消息机制还不是很了解,所以后来发现分析中有很多描述不妥的地方,所以在对消息机制的系统学习之后我又修改完善了这部分的内容。</p>
<!--more-->
<h2>引入</h2>
<p><code>Android Studio</code>的<code>3.0</code>版本中引入了一个强大的性能分析工具:<code>Android Profiler</code>,对于它的详细介绍可以看<a href="https://developer.android.com/studio/profile/android-profiler.html">官方的文档</a>。</p>
<p>我们对一个简单的<code>HelloWorld</code>应用进行方法分析:</p>
<p><img alt="" src="/images/android_event_1.png"></p>
<p>可以看到,对于这样一个没有任务需要处理的程序,这段时间中它一直执行的是<code>nativePollOnce()</code>方法,对于这个,<code>stackoverflow</code>上就有人提了<a href="https://stackoverflow.com/questions/38818642/android-what-is-message-queue-native-poll-once-in-android">一个问题</a>。这个方法其实就是消息队列在队列中没有消息时处于等待状态执行的一个<code>Native</code>方法。</p>
<p>我们的分析就从消息队列(MessageQueue)与负责执行循环过程的<code>Looper</code>对象的创建与开始运行开始。</p>
<h2>Looper与MessageQueue的创建</h2>
<p>当一个<code>Activity</code>被创建时,<code>ActivityThread</code>的<code>main()</code>方法会被执行(关于<code>Activity</code>创建过程的内容,请参阅启动分析相关的文章):</p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Trace</span><span class="o">.</span><span class="na">traceBegin</span><span class="o">(</span><span class="n">Trace</span><span class="o">.</span><span class="na">TRACE_TAG_ACTIVITY_MANAGER</span><span class="o">,</span> <span class="s">"ActivityThreadMain"</span><span class="o">);</span>
<span class="o">...</span>
<span class="n">Looper</span><span class="o">.</span><span class="na">prepareMainLooper</span><span class="o">();</span>
<span class="o">...</span>
<span class="n">Looper</span><span class="o">.</span><span class="na">loop</span><span class="o">();</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">RuntimeException</span><span class="o">(</span><span class="s">"Main thread loop unexpectedly exited"</span><span class="o">);</span>
<span class="o">}</span>
</pre></div>
<h3>Looper的创建</h3>
<p>第5行中,调用<code>Looper</code>的<code>prepareMainLooper()</code>方法来创建<code>Looper</code>对象:</p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">prepareMainLooper</span><span class="o">()</span> <span class="o">{</span>
<span class="n">prepare</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
<span class="kd">synchronized</span> <span class="o">(</span><span class="n">Looper</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">sMainLooper</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">IllegalStateException</span><span class="o">(</span><span class="s">"The main Looper has already been prepared."</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">sMainLooper</span> <span class="o">=</span> <span class="n">myLooper</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">prepare</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">quitAllowed</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">sThreadLocal</span><span class="o">.</span><span class="na">get</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">RuntimeException</span><span class="o">(</span><span class="s">"Only one Looper may be created per thread"</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">sThreadLocal</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="k">new</span> <span class="n">Looper</span><span class="o">(</span><span class="n">quitAllowed</span><span class="o">));</span>
<span class="o">}</span>
</pre></div>
<p>这里的<code>sThreadLocal</code>对象的类型是<code>ThreadLocal<Looper></code>是一个存放在<code>TLS</code>(<a href="https://en.wikipedia.org/wiki/Thread-local_storage">Thread-local storage</a>)中的对象容器,储存在其中的对象的特点是每个线程中只有一个,并且个线程中储存的该对象不相同。</p>
<p>我们在这里新建了一个<code>Looper</code>对象并放入了<code>TLS</code>中:</p>
<div class="highlight"><pre><span></span><span class="kd">private</span> <span class="nf">Looper</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">quitAllowed</span><span class="o">)</span> <span class="o">{</span>
<span class="n">mQueue</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MessageQueue</span><span class="o">(</span><span class="n">quitAllowed</span><span class="o">);</span>
<span class="n">mThread</span> <span class="o">=</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">();</span>
<span class="o">}</span>
</pre></div>
<p><code>mThread</code>保存了当前运行<code>Looper</code>的进程信息。</p>
<p>而<code>mQueue</code>就是与<code>Looper</code>对应的<code>MessageQueue</code>。</p>
<h3>MessageQueue的创建</h3>
<div class="highlight"><pre><span></span><span class="n">MessageQueue</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">quitAllowed</span><span class="o">)</span> <span class="o">{</span>
<span class="n">mQuitAllowed</span> <span class="o">=</span> <span class="n">quitAllowed</span><span class="o">;</span>
<span class="n">mPtr</span> <span class="o">=</span> <span class="n">nativeInit</span><span class="o">();</span>
<span class="o">}</span>
</pre></div>
<p>构造函数十分简单,除了初始化<code>quitAllowed</code>标记之外,就是对<code>mPtr</code>的初始化了。</p>
<p>那么<code>mPtr</code>是什么呢?可以推测出的是,真正的<code>MessageQueue</code>的创建一定在<code>nativeInit</code>这个<code>Native</code>调用中,也就是说,我们的<code>MessageQueue</code>实际上存在于<code>Native</code>层。</p>
<p><code>android_os_MessageQueue.cpp</code>:</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">jlong</span> <span class="nf">android_os_MessageQueue_nativeInit</span><span class="p">(</span><span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="n">jclass</span> <span class="n">clazz</span><span class="p">)</span> <span class="p">{</span>
<span class="n">NativeMessageQueue</span><span class="o">*</span> <span class="n">nativeMessageQueue</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NativeMessageQueue</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">nativeMessageQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="n">jniThrowRuntimeException</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="s">"Unable to allocate native queue"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">nativeMessageQueue</span><span class="o">-></span><span class="n">incStrong</span><span class="p">(</span><span class="n">env</span><span class="p">);</span>
<span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">jlong</span><span class="o">></span><span class="p">(</span><span class="n">nativeMessageQueue</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>在<code>Native</code>层创建了一个<code>NativeMessageQueue</code>对象:</p>
<div class="highlight"><pre><span></span><span class="n">NativeMessageQueue</span><span class="o">::</span><span class="n">NativeMessageQueue</span><span class="p">()</span> <span class="o">:</span>
<span class="n">mPollEnv</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">mPollObj</span><span class="p">(</span><span class="nb">NULL</span><span class="p">),</span> <span class="n">mExceptionObj</span><span class="p">(</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mLooper</span> <span class="o">=</span> <span class="n">Looper</span><span class="o">::</span><span class="n">getForThread</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mLooper</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mLooper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Looper</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
<span class="n">Looper</span><span class="o">::</span><span class="n">setForThread</span><span class="p">(</span><span class="n">mLooper</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>这里做的事情可以和<code>Java</code>层进行对应:在<code>TLS</code>中创建了一个<code>Looper</code>对象,但这个<code>Looper</code>对象和<code>Java</code>层并不是同一个,并且他们的功能也不相同:<code>Java</code>层的<code>Looper</code>是为了处理的消息队列中的消息,<code>Native</code>中的<code>Looper</code>是为了处理注册的自定义<code>Fd</code>引起的<code>Request</code> 消息,这些消息一般来自于系统底层如触摸事件等(这个部分另开文章讲,这篇文章只关注一般的事件分发)。</p>
<p>我们来看看这个与<code>NativeMessageQueue</code>对应的<code>Native</code> <code>Looper</code>的构造:</p>
<h3>Native Looper 创建与 Epoll的初始化</h3>
<div class="highlight"><pre><span></span><span class="n">Looper</span><span class="o">::</span><span class="n">Looper</span><span class="p">(</span><span class="kt">bool</span> <span class="n">allowNonCallbacks</span><span class="p">)</span> <span class="o">:</span>
<span class="n">mAllowNonCallbacks</span><span class="p">(</span><span class="n">allowNonCallbacks</span><span class="p">),</span> <span class="n">mSendingMessage</span><span class="p">(</span><span class="nb">false</span><span class="p">),</span>
<span class="n">mPolling</span><span class="p">(</span><span class="nb">false</span><span class="p">),</span> <span class="n">mEpollFd</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="n">mEpollRebuildRequired</span><span class="p">(</span><span class="nb">false</span><span class="p">),</span>
<span class="n">mNextRequestSeq</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">mResponseIndex</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">mNextMessageUptime</span><span class="p">(</span><span class="n">LLONG_MAX</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mWakeEventFd</span> <span class="o">=</span> <span class="n">eventfd</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">EFD_NONBLOCK</span> <span class="o">|</span> <span class="n">EFD_CLOEXEC</span><span class="p">);</span>
<span class="n">LOG_ALWAYS_FATAL_IF</span><span class="p">(</span><span class="n">mWakeEventFd</span> <span class="o"><</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Could not make wake event fd: %s"</span><span class="p">,</span>
<span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
<span class="n">AutoMutex</span> <span class="nf">_l</span><span class="p">(</span><span class="n">mLock</span><span class="p">);</span>
<span class="n">rebuildEpollLocked</span><span class="p">();</span>
<span class="p">}</span>
</pre></div>
<p>这里就要涉及一些<code>Linux</code>中系统调用中<code>eventfd()</code>函数与多路I/O复用函数<code>epoll()</code>的相关知识了,这部分也是消息机制的底层核心。</p>
<p>上面的代码中第5行中使用<code>eventFd()</code>系统调用获取了一个<code>mWakeEventFd</code>作为后续<code>epoll()</code>用于唤醒的<code>File Descriper</code>(文件描述符)。这里是较之前版本有所不同的地方,网上找到的大部分分析文章中的这个地方还是使用的之前使用的管道机制,也就是通过<code>pipe()</code>系统调用来创建一对<code>Fd</code>,再利用这对<code>Fd</code>来进行监听唤醒操作。相比于管道,<code>Linux</code>在内核版本2.6.22引入的<code>eventFd</code>在解决这种简单的监听操作中的开销比较小,并且更加轻量。</p>
<p>我们现在有了一个<code>eventFd</code>对象的<code>Fd</code>,下面我们进入第10行的<code>rebuildEpollLocked()</code>调用:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">Looper</span><span class="o">::</span><span class="n">rebuildEpollLocked</span><span class="p">()</span> <span class="p">{</span>
<span class="p">...</span>
<span class="n">mEpollFd</span> <span class="o">=</span> <span class="n">epoll_create</span><span class="p">(</span><span class="n">EPOLL_SIZE_HINT</span><span class="p">);</span>
<span class="n">LOG_ALWAYS_FATAL_IF</span><span class="p">(</span><span class="n">mEpollFd</span> <span class="o"><</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Could not create epoll instance: %s"</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
<span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">eventItem</span><span class="p">;</span>
<span class="n">memset</span><span class="p">(</span><span class="o">&</span> <span class="n">eventItem</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">epoll_event</span><span class="p">));</span> <span class="c1">// zero out unused members of data field union</span>
<span class="n">eventItem</span><span class="p">.</span><span class="n">events</span> <span class="o">=</span> <span class="n">EPOLLIN</span><span class="p">;</span>
<span class="n">eventItem</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">mWakeEventFd</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">epoll_ctl</span><span class="p">(</span><span class="n">mEpollFd</span><span class="p">,</span> <span class="n">EPOLL_CTL_ADD</span><span class="p">,</span> <span class="n">mWakeEventFd</span><span class="p">,</span> <span class="o">&</span> <span class="n">eventItem</span><span class="p">);</span>
<span class="n">LOG_ALWAYS_FATAL_IF</span><span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Could not add wake event fd to epoll instance: %s"</span><span class="p">,</span>
<span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">mRequests</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="n">Request</span><span class="o">&</span> <span class="n">request</span> <span class="o">=</span> <span class="n">mRequests</span><span class="p">.</span><span class="n">valueAt</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">eventItem</span><span class="p">;</span>
<span class="n">request</span><span class="p">.</span><span class="n">initEventItem</span><span class="p">(</span><span class="o">&</span><span class="n">eventItem</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">epollResult</span> <span class="o">=</span> <span class="n">epoll_ctl</span><span class="p">(</span><span class="n">mEpollFd</span><span class="p">,</span> <span class="n">EPOLL_CTL_ADD</span><span class="p">,</span> <span class="n">request</span><span class="p">.</span><span class="n">fd</span><span class="p">,</span> <span class="o">&</span> <span class="n">eventItem</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">epollResult</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">ALOGE</span><span class="p">(</span><span class="s">"Error adding epoll events for fd %d while rebuilding epoll set: %s"</span><span class="p">,</span>
<span class="n">request</span><span class="p">.</span><span class="n">fd</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>第3行中,进行了系统调用<code>epoll_create()</code>初始化了一个<code>epoll</code>实例。之后的6-9行创建了<code>epoll</code>注册需要使用的<code>eventItem</code>并设置了<code>events</code>属性与<code>fd</code>域。</p>
<p>在第10行,进行了系统调用<code>epoll_ctl()</code>来将之前创建的<code>mWakeEventFd</code>与<code>eventItem</code>注册到<code>epoll</code>。那么这些步骤的目的是什么呢?</p>
<p>简单地说,<code>epoll</code>这个系统提供的组件允许我们对多个文件描述符(<code>fd</code>)进行监听,注册监听的<code>fd</code>后,可以调用<code>epoll_wait()</code>函数,当<code>fd</code>所指向的对象的数据可用时,<code>epoll_wait()</code>函数就会返回,同时以<code>events</code>的形式返回发生改变的<code>fd</code>对应的<code>eventItem</code>。</p>
<p>借助这个功能,我们就可以实现在没有事件的时候让线程阻塞,当新的事件来临的时候让线程解除阻塞并唤醒。</p>
<p>到这里你可能会想,这样的功能使用一个标志量,不断地查询这个标志量,当标志量发生变化的时候唤醒不也可以实现相同的功能吗?为什么要使用这么复杂的机制呢?这是因为<code>Looper</code>同时为我们提供了<code>addFd()</code>函数让我们可以设置自定义的<code>fd</code>与对应的<code>event</code>,然后在<code>Native Looper</code>中对自定义的<code>fd</code>发生改变的事件进行处理(上面代码中后面的部分就是在处理这部分注册)。之前文章讲过的触摸事件分发就是这样做的。(再次说明这一剖分的内容另外一篇文章讲,本篇只涉及一般的消息处理机制)</p>
<p>现在,注册<code>epoll</code>的过程已经完成,<code>Native Looper</code>的初始化也到此结束。</p>
<p>现在我们回到<code>nativeInit</code>调用,它的返回值赋给了<code>Java</code>层<code>MessageQueue</code>的<code>mPtr</code>域:</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">jlong</span> <span class="nf">android_os_MessageQueue_nativeInit</span><span class="p">(</span><span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="n">jclass</span> <span class="n">clazz</span><span class="p">)</span> <span class="p">{</span>
<span class="n">NativeMessageQueue</span><span class="o">*</span> <span class="n">nativeMessageQueue</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NativeMessageQueue</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">nativeMessageQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="n">jniThrowRuntimeException</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="s">"Unable to allocate native queue"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">nativeMessageQueue</span><span class="o">-></span><span class="n">incStrong</span><span class="p">(</span><span class="n">env</span><span class="p">);</span>
<span class="k">return</span> <span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">jlong</span><span class="o">></span><span class="p">(</span><span class="n">nativeMessageQueue</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>第9行就将创建的<code>NativeMessageQueue</code>对象的地址转换为一个<code>Java long</code>类型返回,之后调用<code>Native</code>方法的时候就会传入这个参数来找到这个<code>MessageQueue</code>。</p>
<p>用一张图来梳理这个过程:</p>
<p><img alt="" src="/images/android_event_2.png"></p>
<h2>消息循环</h2>
<h3>loop()</h3>
<p>初始化过程结束后,我们回到<code>ActivityThread</code>的<code>main()</code>函数:</p>
<div class="highlight"><pre><span></span><span class="n">Looper</span><span class="o">.</span><span class="na">loop</span><span class="o">();</span>
</pre></div>
<p>调用了<code>Looper</code>的<code>loop()</code>函数开始消息循环。</p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">loop</span><span class="o">()</span> <span class="o">{</span>
<span class="kd">final</span> <span class="n">Looper</span> <span class="n">me</span> <span class="o">=</span> <span class="n">myLooper</span><span class="o">();</span>
<span class="o">...</span>
<span class="kd">final</span> <span class="n">MessageQueue</span> <span class="n">queue</span> <span class="o">=</span> <span class="n">me</span><span class="o">.</span><span class="na">mQueue</span><span class="o">;</span>
<span class="o">...</span>
<span class="k">for</span> <span class="o">(;;)</span> <span class="o">{</span>
<span class="n">Message</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="na">next</span><span class="o">();</span> <span class="c1">// might block</span>
<span class="k">if</span> <span class="o">(</span><span class="n">msg</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// No message indicates that the message queue is quitting.</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">...</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">msg</span><span class="o">.</span><span class="na">target</span><span class="o">.</span><span class="na">dispatchMessage</span><span class="o">(</span><span class="n">msg</span><span class="o">);</span>
<span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">traceTag</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Trace</span><span class="o">.</span><span class="na">traceEnd</span><span class="o">(</span><span class="n">traceTag</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">...</span>
<span class="n">msg</span><span class="o">.</span><span class="na">recycleUnchecked</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
<p>省略掉一些<code>log</code>的代码之后,我们看到第7行开始了一个无限循环,循环的第一步就是从<code>MessageQueue</code>里面获取一条<code>Message</code>,后面有一个注释告诉我们这个调用可能会阻塞。我们先不管这个调用具体情况,假设我们从这个调用中返回,我们先看后面的处理过程。</p>
<p>首先检查获取到的<code>msg</code>是否为<code>null</code>,如果为<code>null</code>,那么将会直接退出<code>loop()</code>函数,对于<code>Activity</code>的主线程来说,这个情况只会发生在应用退出的时候。</p>
<p>下面就直接调用了<code>Message</code>的<code>target</code>的<code>dispatchMessage()</code>函数,在使用<code>Handler</code>来发送消息的时候,这个<code>target</code>指的就是<code>Handler</code>本身,后面会看到这个过程。</p>
<h3>MessageQueue next()</h3>
<p>这个函数过程比较长, 我们分开来分析。</p>
<div class="highlight"><pre><span></span><span class="n">Message</span> <span class="nf">next</span><span class="o">()</span> <span class="o">{</span>
<span class="kd">final</span> <span class="kt">long</span> <span class="n">ptr</span> <span class="o">=</span> <span class="n">mPtr</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">ptr</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="kt">int</span> <span class="n">pendingIdleHandlerCount</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span> <span class="c1">// -1 only during first iteration</span>
<span class="kt">int</span> <span class="n">nextPollTimeoutMillis</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">for</span> <span class="o">(;;)</span> <span class="o">{</span>
<span class="o">...</span>
<span class="n">nativePollOnce</span><span class="o">(</span><span class="n">ptr</span><span class="o">,</span> <span class="n">nextPollTimeoutMillis</span><span class="o">);</span>
</pre></div>
<p>第一部分是变量的初始化,如果<code>MessageQueue</code>的<code>mPtr</code>为0的话,说明<code>NativeMessageQueue</code>没有正确初始化,返回<code>null</code>结束消息循环。</p>
<p>下面定义了两个变量,第一个<code>pendingIdleHandlerCount</code>初始化为-1,它表示的是将要执行的空闲<code>Handler</code>数量,之后会用到。</p>
<p>第二个<code>nextPollTimeoutMillis</code>就是距下一条消息被处理需要等待的时间。</p>
<p>下面又进入了一个无限循环,注意第11行,我们看到了熟悉的调用,它是引入中讲的在没有事件处理的时候不断执行的函数。我们可以猜测,等待的过程就是发生在这个函数中的。</p>
<p>我们同样先看下面的处理,现在只要知道这个函数<strong>会造成阻塞,当有新的<code>Message</code>或者达到超时时间时才会返回</strong>,这点非常重要。</p>
<p>下面的过程:</p>
<div class="highlight"><pre><span></span><span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">final</span> <span class="kt">long</span> <span class="n">now</span> <span class="o">=</span> <span class="n">SystemClock</span><span class="o">.</span><span class="na">uptimeMillis</span><span class="o">();</span> <span class="c1">// 获取当前时刻</span>
<span class="n">Message</span> <span class="n">prevMsg</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">Message</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">mMessages</span><span class="o">;</span> <span class="c1">// Message是一个链表的结构,而mMessages相当于"头指针"</span>
<span class="k">if</span> <span class="o">(</span><span class="n">msg</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&&</span> <span class="n">msg</span><span class="o">.</span><span class="na">target</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// 当message的target为null的时候,被认为是一个特殊的message,我们应当跳过这类message</span>
<span class="k">do</span> <span class="o">{</span>
<span class="n">prevMsg</span> <span class="o">=</span> <span class="n">msg</span><span class="o">;</span>
<span class="n">msg</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
<span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="n">msg</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&&</span> <span class="o">!</span><span class="n">msg</span><span class="o">.</span><span class="na">isAsynchronous</span><span class="o">());</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">msg</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">now</span> <span class="o"><</span> <span class="n">msg</span><span class="o">.</span><span class="na">when</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// 当时间还没到message的执行时间时,更新nextPollTimeoutMillis</span>
<span class="n">nextPollTimeoutMillis</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="n">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="na">when</span> <span class="o">-</span> <span class="n">now</span><span class="o">,</span> <span class="n">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="c1">// message到时间,需要被处理</span>
<span class="n">mBlocked</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="c1">// 取消阻塞状态标记</span>
<span class="c1">// 从链表中取出表头的message</span>
<span class="k">if</span> <span class="o">(</span><span class="n">prevMsg</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">prevMsg</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">mMessages</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">msg</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">DEBUG</span><span class="o">)</span> <span class="n">Log</span><span class="o">.</span><span class="na">v</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"Returning message: "</span> <span class="o">+</span> <span class="n">msg</span><span class="o">);</span>
<span class="n">msg</span><span class="o">.</span><span class="na">markInUse</span><span class="o">();</span> <span class="c1">// 标记使用状态</span>
<span class="k">return</span> <span class="n">msg</span><span class="o">;</span> <span class="c1">// 返回该message</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="c1">// msg == null,没有消息</span>
<span class="n">nextPollTimeoutMillis</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span> <span class="c1">// 如果该值为-1,nativePullOnce()将会无限执行直到有新的消息通知</span>
<span class="o">}</span>
<span class="c1">// 处理退出消息循环的请求,返回null退出消息循环</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mQuitting</span><span class="o">)</span> <span class="o">{</span>
<span class="n">dispose</span><span class="o">();</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
</pre></div>
<p>这个过程比较简单,需要注意的就是<code>Message</code>的链表结构,每次取首元素来进行处理。</p>
<div class="highlight"><pre><span></span><span class="c1">// 只会在第一次没有消息的时候执行,检查mIdleHandlers中注册的IdleHandler</span>
<span class="k">if</span> <span class="o">(</span><span class="n">pendingIdleHandlerCount</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">&&</span> <span class="o">(</span><span class="n">mMessages</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">now</span> <span class="o"><</span> <span class="n">mMessages</span><span class="o">.</span><span class="na">when</span><span class="o">))</span> <span class="o">{</span>
<span class="n">pendingIdleHandlerCount</span> <span class="o">=</span> <span class="n">mIdleHandlers</span><span class="o">.</span><span class="na">size</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// 如果没有注册pendingIdleHandler,继续保持阻塞状态</span>
<span class="k">if</span> <span class="o">(</span><span class="n">pendingIdleHandlerCount</span> <span class="o"><=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">mBlocked</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// 初始化mPendingHandlers</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mPendingIdleHandlers</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">mPendingIdleHandlers</span> <span class="o">=</span> <span class="k">new</span> <span class="n">IdleHandler</span><span class="o">[</span><span class="n">Math</span><span class="o">.</span><span class="na">max</span><span class="o">(</span><span class="n">pendingIdleHandlerCount</span><span class="o">,</span> <span class="mi">4</span><span class="o">)];</span>
<span class="o">}</span>
<span class="n">mPendingIdleHandlers</span> <span class="o">=</span> <span class="n">mIdleHandlers</span><span class="o">.</span><span class="na">toArray</span><span class="o">(</span><span class="n">mPendingIdleHandlers</span><span class="o">);</span>
<span class="c1">// 执行pendingIdleHandler</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">pendingIdleHandlerCount</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="kd">final</span> <span class="n">IdleHandler</span> <span class="n">idler</span> <span class="o">=</span> <span class="n">mPendingIdleHandlers</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
<span class="n">mPendingIdleHandlers</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kt">boolean</span> <span class="n">keep</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// 实质上是执行queueIdle()方法</span>
<span class="n">keep</span> <span class="o">=</span> <span class="n">idler</span><span class="o">.</span><span class="na">queueIdle</span><span class="o">();</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Throwable</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Log</span><span class="o">.</span><span class="na">wtf</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"IdleHandler threw exception"</span><span class="o">,</span> <span class="n">t</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">keep</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// 如果返回是false,则移除这个IdleHandler,不会再执行</span>
<span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span> <span class="n">mIdleHandlers</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">idler</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">// 重置pendingIdleHandler计数</span>
<span class="n">pendingIdleHandlerCount</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="c1">// 由于执行IdleHandler过程中可能已经有新的消息到来,故设置超时为0,直接检查新的消息</span>
<span class="n">nextPollTimeoutMillis</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
</pre></div>
<p>这里的<code>mIdleHandlers</code>中注册了一些需要在没有消息处理时进行的任务,在处理这些任务的过程中使用了<code>pendingIdleHandler</code>作为临时容器。这个过程就是去执行这些<code>IdleHandler</code>的过程。</p>
<p>现在我们看完了返回消息的全过程,其中只有一环没有解决了:<code>nativePollOnce</code></p>
<h3>NativePollOnce()</h3>
<p>调用的是<code>Native</code>层<code>android_os_MessageQueue.cpp</code>下的函数:</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">android_os_MessageQueue_nativePollOnce</span><span class="p">(</span><span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">obj</span><span class="p">,</span>
<span class="n">jlong</span> <span class="n">ptr</span><span class="p">,</span> <span class="n">jint</span> <span class="n">timeoutMillis</span><span class="p">)</span> <span class="p">{</span>
<span class="n">NativeMessageQueue</span><span class="o">*</span> <span class="n">nativeMessageQueue</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o"><</span><span class="n">NativeMessageQueue</span><span class="o">*></span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span>
<span class="n">nativeMessageQueue</span><span class="o">-></span><span class="n">pollOnce</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">timeoutMillis</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>根据传入的地址,找到了之前新建的<code>NativeMessageQueue</code>对象,调用它的<code>pollOnce()</code>方法(注意参数中的<code>timeoutMillis</code>):</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">NativeMessageQueue</span><span class="o">::</span><span class="n">pollOnce</span><span class="p">(</span><span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">pollObj</span><span class="p">,</span> <span class="kt">int</span> <span class="n">timeoutMillis</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mPollEnv</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span>
<span class="n">mPollObj</span> <span class="o">=</span> <span class="n">pollObj</span><span class="p">;</span>
<span class="n">mLooper</span><span class="o">-></span><span class="n">pollOnce</span><span class="p">(</span><span class="n">timeoutMillis</span><span class="p">);</span>
<span class="n">mPollObj</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">mPollEnv</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mExceptionObj</span><span class="p">)</span> <span class="p">{</span>
<span class="n">env</span><span class="o">-></span><span class="n">Throw</span><span class="p">(</span><span class="n">mExceptionObj</span><span class="p">);</span>
<span class="n">env</span><span class="o">-></span><span class="n">DeleteLocalRef</span><span class="p">(</span><span class="n">mExceptionObj</span><span class="p">);</span>
<span class="n">mExceptionObj</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>其实调用的是保存的<code>NativeLooper</code>的<code>pollOnce()</code>方法(注意参数中的<code>timeoutMillis</code>)。</p>
<p>现在我将<code>NativeLooper</code>中关于<code>Native</code>事件循环的代码全部忽略,只分析与前面这个过程有关的部分:</p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="n">Looper</span><span class="o">::</span><span class="n">pollOnce</span><span class="p">(</span><span class="kt">int</span> <span class="n">timeoutMillis</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">outFd</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">outEvents</span><span class="p">,</span> <span class="kt">void</span><span class="o">**</span> <span class="n">outData</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">pollInner</span><span class="p">(</span><span class="n">timeoutMillis</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>调用了<code>pollInner()</code>方法(注意参数中的<code>timeoutMillis</code>),分为两部分分析:</p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="n">Looper</span><span class="o">::</span><span class="n">pollInner</span><span class="p">(</span><span class="kt">int</span> <span class="n">timeoutMillis</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="n">mPolling</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">eventItems</span><span class="p">[</span><span class="n">EPOLL_MAX_EVENTS</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">eventCount</span> <span class="o">=</span> <span class="n">epoll_wait</span><span class="p">(</span><span class="n">mEpollFd</span><span class="p">,</span> <span class="n">eventItems</span><span class="p">,</span> <span class="n">EPOLL_MAX_EVENTS</span><span class="p">,</span> <span class="n">timeoutMillis</span><span class="p">);</span>
<span class="n">mPolling</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">...</span>
<span class="c1">// 检查轮询错误</span>
<span class="k">if</span> <span class="p">(</span><span class="n">eventCount</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">Done</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ALOGW</span><span class="p">(</span><span class="s">"Poll failed with an unexpected error: %s"</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">POLL_ERROR</span><span class="p">;</span>
<span class="k">goto</span> <span class="n">Done</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 检查轮询超时</span>
<span class="k">if</span> <span class="p">(</span><span class="n">eventCount</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#if DEBUG_POLL_AND_WAKE</span>
<span class="n">ALOGD</span><span class="p">(</span><span class="s">"%p ~ pollOnce - timeout"</span><span class="p">,</span> <span class="k">this</span><span class="p">);</span>
<span class="cp">#endif</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">POLL_TIMEOUT</span><span class="p">;</span>
<span class="k">goto</span> <span class="n">Done</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 处理事件</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">eventCount</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">eventItems</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">data</span><span class="p">.</span><span class="n">fd</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">epollEvents</span> <span class="o">=</span> <span class="n">eventItems</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">events</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="n">mWakeEventFd</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">epollEvents</span> <span class="o">&</span> <span class="n">EPOLLIN</span><span class="p">)</span> <span class="p">{</span>
<span class="n">awoken</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">ALOGW</span><span class="p">(</span><span class="s">"Ignoring unexpected epoll events 0x%x on wake event fd."</span><span class="p">,</span> <span class="n">epollEvents</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>第6行就是事情的关键,我们执行了系统<code>epoll_wait()</code>调用(对我们之前创建的<code>mEpollFd</code> <code>epoll</code>实例),这是一个阻塞调用,当注册的<code>Fd</code><strong>有新内容或者到达超时时间时才会返回</strong>。我们还记得前面我们创建了<code>mWakeEventFd</code>和<code>eventItem</code>并把它注册到了<code>mEpollFd</code>中。这样,只要<code>mWakeEventFd</code>中有了新的内容,这行调用就会返回,解除阻塞。</p>
<p>现在我们可以推测,当有新消息到来时,正是以向<code>mWakeEvendFd</code>中写入内容的方式来使<code>nativePollOnce()</code>调用返回,达到了通知消息循环继续处理的目的。</p>
<p>如果没有新的消息呢?我们一步步传进来的<code>timeoutMillis</code>就作为了<code>epoll_wait()</code>的超时参数,一旦到达这个时间,<code>epoll_wait()</code>函数就会返回,这达到了我们等待一段时间再去执行下一条消息的目的。</p>
<p>如果超时,20行检测出超时,跳转到<code>Done</code>。</p>
<p>如果因<code>fd</code>触发而返回,会进入28行的事件处理过程,这个过程依据拿到的<code>eventItem</code>对象,检查<code>fd</code>与<code>events</code>标志,如果是我们之前设置的用于唤醒的<code>mWakeEventFd</code>,调用<code>awaken()</code>:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">Looper</span><span class="o">::</span><span class="n">awoken</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">uint64_t</span> <span class="n">counter</span><span class="p">;</span>
<span class="n">TEMP_FAILURE_RETRY</span><span class="p">(</span><span class="n">read</span><span class="p">(</span><span class="n">mWakeEventFd</span><span class="p">,</span> <span class="o">&</span><span class="n">counter</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">)));</span>
<span class="p">}</span>
</pre></div>
<p>做的事情非常简单,通过<code>read()</code>读取并清零<code>fd</code>中的数据。</p>
<p>你可能会想,为什么什么事情都没有做呢?因为这个<code>mWakeEventFd</code>存在的唯一目的就是解除阻塞,现在这个目的已经达到了,我们只要重置它以便下一次使用就可以了。</p>
<p><code>Done</code>标号以后的代码与我们的过程无关,执行了自定义<code>fd</code>消息处理相关的内容。最后将<code>result</code>返回:</p>
<div class="highlight"><pre><span></span> <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</pre></div>
<p>现在我们可以重新看待<code>nativePollOnce()</code>函数,再次强调,它的作用是<strong>阻塞,当有新的消息或达到超时后返回</strong>。而这个核心的特性,完全是利用系统提供的<code>epoll</code>机制实现的。</p>
<p>现在整个<code>Java</code>消息循环的处理过程已经看完了,下面我们来结合常用的<code>Handler</code>来讲解向消息队列中投入新的消息的过程。</p>
<blockquote>
<p><a href="http://www.viseator.com/2017/10/24/android_event_2/">Android 消息机制(二)Handler对消息机制的使用</a></p>
</blockquote>
</article>
<div class="tags">
<p>tags: <a href="/tag/android.html">android</a>, <a href="/tag/event.html">event</a></p>
</div>
<hr>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
<li>
<a href="https://github.com/HUSTMeituanClub">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a href="mailto:@hustmeituan.club">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-envelope fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">
Blog powered by <a href="http://getpelican.com">Pelican</a>,
which takes great advantage of <a href="http://python.org">Python</a>.
</p> </div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="/theme/js/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="/theme/js/bootstrap.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="/theme/js/clean-blog.min.js"></script>
</body>
</html>