
前言
學 Android 有一段時間了,一直都只顧着學新的東西,最近發現很多平常用的少的東西竟讓都忘了,趁着這兩天,打算把有關 Activity 的內容以問題的形式梳理出來,也供大家查缺補漏。
本文中,我將一改往日寫博客的習慣,全文用 XMind 將所有知識點以思維導圖的形式呈現,歡迎大家食用~~
文章目錄

方便大家學習,我在 GitHub 上建立個 倉庫
-
倉庫內容與博客同步更新。由於我在
稀土掘金簡書CSDN博客園等站點,都有新內容發布。所以大家可以直接關注該倉庫,以免錯過精彩內容!
神圖
- 在開始之前,先讓我們看看
Android的activity到底都有哪些東西? - 借一張網上很火的圖帶你了解
Activity

1. 生命周期
- 先貼一張聞名遐邇的圖
- 我們生命周期先看看具體有哪些方法回調,在逐一攻破:

1.1 Dialog 彈出時

- 如果是單純是創建的
dialog,Activity並不會執行生命周期的方法 - 但是如果是跳轉到一個不是全屏的
Activity的話, 當然就是按照正常的生命周期來執行了 - 即
onPasue()->onPause()( 不會執行原Activity的onStop(), 否則上個頁面就不顯示了 )
1.2 橫豎屏切換時

- 不設置
Activity的android:configChanges時,切屏會重新調用各個生命周期,切橫屏時會執行一次,切豎屏時會執行兩次 - 設置
Activity的android:configChanges="orientation"時,切屏還是會重新調用各個生命周期,切橫、豎屏時只會執行一次 - 設置
Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命周期,只會執行onConfigurationChanged方法 - 注意:還有一點,非常重要,一個
Android的變更細節!當API >12時,需要加入screenSize屬性,否則屏幕切換時即使你設置了orientation系統也會重建Activity!
1.3 不同場景下 Activity 生命周期的變化過程

- 啟動
Activity:onCreate()--->onStart()--->onResume(),Activity進入運行狀態。 - 鎖屏時會執行
onPause()和onStop(), 而開屏時則應該執行onStart()onResume()

Activity退居后台: 當前Activity轉到新的Activity界面或按Home鍵回到主屏:onPause()--->onStop(),進入停滯狀態。Activity返回前台:onRestart()--->onStart()--->onResume(),再次回到運行狀態。Activity退居后台: 且系統內存不足, 系統會殺死這個后台狀態的Activity,若再次回到這個Activity,則會走onCreate()-->onStart()--->onResume()
1.4 將一個 Activity 設置成窗口的樣式

只需要給我們的 Activity 配置如下屬性即可。
android:theme="@android:style/Theme.Dialog"
1.5 退出已調用多個 Activity 的 Application
- 通常情況用戶退出一個
Activity只需按返回鍵,我們寫代碼想退出activity直接調用finish()方法就行。

- 發送特定廣播:
- 在需要結束應用時, 發送一個特定的廣播,每個
Activity收到廣播后,關閉 即可。 - 給某個
activity注冊接受接受廣播的意圖registerReceiver(receiver, filter) - 如果過接受到的是 關閉
activity的廣播activity finish()掉
- 遞歸退出
- 就調用
finish()方法 把當前的Activity退出 - 在打開新的
Activity時使用startActivityForResult, 然后自己加標志, 在onActivityResult中處理, 遞歸關閉。
- 其實
- 也可以通過
intent的flag來實現intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)激活一個新的activity。 - 此時如果該任務棧中已經有該
Activity, 那么系統會把這個Activity上面的所有Activity干掉。 - 其實相當於給
Activity配置的啟動模式為singleTask。
- 記錄打開的
Activity
- 每打開一個
Activity, 就記錄下來。 - 在需要退出時 , 關閉每一個
Activity
1.6 鎖定屏與解鎖屏幕,Activity 如何執行生命周期

- 鎖屏時會執行
onPause()和onStop(), 而開屏時則應該執行onStart()onResume()
1.7 修改 Activity 進入和退出動畫

- 可以通過兩種方式 , 一是通過定義
Activity的主題 ,二是通過覆寫Activity的overridePendingTransition方法。 - 通過設置主題樣式在
styles.xml中編輯代碼 , 添加themes.xml文件:在AndroidManifest.xml中給指定的Activity指定theme。 - 覆寫
overridePendingTransition方法:overridePendingTransition(R.anim.fade, R.anim.hold);
1.8 Activity 的四種狀態

runnig:用戶可以點擊,activity處於棧頂狀態。paused:activity失去焦點的時候,被一個非全屏的activity占據或者被一個透明的activity覆蓋,這個狀態的activity並沒有銷毀,它所有的狀態信息和成員變量仍然存在,只是不能夠被點擊。(內存緊張的情況,這個activity有可能被回收)

stopped:這個activity被另外一個activity完全覆蓋,但是這個activity的所有狀態信息和成員變量仍然存在(除了內存緊張)killed:這個activity已經被銷毀,其所有的狀態信息和成員變量已經不存在了。
1.9 如何處理異常退出

Activity異常退出的時候 -->onPause()-->onSaveInstanceState()-->onStop()-->onDestory()- 需要注意的是
onSaveInstanceState()方法與onPause並沒有嚴格的先后關系,有可能在onPause之前,也有可能在其后面調用,但會在onStop()方法之前調用 - 異常退出后又重新啟動該
Activity-->onCreate()-->onStart()-->onRestoreInstanceState()-->onResume()

- 搞懂這個生命周期的執行后就可以回答了,首先要知道面試官的意思:是要重新啟動並恢復這個
Activity還是說直接退出整個app - 如果要恢復則要在
onSaveInstanceState()中進行保存數據並在onRestoreInstanceState()中進行恢復 - 如果是要退出
app的話就要捕獲全局的異常信息,並退出app - 當然個人建議是使用
UncaughtExceotionHandler來捕獲全局異常進行退出app的操作,這樣會減少之前崩潰所造成的后遺症!
1.10 什么是 onNewIntent

-
如果
IntentActivity處於任務棧的頂端,也就是說之前打開過的Activity,現在處於onPause、onStop狀態的話,其他應用再發送Intent的話 -
執行順序為:
onNewIntent,onRestart,onStart,onResume。
2. 啟動模式
2.1 啟動模式

Activity一共有四種launchMode:standard、singleTop、singleTask、singleInstance。

Standard模式(默認模式)
-
說明: 每次啟動一個
Activity都會又一次創建一個新的實例入棧,無論這個實例是否存在。 -
生命周期:每次被創建的實例
Activity的生命周期符合典型情況,它的onCreate、onStart、onResume都會被調用。 -
舉例:此時
Activity棧中以此有A、B、C三個Activity,此時C處於棧頂,啟動模式為Standard模式。若在C Activity中加入點擊事件,須要跳轉到還有一個同類型的C Activity。結果是還有一個C Activity進入棧中,成為棧頂。

SingleTop模式(棧頂復用模式)
-
說明:分兩種處理情況:須要創建的
Activity已經處於棧頂時,此時會直接復用棧頂的Activity。不會再創建新的Activity;若須要創建的Activity不處於棧頂,此時會又一次創建一個新的Activity入棧,同Standard模式一樣。 -
生命周期:若情況一中棧頂的
Activity被直接復用時,它的onCreate、onStart不會被系統調用,由於它並沒有發生改變。可是一個新的方法onNewIntent會被回調(Activity被正常創建時不會回調此方法)。 -
舉例:此時
Activity棧中以此有A、B、C三個Activity,此時C處於棧頂,啟動模式為SingleTop模式。情況一:在C Activity中加入點擊事件,須要跳轉到還有一個同類型的C Activity。結果是直接復用棧頂的C Activity。情況二:在C Activity中加入點擊事件,須要跳轉到還有一個A Activity。結果是創建一個新的Activity入棧。成為棧頂。

SingleTask模式(棧內復用模式)
-
說明:若須要創建的
Activity已經處於棧中時,此時不會創建新的Activity,而是將存在棧中的Activity上面的其他Activity所有銷毀,使它成為棧頂。 -
如果是在別的應用程序中啟動它,則會新建一個
task,並在該task中啟動這個Activity,singleTask允許別的Activity與其在一個task中共存,也就是說,如果我在這個singleTask的實例中再打開新的Activity,這個新的Activity還是會在singleTask的實例的task中。 -
生命周期:同
SingleTop模式中的情況一同樣。僅僅會又一次回調Activity中的onNewIntent方法 -
舉例:此時
Activity棧中以此有A、B、C三個Activity。此時C處於棧頂,啟動模式為SingleTask模式。情況一:在C Activity中加入點擊事件,須要跳轉到還有一個同類型的C Activity。結果是直接用棧頂的C Activity。情況二:在C Activity中加入點擊事件,須要跳轉到還有一個A Activity。結果是將A Activity上面的B、C所有銷毀,使A Activity成為棧頂。

SingleInstance模式(單實例模式)
-
說明:
SingleInstance比較特殊,是全局單例模式,是一種加強的SingleTask模式。它除了具有它所有特性外,還加強了一點:只有一個實例,並且這個實例獨立運行在一個task中,這個task只有這個實例,不允許有別的Activity存在。 -
這個經常使用於系統中的應用,比如
Launch、鎖屏鍵的應用等等,整個系統中僅僅有一個!所以在我們的應用中一般不會用到。了解就可以。 -
舉例:比方
A Activity是該模式,啟動A后。系統會為它創建一個單獨的任務棧,由於棧內復用的特性。興許的請求均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀。
2.2 啟動模式的使用方式

- 在
Manifest.xml中指定Activity啟動模式
- 一種靜態的指定方法
- 在
Manifest.xml文件里聲明Activity的同一時候指定它的啟動模式 - 這樣在代碼中跳轉時會依照指定的模式來創建
Activity。
- 啟動
Activity時。在Intent中指定啟動模式去創建Activity
- 一種動態的啟動模式
- 在
new一個Intent后 - 通過
Intent的addFlags方法去動態指定一個啟動模式。
- 注意:以上兩種方式都能夠為
Activity指定啟動模式,可是二者還是有差別的。
-
優先級:動態指定方式即另外一種比第一種優先級要高,若兩者同一時候存在,以另外一種方式為准。
-
限定范圍:第一種方式無法為
Activity直接指定FLAG_ACTIVITY_CLEAR_TOP標識,另外一種方式無法為Activity指定singleInstance模式。
2.3 啟動模式的實際應用場景
這四種模式中的
Standard模式是最普通的一種,沒有什么特別注意。而SingleInstance模式是整個系統的單例模式,在我們的應用中一般不會應用到。所以,這里就具體解說SingleTop和SingleTask模式的運用場景:

SingleTask模式的運用場景
- 最常見的應用場景就是保持我們應用開啟后僅僅有一個
Activity的實例。 - 最典型的樣例就是應用中展示的主頁(
Home頁)。 - 假設用戶在主頁跳轉到其他頁面,運行多次操作后想返回到主頁,假設不使用
SingleTask模式,在點擊返回的過程中會多次看到主頁,這明顯就是設計不合理了。
SingleTop模式的運用場景
- 假設你在當前的
Activity中又要啟動同類型的Activity - 此時建議將此類型
Activity的啟動模式指定為SingleTop,能夠降低Activity的創建,節省內存!
- 注意:復用
Activity時的生命周期回調
- 這里還須要考慮一個
Activity跳轉時攜帶頁面參數的問題。 - 由於當一個
Activity設置了SingleTop或者SingleTask模式后,跳轉此Activity出現復用原有Activity的情況時,此Activity的onCreate方法將不會再次運行。onCreate方法僅僅會在第一次創建Activity時被運行。 - 而一般
onCreate方法中會進行該頁面的數據初始化、UI初始化,假設頁面的展示數據無關頁面跳轉傳遞的參數,則不必操心此問題 - 若頁面展示的數據就是通過
getInten()方法來獲取,那么問題就會出現:getInten()獲取的一直都是老數據,根本無法接收跳轉時傳送的新數據!
- 以下,通過一個樣例來具體解釋:

-
以上代碼中的
CourseDetailActivity在配置文件里設置了啟動模式是SingleTop模式,依據上面啟動模式的介紹可得知,當CourseDetailActivity處於棧頂時。 -
再次跳轉頁面到
CourseDetailActivity時會直接復用原有的Activity,並且此頁面須要展示的數據是從getIntent()方法得來,可是initData()方法不會再次被調用,此時頁面就無法顯示新的數據。 -
當然這樣的情況系統早就為我們想過了,這時我們須要另外一個回調
onNewIntent(Intent intent)方法。此方法會傳入最新的intent,這樣我們就能夠解決上述問題。這里建議的方法是又一次去setIntent。然后又一次去初始化數據和UI。代碼例如以下所看到的:

- 這樣,在一個頁面中能夠反復跳轉並顯示不同的內容。
2.4 快速啟動一個 Activity

- 這個問題其實也是比較簡單的,就是不要在
Activity的onCreate方法中執行過多繁重的操作,並且在onPasue方法中同樣不能做過多的耗時操作。
2.5 啟動流程
-
注意!這里並不是要回答
Activity的生命周期!
2.6 Activity 的 Flags

-
標記位既能夠設定Activity的啟動模式,如同上面介紹的,在動態指定啟動模式,比方
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等。它還能夠影響Activity的運行狀態 ,比方FLAG_ACTIVITY_CLEAN_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。 -
以下介紹幾個基本的標記位,切勿死記,理解幾個就可以,須要時再查官方文檔。

FLAG_ACTIVITY_NEW_TASK
- 作用是為
Activity指定“SingleTask”啟動模式。跟在AndroidMainfest.xml指定效果同樣
FLAG_ACTIVITY_SINGLE_TOP
- 作用是為
Activity指定“SingleTop”啟動模式,跟在AndroidMainfest.xml指定效果同樣。
FLAG_ACTIVITY_CLEAN_TOP
- 具有此標記位的
Activity,啟動時會將與該Activity在同一任務棧的其他Activity出棧。 - 一般與
SingleTask啟動模式一起出現。 - 它會完畢
SingleTask的作用。 - 但事實上
SingleTask啟動模式默認具有此標記位的作用
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- 具有此標記位的
Activity不會出如今歷史Activity的列表中 - 使用場景:當某些情況下我們不希望用戶通過歷史列表回到
Activity時,此標記位便體現了它的效果。 - 它等同於在
xml中指定Activity的屬性.
2.7 onNewInstent()方法什么時候執行

這個是啟動模式中的了,當此 Activity 的實例已經存在,並且此時的啟動模式為 SingleTask 和 SingleInstance ,另外當這個實例位於棧頂且啟動模式為 SingleTop 時也會觸發 onNewInstent() 。
3. 數據
3.1 Activity 間通過 Intent 傳遞數據大小限制

Intent在傳遞數據時是有大小限制的,這里官方並未詳細說明,不過通過實驗的方法可以測出數據應該被限制在1MB之內(1024KB)- 我們采用傳遞
Bitmap的方法,發現當圖片大小超過1024(准確地說是1020左右)的時候,程序就會出現閃退、停止運行等異常(不同的手機反應不同) - 因此可以判斷
Intent的傳輸容量在1MB之內。
3.2 內存不足時系統會殺掉后台的Activity,若需要進行一些臨時狀態的保存,在哪個方法進行

-
Activity的onSaveInstanceState()和onRestoreInstanceState()並不是生命周期方法,它們不同於onCreate()、onPause()等生命周期方法,它們並不一定會被觸發。 -
onSaveInstanceState()方法,當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷毀一個Activity,onSaveInstanceState()會被調用。 -
但是當用戶主動去銷毀一個
Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。 -
除非該
activity不是被用戶主動銷毀的,通常onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
3.3 onSaveInstanceState() 被執行的場景

- 系統不知道你按下
HOME后要運行多少其他的程序,自然也不知道activity A是否會被銷毀 - 因此系統都會調用
onSaveInstanceState(),讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則:
- 當用戶按下
HOME鍵時 - 長按
HOME鍵,選擇運行其他的程序時 - 鎖屏時
- 從
activity A中啟動一個新的activity時 - 屏幕方向切換時
3.4 兩個 Activity 之間跳轉時必然會執行的方法

一般情況下比如說有兩個 activity , 分別叫 A , B ,當在 A 里面激活 B 組件的時候, A 會調用 onPause() 方法,然后 B 調用 onCreate() , onStart() , onResume() 。
這個時候 B 覆蓋了窗體, A 會調用 onStop() 方法. 如果 B 是個透明的,或者 是對話框的樣式, 就不會調用 A 的 onStop() 方法。
3.5 用 Intent 去啟動一個Activity 之外的方法

- 使用
adb shell am命令
am啟動一個activityadb shell am start com.example.fuchenxuan/.MainActivityam發送一個廣播,使用actionadb shell am broadcast -a magcomm.action.TOUCH_LETTER
3.6 scheme 跳轉協議

3.6.1 定義

-
服務器可以定制化跳轉
app頁面 -
app可以通過Scheme跳轉到另一個app頁面 -
可以通過
h5頁面跳轉app原生頁面
3.6.2 協議格式:


-
qh代表Scheme協議名稱 -
test代表Scheme作用的地址域 -
8080代表改路徑的端口號 -
/goods代表的是指定頁面(路徑) -
goodsId和name代表傳遞的兩個參數
3.6.3 Scheme使用
- 定義一個
Scheme

- 獲取
Scheme跳轉的參數

- 調用方式
- 原生調用

- html調用

- 判斷某個Scheme是否有效

- 關於scheme跳轉協議,可以查看下面的博客,站在巨人的肩膀上,才能看得更遠
Android產品研發(十一)-->應用內跳轉Scheme協議
4. Context
4.1 Context , Activity , Appliction 的區別

- 相同:
Activity和Application都是Context的子類。 Context從字面上理解就是上下文的意思, 在實際應用中它也確實是起到了管理 上下文環境中各個參數和變量的總用, 方便我們可以簡單的訪問到各種資源。- 不同:維護的生命周期不同。
Context維護的是當前的Activity的生命周期,Application維護的是整個項目的生命周期。 - 使用
context的時候, 小心內存泄露, 防止內存泄露
4.2 Context 是什么

-
它描述的是一個應用程序環境的信息,即上下文。
-
該類是一個抽象(
abstract class)類,Android提供了該抽象類的具體實 現類(ContextIml)。 -
通過它我們可以獲取應用程序的資源和類, 也包括一些應用級別操作, 例如:啟動一個
Activity,發送廣播,接受Intent,信息,等。
4.2.1 附加一張 Context 繼承關系圖

4.3 獲取當前屏幕 Activity 的對象

- 使用 ActivityLifecycleCallbacks
Android 如何獲取當前Activity實例對象?
4.4 Activity 的管理機制

-
面試官問這個問題,想看看大家對Activity了解是否深入:
- 什么是 ActivityRecord
- 什么是 TaskRecord
- 什么是 ActivityManagerService
4.5 什么是 Activity

- 四大組件之一,通常一個用戶交互界面對應一個
activity。 activity是Context的子類,同時實現了window.callback和keyevent.callback,可以處理與窗體用戶交互的事件。- 開發中常用的有
FragmentActivity、ListActivity、TabActivity(Android 4.0被Fragment取代)
5. 進程
5.1 Android 進程優先級
- 前台 / 可見 / 服務 / 后台 / 空

5.1.1 前台進程:Foreground process

- 用戶正在交互的
Activity(onResume()) - 當某個
Service綁定正在交互的Activity - 被主動調用為前台
Service(startForeground()) - 組件正在執行生命周期的回調(
onCreate()、onStart()、onDestory()) BroadcastReceiver正在執行onReceive()
5.1.2 可見進程:Visible process

- 我們的
Activity處在onPause()(沒有進入onStop()) - 綁定到前台
Activity的Service
5.1.3 服務進程:Service process

- 簡單的
startService()啟動。
5.1.4 后台進程:Background process

- 對用戶沒有直接影響的進程 ---
Activity處於onStop()的時候。 android:process=":xxx"
5.1.5 空進程:Empty process

- 不含有任何的活動的組件。(
Android設計的,處於緩存的目的,為了第二次啟動更快,采取的一個權衡)
5.2 可見進程

可見進程指部分程序界面能夠被用戶看見,卻不在前台與用戶交互的進程。例如,我們在一個界面上彈出一個對話框(該對話框是一個新的
Activity),那么在對話框后面的原界面是可見的,但是並沒有與用戶進行交互,那么原界面就是可見進程。
- 一個進程滿足下面任何一個條件都被認為是可視的:
- 寄宿着一個不是前台的活動,但是它對用戶仍可見(它的
onPause()方法已經被調用)。舉例來說,這可能發生在,如果一個前台活動在一個對話框(其他進程的)運行之后仍然是可視的,比如輸入法的彈出時。 - 寄宿着一個服務,該服務綁定到一個可視的活動。
- 一個可視進程被認為是及其重要的且不會被殺死,除非為了保持前台進程運行。
5.3 服務進程

-
服務進程是通過
startService()方法啟動的進程,但不屬於前台進程和可見進程。例如,在后台播放音樂或者在后台下載就是服務進程。 -
系統保持它們運行,除非沒有足夠內存來保證所有的前台進程和可視進程。
5.4 后台進程

- 后台進程是一個保持着一個當前對用戶不可視的活動(已經調用
Activity對象的onStop()方法)(如果還有除了UI線程外其他線程在運行話,不受影響)。
例如我正在使用
Home鍵讓
- 這些進程沒有直接影響用戶體驗,並且可以在任何時候被殺以收回內存用於一個前台、可視、服務進程。
- 一般地有很多后台進程運行着,因此它們保持在一個
LRU(least recently used,即最近最少使用,如果您學過操作系統的話會覺得它很熟悉,跟內存的頁面置換算法LRU一樣)列表以確保最近使用最多的活動的進程最后被殺。
5.5 空進程

-
空進程是一個沒有保持活躍的應用程序組件的進程,不包含任何活躍組件。
-
保持這個進程可用的唯一原因是作為一個
cache以提高下次啟動組件的速度。系統進程殺死這些進程,以在進程cache和潛在的內核cache之間平衡整個系統資源。 -
android進程的回收順序從先到后分別是:空進程,后台進程,服務進程,可見進程,前台進程。
5.6 什么是 ANR,如何避免

5.6.1 什么是ANR

ANR,全稱為Application Not Responding。- 在
Android中,如果你的應用程序有一段時間沒有響應,系統會向用戶顯示一個對話框,這個對話框稱作應用程序無響應對話框。
5.6.2 用戶行為

- 用戶可以選擇讓程序繼續運行,也可以讓程序停止運行。
- 他們在使用你的應用程序時,並不希望每次都要處理這個對話框。
- 因此,在程序里對響應性能的設計很重要,這樣,系統不會顯示
ANR給用戶。
5.6.3 Android不同組件ANR超時時間不同

- 不同的組件發生
ANR的時間不一樣,主線程(Activity、Service)是5秒,BroadCastReceiver是10秒。
5.6.4 解決方案

- 將所有耗時操作,比如訪問網絡,
Socket通信,查詢大量SQL語句,復雜邏輯計算等都放在子線程中去,然后通過handler.sendMessage、runonUITread、AsyncTask等方式更新UI,以確保用戶界面操作的流暢度。 - 如果耗時操作需要讓用戶等待,那么可以在界面上顯示進度條。
5.7 android的任務棧 Task

- 一個
Task包含的就是activity集合,android系統可以通過任務棧有序的管理activity - 一個app當中可能不止一個任務棧,在某些情況下,一個
activity也可以獨享一個任務棧(singleInstance模式啟動的activity)
總結
- 本文基本涵蓋了
Android Activity的所有知識點。對於App啟動、AMS希望大家能根據文中鏈接或者Google搜索的形式繼續展開學習。 重點:關於Android的四大組件,到現在為止我才總結完 Activity ,馬上我將繼續針對,Service,BroadcastRecevier等,以及事件分發、滑動沖突、新能優化等重要模塊,進行全面總結,歡迎大家關注 _yuanhao 的 博客園 ,方便及時接收更新
碼字不易,你的點贊是我總結的最大動力!
-
由於我在「稀土掘金」「簡書」「
CSDN」「博客園」等站點,都有新內容發布。所以大家可以直接關注我的GitHub倉庫,以免錯過精彩內容! -
一萬多字長文,加上精美思維導圖,記得點贊哦,歡迎關注 _yuanhao 的 博客園 ,我們下篇文章見!

