閱讀本文大概需要 4 分鐘。
上一篇,我們瞭解到Android裏觸摸事件是如何一步一步轉入UI線程的message queue裏被執行的,這種事件是由外部事件觸發的。
我接着對小張說:其實Android裏還有一種UI queue裏的事件更爲大家熟知,你天天寫代碼都在與之打交道,你知道嗎?
小張有些丈二和尚摸不着頭腦,想了一會兒問道:能給一些提示嗎?
我提示道:它是Android系統框架層產生的事件,你在四大組件上寫的代碼均無法逃脫它的掌控!
小張雖然不是很清楚爲什麼,但是由於提示太明顯,問道:你說的難道是四大組件的生命週期?
我肯定道:沒錯,比如你天天寫Activity,在其onCreate, onResume等生命週期裏寫業務代碼,那你知道四大組件的生命週期是怎麼來的嗎?
小張懷疑到:難道它們也是handler消息機制觸發的嗎?
我說道:你沒有聽錯!就連四大組件的生命週期也遵循了這個事件驅動模型,它們均是由Android系統框架層產生相應的message扔進UI queue觸發的。
小張緊接着問道:如果這樣的話,UI線程裏必然存在一個handler在處理對應的message,以辨別這個message是哪個組件,是什麼生命週期階段。
我點了點頭,道:你說得沒錯!你在Android源碼裏見過這個handler嗎?
小張搖了搖頭:我平時業務做得比較多,對Android系統框架層的源碼看得比較少。
我聽了後說道:那你平時可得多關注關注一些底層原理類的東西了,業務是永遠在變動,而越是底層的東西越是相對穩定的,只有弄清楚基礎才能知其所以然,更好的爲業務服務。
小張聽後,連忙點頭:你說得是,回去一定惡補這塊短板。
我繼續說道:好了,Android源碼裏有個ActivityThread內部類H就是剛纔所說的handler了,你看看它的源碼,你就知道它都在幹些什麼了。
1private final class H extends Handler {
2 ...
3 public void handleMessage(Message msg) {
4 if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
5
6 switch (msg.what) {
7
8 case LAUNCH_ACTIVITY: {
9 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
10 r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo);
11 handleLaunchActivity(r, null);
12 }
13 break;
14
15 case RELAUNCH_ACTIVITY: {
16 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
17 handleRelaunchActivity(r);
18 }
19 break;
20
21 case PAUSE_ACTIVITY:
22 handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
23 maybeSnapshot();
24 break;
25
26 case PAUSE_ACTIVITY_FINISHING:
27 handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
28 break;
29
30 case STOP_ACTIVITY_SHOW:
31 handleStopActivity((IBinder)msg.obj, true, msg.arg2);
32 break;
33
34 case STOP_ACTIVITY_HIDE:
35 handleStopActivity((IBinder)msg.obj, false, msg.arg2);
36 break;
37 ...
38 case RESUME_ACTIVITY:
39 handleResumeActivity((IBinder)msg.obj, true, msg.arg1 != 0);
40 break;
41 case SEND_RESULT:
42 handleSendResult((ResultData)msg.obj);
43 break;
44 case DESTROY_ACTIVITY:
45 handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,msg.arg2, false);
46 break;
47 ...
48 case NEW_INTENT:
49 handleNewIntent((NewIntentData)msg.obj);
50 break;
51 case RECEIVER:
52 handleReceiver((ReceiverData)msg.obj);
53 maybeSnapshot();
54 break;
55 case CREATE_SERVICE:
56 handleCreateService((CreateServiceData)msg.obj);
57 break;
58 case BIND_SERVICE:
59 handleBindService((BindServiceData)msg.obj);
60 break;
61 case UNBIND_SERVICE:
62 handleUnbindService((BindServiceData)msg.obj);
63 break;
64 ...
65 case STOP_SERVICE:
66 handleStopService((IBinder)msg.obj);
67 maybeSnapshot();
68 break;
69 ...
70 }
71 if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
72 }
73 ...
74}
從上面的代碼,我們可以清晰的看到四大組件的生命週期函數調用赫然在列!
小張看了代碼後發問了:我看到Activity生命週期相應的每個msg.obj前面都用了IBinder進行了強轉,是不是說明這些message都不是App自己進程裏產生扔過來的?
我笑道:你的眼力不錯嘛。這就問到message的事件來源問題了,你說的不錯,你平時打開一個Activity都有哪些方式?
小張說道:要麼顯示,要麼隱式的startActivity。
我繼續問道:嗯,那你知道這個過程大概是怎麼樣的嗎?
小張說道:這個我知道,用戶App會通過Binder IPC通信詢問AMS(ActivityManagerService),向其索要滿足條件的Activity。
我說道:沒錯,我們知道AMS是在系統的SystemServer進程中,統管Android上的所有Activity(這又是典型解耦手段--集中管理的HUB思想)。當找到了對應的Activity之後,由於跨進程,就通過Binder IPC手段來通知用戶App進程所在的UI線程來打開對應的Activity。
小張接道:所以,AMS就把這種意圖打包進message裏,通過Binder IPC扔進UI線程的message queue中[注],當UI線程喚醒時,取出message交由H實例handler處理時,就進入了上述代碼的switch case分支,發現了是LAUNCH_ACTIVITY,就調用handleLaunchActivity處理邏輯,其中就嵌入了Activity的onCreate, onStart調用,預留給開發者重寫具體的業務邏輯。
我哈哈笑道:你都學會搶答了,進步很快啊,孺子可教也。所以你看,爲什麼平時說在UI線程的生命週期做繁重的耗時任務會導致UI卡頓或者ANR?
小張答道:由於任何UI線程的業務代碼均逃離不了組件的生命週期,而生命週期又源於UI queue中的message的處理,所以如果在任何一個生命週期做了耗時任務,這會導致queue中後面的message無法得到及時的處理,所以看起來就是有反應延時,也就是視覺上的卡頓,嚴重的會長時間得不到處理,從而導致ANR的發生。
我肯定的點了點頭:你看,底層原理一通百通,平時Android開發時要遵循的在這裏得到了真實的解答,是不是感覺理解更加深刻了?
小張興奮的說道:是啊,知道了爲什麼之後,感覺有種入木三分的感覺,以後在開發中就絕對不會犯這樣的錯誤了。
最後我又繼續補充道:其實這其中也蘊含了一種設計思想。當你想設計一套底層框架系統,而且又希望上層應用遵循你的規則,就需要預留這樣的接口或者抽象函數,以供開發者來具體實現。
小張說道:這就是依賴倒置思想吧?
我說道:是的。其實Android系統源碼裏有大量的設計模式的運用,有機會可以好好看看。
小張:嗯,看來底層原理還挺有意思的,弄懂後還能加強對上層應用的理解,真是非常有必要系統性的學習了。
[注]:這裏AMS其實並不是和UI線程直接打交道,而是通過App端的Binder線程,然後再傳遞給UI線程。如下圖:
有熱愛Android技術的同學,歡迎加 QQ羣 726464410,或者掃描QQ羣二維碼 和 微信公衆號二維碼。用詼諧的方式學習Android硬核知識點。
歡迎轉發,關注公衆號