Android四大組件是指 Activity、Service(服務)、BroadcastReceiver(廣播)、ContentProvider。
在注冊方面,Activity、Service、ContentProvider 必須在 AndroidManifest 中注冊,而 BroadcastReceiver 既可以在 AndroidManifest 中注冊,也可以通過代碼來注冊。如下圖所示:

在調(diào)用方式上,Activity、Service、BroadcastReceiver 需要借助 Intent,而 ContentProvider 不需要借助 Intent。
1. Activity
Activity 是一種展示型組件,起作用就在于向用戶直接展示一個界面,并且接收用戶操作信息從而進行交互。Activity 是唯一一種可以直接感知的組件,可以說,在用戶看來 Activity 就是一個Android應用的全部。
1.1 Activity 的啟動方式
Activity 啟動方式有兩種,顯式啟動和隱式啟動:
顯式啟動
明確指定一個 Activity 組件,正如我們平常大部分時間所使用的方式:(文中全部代碼使用Kotlin)
startActivity(Intent(this, SampleActivity::class.java))或者startActivityForResult(Intent(this, SampleActivity::class.java), 0)隱式啟動
指向一個或多個 Activity 組件,隱式啟動需要我們再 AndroidManifest 中為 Activity 配置 intent-filter ,比如:
<!--Activity注冊-->
<activity android:name=".SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
這個時候,我們可以使用如下方法啟動 SampleActivity
// 創(chuàng)建Intent,并制定action
val intent = Intent("sampleActivity")
startActivity(intent)
隱式啟動這種方式,相信我們平常也是用的比較多的,比如調(diào)用系統(tǒng)撥號功能。
1.2 Activity 的啟動模式
Activity 的啟動模式有4種,standard、singleTop、singleTask、singleInstance
- standard 標準模式,系統(tǒng)的默認啟動模式。每次啟動 Activity,都會在當前棧中創(chuàng)建一個實例。
- singleTop 棧頂復用模式。啟動 Activity 時,如當前任務棧頂已經(jīng)存在該 Activity 的實例,則不會重新創(chuàng)建,同時它的 onNewIntent 方法被調(diào)用。
- singleTask 棧內(nèi)復用模式。這是一種單實例模式,只要 Activity 在一個任務棧中存在,多次啟動時都不會重新創(chuàng)建實例,而是將棧頂?shù)皆?Activity 實例之前的 Activity 全部 finish,使該 Activity 處于棧頂,并調(diào)用其 onNewIntent 方法。
- singleInstance 單實例模式。這是一種加強的 singleTask 模式,除了擁有 singleTask 全部特征外,此模式的 Activity 只能單獨位于一個任務棧中。
1.3 Activity 的啟動過程
關于 Activity 的啟動過程,這里就簡單文字描述下,就不貼源碼了,關于源碼的解析,有機會單獨記錄。
啟動 Activity 都是調(diào)用 startActivity(ForResult) 方法,所以,就從 startActivity(ForResult) 方法開始:
- startActivity(ForResult) 方法有多種重載方式,但它們最終都會調(diào)用
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) - startActivityForResult 方法中調(diào)用 Instrumentation 的
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, UserHandle user),該方法的參數(shù)中傳入了 mMainThread.getApplicationThread(),它的類型是 ApplicationThread,ApplicationThread 是 ActivityThread 的一個內(nèi)部類。 - Instrumentation.execStartActivity 方法中,由 ActivityManagerNative.getDefault() 的 startActivity 方法完成 Activity 啟動。ActivityManagerService(下面簡稱AMS)繼承自 ActivityManagerNative,而 ActivityManagerNative 繼承自 Binder 并實現(xiàn)了 IActivityManager 這個 Binder 接口,因此 AMS 也是一個 Binder,它是 IActivityManager 的具體實現(xiàn)。由于 ActivityManagerNative.getDefault() 其實是一個 IActivityManager 類型的 Binder 對象,因此它的具體實現(xiàn)是 AMS。所以,Activity 的啟動過程有轉移到了 AMS 中。
- AMS 的 startActivity 方法調(diào)用 ActivityStackSupervisor 的 startActivityMayWait 方法。startActivityMayWait 中又調(diào)用 startActivityLocked 方法。startActivityLocked 又調(diào)用了 startActivityUncheckedLocked 方法。接著 startActivityUncheckedLocked 又調(diào)用了 ActivityStack 的 resumeTopActivitiesLocked 方法。
- 這個時候啟動過程已經(jīng)從 ActivityStackSupervisor 轉移到了 ActivityStack,ActivityStack 的 resumeTopActivitiesLocked 又會調(diào)用 resumeTopActivityInnerLocked 方法,resumeTopActivityInnerLocked 又調(diào)用了 ActivityStackSupervisor 的 startSpecificActivityLocked 方法。
- 此時,啟動過程又回到了 ActivityStackSupervisor,在 startSpecificActivityLocked 方法中,調(diào)用 realStartActivityLocked 方法。
- 在 realStartActivityLocked 中,有這樣一段代碼
app.thread.scheduleLaunchActivity(**),app.thread 的類型為 IApplicationThread,其實現(xiàn)者是 ActivityThread 中的內(nèi)部類 ApplicationThread。饒了一大圈,Activity 的啟動過程最終回到了 ApplicationThread 中,ApplicationThread 通過 scheduleLaunchActivity 方法來啟動 Activity。 - scheduleLaunchActivity 做的事情也很簡單,就是發(fā)送一個啟動 Activity 的消息交由 Handler 處理,這個 Handler 有一個很簡潔的名字:H。
- H 對 Activity 啟動消息的處理是調(diào)用 ActivityThread 的 handleLaunchActivity 方法來實現(xiàn) Activity 的啟動。
- 最終,在 handleLaunchActivity 中,調(diào)用 performLaunchActivity 方法完成 Activity 對象的創(chuàng)建和啟動,之后,又通過 HandlerResumeActivity 方法來調(diào)用被啟動 Activity 的 onResume 這一生命周期方法。
2. Service
Service 是一種計算型組件,用于在后臺執(zhí)行耗時操作。由于 Service 組件工作在后臺,因此用戶無法直接感知到它的存在。
2.1 Service 的運行模式
Activity 組件只有一種運行模式,即 Activity 處于啟動狀態(tài),Service 組件略有不同,它有兩種狀態(tài):啟動狀態(tài)和綁定狀態(tài)。
-
啟動狀態(tài)
Service 內(nèi)部可以做一些后臺計算,并且不需要和外界有直接的交互。該運行模式可以使用 Context 的startService(Intent)方法啟動Service。 -
綁定狀態(tài)
Service 內(nèi)部同樣可以進行后臺計算,但是處于這種狀態(tài)時,外界可以很方便地和 Service 組件進行通信。該運行模式使用 Context 的bindService(Intent, ServiceConnection, int)方法啟動 Service。
2.2 Service 的啟動過程
2.2.1 startService 方式
示例代碼:startService(Intent(this, SampleService::class.java))
Context 的實現(xiàn)類只有 ContextWrapper,所以該 Service 的啟動從 ContextWrapper 的 startService 方法開始:
- startService 方法調(diào)用 ContextImpl 的 startService 方法。
- 在 ContextImpl 中,startService 方法會調(diào)用 startServiceCommon 方法,而 startServiceCommon 方法又通過調(diào)用
ActivityManagerNative.getDefault().startService(**)來啟動一個 Service。對于 ActivityManagerNative.getDefault() 這個對象,在 Activity 的啟動過程中第 3 點分析了,它實際上就是 AMS (ActivityManagerService),這里就不在重復說明了。需要注意的是,通過 AMS 來啟動 Service 的行為是一個遠程過程調(diào)用。 - 在 AMS 的 startService 中,會通過 ActiveServices 對象來完成 Service 后續(xù)的啟動過程。也就是調(diào)用 ActiveServices 的 startServiceLocked 方法。
- 在 ActiveServices 的 startServiceLocked 方法尾部會調(diào)用 startServiceInnerLocked 方法,startServiceInnerLocked 又調(diào)用了 realStartServiceLocked 方法。
- 在 realStartServiceLocked 方法中,首先通過 app.thread 的 scheduleCreateService 方法來創(chuàng)建并調(diào)用其 onCreate,接著再調(diào)用 sendServiceArgsLocked 方法來調(diào)用 Service 的其它方法,比如 onStartCommand,這兩個過程均是進程間通信。
- 由 Activity 的啟動過程第 10 點可知,app.thread 是 ApplicationThread 的實例,也就是調(diào)用了 ApplicationThread 的 scheduleCreateService 方法來創(chuàng)建 Service。這個過程和 Activity 的啟動過程是類似的,都是通過發(fā)送消息給 Handler H 來完成。
- H 通過 ActivityThread 的 handleCreateService 方法來完成 Service 的最終啟動。handleCreateService 主要完成如下幾件事:
- 通過類加載器創(chuàng)建 Service 的實例。
- 創(chuàng)建 Application 對象并調(diào)用其 onCreate,當然 Application 的創(chuàng)建過程只有一次。
- 創(chuàng)建 ContextImpl 對象并通過 Service 的 attach 方法建立二者之間的聯(lián)系,這個過程和 Activity 實際上是類似的,畢竟 Service 和 Activity 都是一個 Context。
- 最后調(diào)用 Service 的 onCreate 方法并將 Service 對象存儲到 ActivityThread 中的一個列表。
- 由于 Service 的 onCreate 方法被執(zhí)行了,這也意味著 Service 已經(jīng)啟動了。除此之外, ActivityThread 中還會通過 handleServiceArgs 方法調(diào)用 Service 的 onStartCommand 方法。
2.2.2 bindService 方式
示例代碼:
val service = Intent(this, SampleService::class.java)
bindService(service, mServiceConnection, Service.BIND_AUTO_CREATE)
和 startService 一樣,bindService 的過程也是從 ContextWrapper 開始的:
- bindService 方法調(diào)用 ContextImpl 的 bindService 方法,然后 bindService 方法調(diào)用 bindServiceCommon 方法。
- 在 bindServiceCommon 中,首先將客戶端的 ServiceConnection 對象轉化為 ServiceDispatcher.InnerConnection 對象。接著通過 AMS 來完成 Service 的具體綁定過程,方式是調(diào)用
ActivityManagerNative.getDefault().bindService(**)。 - AMS 的 bindService 方法調(diào)用 ActiveServices 的 bindServiceLocked 方法,bindServiceLocked 方法再調(diào)用 bringUpServiceLocked,bringUpServiceLocked 又會調(diào)用 realStartServiceLocked 方法。
- realStartServiceLocked 方法的邏輯和
2.2.1中的邏輯類似,最終都是通過 ApplicationThread 來完成 Service 實例的創(chuàng)建并執(zhí)行其 onCreate 方法。和 startService 方式不同的是,Service 的綁定過程會調(diào)用app.thread的 scheduleBindService 方法。 - 和上述類似,scheduleBindService 也是通過發(fā)送消息給 Handler H 來中轉的。
- H 接收到綁定 Service 的消息時,調(diào)用 ActivityThread 的 handleBindService 方法來處理。
- 在 handleBindService 中,首先根據(jù) Service 的 token 取出 Service 對象,然后調(diào)用 Service 的 onBind 方法。接著通過 ActivityManagerNative.getDefault() 的 publishService 方法調(diào)用客戶端的 ServiceConnection 中的 onServiceConnection。
3 BroadcastReceiver
BroadcastReceiver 也叫廣播接收者,是一種消息型組件,用于在不同的組件甚至不同的應用之間傳遞消息。 BroadcastReceiver 同樣是用戶無感知的。
3.1 BroadcastReceiver 的注冊方式
BroadcastReceiver 的注冊方式有兩種,靜態(tài)注冊和動態(tài)注冊。
-
靜態(tài)注冊
指在 AndroidManifest 中注冊廣播,這種廣播在應用安裝時被系統(tǒng)解析,不需要啟動應用就可以收到相應的廣播。應用的開機啟動就是用到了這類 BroadcastReceiver 來監(jiān)聽手機的啟動。注冊代碼可參考上文的圖。 -
動態(tài)注冊
在代碼中調(diào)用 Context.registerReceiver() 來實現(xiàn)注冊。此類注冊要在不使用的時候調(diào)用 Context.unRegisterReceiver() 進行反注冊,否則容易因持有 Context 實例造成內(nèi)存泄漏。
4. ContextProvider
ContentProvider 是一種數(shù)據(jù)共享型組件,用于向其它組件甚至其它應用共享數(shù)據(jù)。由于只是用來組件或者應用間共享數(shù)據(jù),用戶同樣無法直接感知。
一個 ContentProvider 組件內(nèi)部要實現(xiàn)數(shù)據(jù)的增刪改查四種操作,其內(nèi)部維持著一份數(shù)據(jù)集合。這個數(shù)據(jù)集合有多種實現(xiàn)方式,ContentProvider 本身對其沒有任何要求,常見的使用數(shù)據(jù)庫來實現(xiàn)。
四大組件是否可以開啟多進程
上面對Android的四大組件的作用進行了一個大致的說明,由于篇幅原因,這里只簡單描述了下 Activity 和 Service 啟動過程,找機會再整理下 BroadcastReceiver 和 ContentProvider 的工作過程。
我們知道,Android 應用是可以開啟多個進程的,就是在 AndroidManifest 中使用 android:process 屬性,比如要給某一 Activity 指定運行進程,則在其 <activity> 標簽中添加 android:process 屬性即可。那么,其它的三種組件是否也可以為其指定運行進程呢?也就是說,Android的四大組件是否都可以開啟多進程?
這里我大膽假設小心求證下,我先假設都可以,并且開啟方式都和 Activity 相同,如果不行的話,再根據(jù)問題進行相應調(diào)整,并最終得出結論。
demo:
AndroidManifest中注冊
<!--Activity注冊-->
<activity android:name=".SampleActivity"
android:process=":SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!--Service注冊-->
<service
android:name=".SampleService"
android:process=":SampleService"/>
<!--BroadcastReceiver靜態(tài)注冊-->
<receiver
android:name=".SampleReceiver"
android:process=":SampleReceiver">
<intent-filter>
<action android:name="com.cy.receiver.sample"/>
</intent-filter>
</receiver>
<!--ContentProvider注冊-->
<provider
android:name=".SampleContentProvider"
android:process=":SampleContentProvider"
android:authorities="com.cy.multiprocess.SampleContentProvider"
android:exported="true">
</provider>
MainActivity中啟動 Activity、Service,發(fā)送廣播,訪問ContentProvider
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "主進程號為:${getCurProcessName()}")
// 啟動 Activity
startActivity(Intent(this, SampleActivity::class.java))
// 啟動 Service
val service = Intent(this, SampleService::class.java)
startService(service)
// 發(fā)送廣播
sendBroadcast(Intent("com.cy.receiver.sample"))
// 訪問ContentProvider
val uri = Uri.parse("content://com.cy.multiprocess.SampleContentProvider")
contentResolver.query(uri, null, null, null, null)
}
}
/**
* Context 的擴展方法:獲取當前進程號
*/
fun Context.getCurProcessName(): String? {
val pid = android.os.Process.myPid()
val mActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (appProcess in mActivityManager.runningAppProcesses) {
if (appProcess.pid == pid) {
return appProcess.processName
}
}
return null
}
最后在各自生命周期方法中打印進程名,運行結果如下:

由圖可知,每個組件所運行的進程id、進程名和主進程都不一樣,所以結論是:Android的四大組件都可以開啟多進程。