Android四大組件是指 Activity、Service(服務(wù))蜈首、BroadcastReceiver(廣播)者娱、ContentProvider。
在注冊(cè)方面济蝉,Activity杰刽、Service、ContentProvider 必須在 AndroidManifest 中注冊(cè)王滤,而 BroadcastReceiver 既可以在 AndroidManifest 中注冊(cè)贺嫂,也可以通過代碼來(lái)注冊(cè)。如下圖所示:
在調(diào)用方式上雁乡,Activity第喳、Service、BroadcastReceiver 需要借助 Intent踱稍,而 ContentProvider 不需要借助 Intent曲饱。
1. Activity
Activity 是一種展示型組件,起作用就在于向用戶直接展示一個(gè)界面珠月,并且接收用戶操作信息從而進(jìn)行交互扩淀。Activity 是唯一一種可以直接感知的組件,可以說啤挎,在用戶看來(lái) Activity 就是一個(gè)Android應(yīng)用的全部驻谆。
1.1 Activity 的啟動(dòng)方式
Activity 啟動(dòng)方式有兩種,顯式啟動(dòng)和隱式啟動(dòng):
顯式啟動(dòng)
明確指定一個(gè) Activity 組件庆聘,正如我們平常大部分時(shí)間所使用的方式:(文中全部代碼使用Kotlin)
startActivity(Intent(this, SampleActivity::class.java))
或者startActivityForResult(Intent(this, SampleActivity::class.java), 0)
隱式啟動(dòng)
指向一個(gè)或多個(gè) Activity 組件胜臊,隱式啟動(dòng)需要我們?cè)?AndroidManifest 中為 Activity 配置 intent-filter ,比如:
<!--Activity注冊(cè)-->
<activity android:name=".SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
這個(gè)時(shí)候伙判,我們可以使用如下方法啟動(dòng) SampleActivity
// 創(chuàng)建Intent象对,并制定action
val intent = Intent("sampleActivity")
startActivity(intent)
隱式啟動(dòng)這種方式,相信我們平常也是用的比較多的宴抚,比如調(diào)用系統(tǒng)撥號(hào)功能勒魔。
1.2 Activity 的啟動(dòng)模式
Activity 的啟動(dòng)模式有4種甫煞,standard、singleTop沥邻、singleTask危虱、singleInstance
- standard 標(biāo)準(zhǔn)模式,系統(tǒng)的默認(rèn)啟動(dòng)模式唐全。每次啟動(dòng) Activity埃跷,都會(huì)在當(dāng)前棧中創(chuàng)建一個(gè)實(shí)例。
- singleTop 棧頂復(fù)用模式邮利。啟動(dòng) Activity 時(shí)弥雹,如當(dāng)前任務(wù)棧頂已經(jīng)存在該 Activity 的實(shí)例,則不會(huì)重新創(chuàng)建延届,同時(shí)它的 onNewIntent 方法被調(diào)用剪勿。
- singleTask 棧內(nèi)復(fù)用模式。這是一種單實(shí)例模式方庭,只要 Activity 在一個(gè)任務(wù)棧中存在厕吉,多次啟動(dòng)時(shí)都不會(huì)重新創(chuàng)建實(shí)例,而是將棧頂?shù)皆?Activity 實(shí)例之前的 Activity 全部 finish械念,使該 Activity 處于棧頂头朱,并調(diào)用其 onNewIntent 方法。
- singleInstance 單實(shí)例模式龄减。這是一種加強(qiáng)的 singleTask 模式项钮,除了擁有 singleTask 全部特征外,此模式的 Activity 只能單獨(dú)位于一個(gè)任務(wù)棧中希停。
1.3 Activity 的啟動(dòng)過程
關(guān)于 Activity 的啟動(dòng)過程烁巫,這里就簡(jiǎn)單文字描述下,就不貼源碼了宠能,關(guān)于源碼的解析亚隙,有機(jī)會(huì)單獨(dú)記錄。
啟動(dòng) Activity 都是調(diào)用 startActivity(ForResult) 方法违崇,所以阿弃,就從 startActivity(ForResult) 方法開始:
- startActivity(ForResult) 方法有多種重載方式,但它們最終都會(huì)調(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 的一個(gè)內(nèi)部類肴楷。 - Instrumentation.execStartActivity 方法中,由 ActivityManagerNative.getDefault() 的 startActivity 方法完成 Activity 啟動(dòng)荠呐。ActivityManagerService(下面簡(jiǎn)稱AMS)繼承自 ActivityManagerNative赛蔫,而 ActivityManagerNative 繼承自 Binder 并實(shí)現(xiàn)了 IActivityManager 這個(gè) Binder 接口砂客,因此 AMS 也是一個(gè) Binder,它是 IActivityManager 的具體實(shí)現(xiàn)呵恢。由于 ActivityManagerNative.getDefault() 其實(shí)是一個(gè) IActivityManager 類型的 Binder 對(duì)象鞠值,因此它的具體實(shí)現(xiàn)是 AMS。所以渗钉,Activity 的啟動(dòng)過程有轉(zhuǎn)移到了 AMS 中彤恶。
- AMS 的 startActivity 方法調(diào)用 ActivityStackSupervisor 的 startActivityMayWait 方法。startActivityMayWait 中又調(diào)用 startActivityLocked 方法鳄橘。startActivityLocked 又調(diào)用了 startActivityUncheckedLocked 方法声离。接著 startActivityUncheckedLocked 又調(diào)用了 ActivityStack 的 resumeTopActivitiesLocked 方法。
- 這個(gè)時(shí)候啟動(dòng)過程已經(jīng)從 ActivityStackSupervisor 轉(zhuǎn)移到了 ActivityStack瘫怜,ActivityStack 的 resumeTopActivitiesLocked 又會(huì)調(diào)用 resumeTopActivityInnerLocked 方法术徊,resumeTopActivityInnerLocked 又調(diào)用了 ActivityStackSupervisor 的 startSpecificActivityLocked 方法。
- 此時(shí)鲸湃,啟動(dòng)過程又回到了 ActivityStackSupervisor赠涮,在 startSpecificActivityLocked 方法中,調(diào)用 realStartActivityLocked 方法暗挑。
- 在 realStartActivityLocked 中笋除,有這樣一段代碼
app.thread.scheduleLaunchActivity(**)
,app.thread 的類型為 IApplicationThread窿祥,其實(shí)現(xiàn)者是 ActivityThread 中的內(nèi)部類 ApplicationThread株憾。饒了一大圈,Activity 的啟動(dòng)過程最終回到了 ApplicationThread 中晒衩,ApplicationThread 通過 scheduleLaunchActivity 方法來(lái)啟動(dòng) Activity嗤瞎。 - scheduleLaunchActivity 做的事情也很簡(jiǎn)單,就是發(fā)送一個(gè)啟動(dòng) Activity 的消息交由 Handler 處理听系,這個(gè) Handler 有一個(gè)很簡(jiǎn)潔的名字:H贝奇。
- H 對(duì) Activity 啟動(dòng)消息的處理是調(diào)用 ActivityThread 的 handleLaunchActivity 方法來(lái)實(shí)現(xiàn) Activity 的啟動(dòng)。
- 最終靠胜,在 handleLaunchActivity 中掉瞳,調(diào)用 performLaunchActivity 方法完成 Activity 對(duì)象的創(chuàng)建和啟動(dòng),之后浪漠,又通過 HandlerResumeActivity 方法來(lái)調(diào)用被啟動(dòng) Activity 的 onResume 這一生命周期方法陕习。
2. Service
Service 是一種計(jì)算型組件,用于在后臺(tái)執(zhí)行耗時(shí)操作址愿。由于 Service 組件工作在后臺(tái)该镣,因此用戶無(wú)法直接感知到它的存在。
2.1 Service 的運(yùn)行模式
Activity 組件只有一種運(yùn)行模式响谓,即 Activity 處于啟動(dòng)狀態(tài)损合,Service 組件略有不同省艳,它有兩種狀態(tài):啟動(dòng)狀態(tài)和綁定狀態(tài)。
-
啟動(dòng)狀態(tài)
Service 內(nèi)部可以做一些后臺(tái)計(jì)算嫁审,并且不需要和外界有直接的交互跋炕。該運(yùn)行模式可以使用 Context 的startService(Intent)
方法啟動(dòng)Service。 -
綁定狀態(tài)
Service 內(nèi)部同樣可以進(jìn)行后臺(tái)計(jì)算律适,但是處于這種狀態(tài)時(shí)辐烂,外界可以很方便地和 Service 組件進(jìn)行通信。該運(yùn)行模式使用 Context 的bindService(Intent, ServiceConnection, int)
方法啟動(dòng) Service捂贿。
2.2 Service 的啟動(dòng)過程
2.2.1 startService 方式
示例代碼:startService(Intent(this, SampleService::class.java))
Context 的實(shí)現(xiàn)類只有 ContextWrapper棉圈,所以該 Service 的啟動(dòng)從 ContextWrapper 的 startService 方法開始:
- startService 方法調(diào)用 ContextImpl 的 startService 方法。
- 在 ContextImpl 中眷蜓,startService 方法會(huì)調(diào)用 startServiceCommon 方法分瘾,而 startServiceCommon 方法又通過調(diào)用
ActivityManagerNative.getDefault().startService(**)
來(lái)啟動(dòng)一個(gè) Service。對(duì)于 ActivityManagerNative.getDefault() 這個(gè)對(duì)象吁系,在 Activity 的啟動(dòng)過程中第 3 點(diǎn)分析了德召,它實(shí)際上就是 AMS (ActivityManagerService),這里就不在重復(fù)說明了汽纤。需要注意的是上岗,通過 AMS 來(lái)啟動(dòng) Service 的行為是一個(gè)遠(yuǎn)程過程調(diào)用。 - 在 AMS 的 startService 中蕴坪,會(huì)通過 ActiveServices 對(duì)象來(lái)完成 Service 后續(xù)的啟動(dòng)過程肴掷。也就是調(diào)用 ActiveServices 的 startServiceLocked 方法。
- 在 ActiveServices 的 startServiceLocked 方法尾部會(huì)調(diào)用 startServiceInnerLocked 方法背传,startServiceInnerLocked 又調(diào)用了 realStartServiceLocked 方法呆瞻。
- 在 realStartServiceLocked 方法中,首先通過 app.thread 的 scheduleCreateService 方法來(lái)創(chuàng)建并調(diào)用其 onCreate径玖,接著再調(diào)用 sendServiceArgsLocked 方法來(lái)調(diào)用 Service 的其它方法痴脾,比如 onStartCommand,這兩個(gè)過程均是進(jìn)程間通信梳星。
- 由 Activity 的啟動(dòng)過程第 10 點(diǎn)可知赞赖,app.thread 是 ApplicationThread 的實(shí)例,也就是調(diào)用了 ApplicationThread 的 scheduleCreateService 方法來(lái)創(chuàng)建 Service冤灾。這個(gè)過程和 Activity 的啟動(dòng)過程是類似的前域,都是通過發(fā)送消息給 Handler H 來(lái)完成。
- H 通過 ActivityThread 的 handleCreateService 方法來(lái)完成 Service 的最終啟動(dòng)韵吨。handleCreateService 主要完成如下幾件事:
- 通過類加載器創(chuàng)建 Service 的實(shí)例匿垄。
- 創(chuàng)建 Application 對(duì)象并調(diào)用其 onCreate,當(dāng)然 Application 的創(chuàng)建過程只有一次。
- 創(chuàng)建 ContextImpl 對(duì)象并通過 Service 的 attach 方法建立二者之間的聯(lián)系年堆,這個(gè)過程和 Activity 實(shí)際上是類似的,畢竟 Service 和 Activity 都是一個(gè) Context盏浇。
- 最后調(diào)用 Service 的 onCreate 方法并將 Service 對(duì)象存儲(chǔ)到 ActivityThread 中的一個(gè)列表变丧。
- 由于 Service 的 onCreate 方法被執(zhí)行了,這也意味著 Service 已經(jīng)啟動(dòng)了绢掰。除此之外痒蓬, ActivityThread 中還會(huì)通過 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 對(duì)象轉(zhuǎn)化為 ServiceDispatcher.InnerConnection 對(duì)象鲁捏。接著通過 AMS 來(lái)完成 Service 的具體綁定過程,方式是調(diào)用
ActivityManagerNative.getDefault().bindService(**)
给梅。 - AMS 的 bindService 方法調(diào)用 ActiveServices 的 bindServiceLocked 方法,bindServiceLocked 方法再調(diào)用 bringUpServiceLocked双揪,bringUpServiceLocked 又會(huì)調(diào)用 realStartServiceLocked 方法动羽。
- realStartServiceLocked 方法的邏輯和
2.2.1
中的邏輯類似,最終都是通過 ApplicationThread 來(lái)完成 Service 實(shí)例的創(chuàng)建并執(zhí)行其 onCreate 方法拘哨。和 startService 方式不同的是,Service 的綁定過程會(huì)調(diào)用app.thread
的 scheduleBindService 方法姨夹。 - 和上述類似,scheduleBindService 也是通過發(fā)送消息給 Handler H 來(lái)中轉(zhuǎn)的贾虽。
- H 接收到綁定 Service 的消息時(shí)菇肃,調(diào)用 ActivityThread 的 handleBindService 方法來(lái)處理琐谤。
- 在 handleBindService 中,首先根據(jù) Service 的 token 取出 Service 對(duì)象,然后調(diào)用 Service 的 onBind 方法。接著通過 ActivityManagerNative.getDefault() 的 publishService 方法調(diào)用客戶端的 ServiceConnection 中的 onServiceConnection。
3 BroadcastReceiver
BroadcastReceiver 也叫廣播接收者,是一種消息型組件毁欣,用于在不同的組件甚至不同的應(yīng)用之間傳遞消息。 BroadcastReceiver 同樣是用戶無(wú)感知的。
3.1 BroadcastReceiver 的注冊(cè)方式
BroadcastReceiver 的注冊(cè)方式有兩種,靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)。
-
靜態(tài)注冊(cè)
指在 AndroidManifest 中注冊(cè)廣播帕翻,這種廣播在應(yīng)用安裝時(shí)被系統(tǒng)解析,不需要啟動(dòng)應(yīng)用就可以收到相應(yīng)的廣播嘀掸。應(yīng)用的開機(jī)啟動(dòng)就是用到了這類 BroadcastReceiver 來(lái)監(jiān)聽手機(jī)的啟動(dòng)卿拴。注冊(cè)代碼可參考上文的圖。 -
動(dòng)態(tài)注冊(cè)
在代碼中調(diào)用 Context.registerReceiver() 來(lái)實(shí)現(xiàn)注冊(cè)。此類注冊(cè)要在不使用的時(shí)候調(diào)用 Context.unRegisterReceiver() 進(jìn)行反注冊(cè)等浊,否則容易因持有 Context 實(shí)例造成內(nèi)存泄漏过咬。
4. ContextProvider
ContentProvider 是一種數(shù)據(jù)共享型組件衔掸,用于向其它組件甚至其它應(yīng)用共享數(shù)據(jù)。由于只是用來(lái)組件或者應(yīng)用間共享數(shù)據(jù),用戶同樣無(wú)法直接感知。
一個(gè) ContentProvider 組件內(nèi)部要實(shí)現(xiàn)數(shù)據(jù)的增刪改查四種操作仅炊,其內(nèi)部維持著一份數(shù)據(jù)集合桐经。這個(gè)數(shù)據(jù)集合有多種實(shí)現(xiàn)方式,ContentProvider 本身對(duì)其沒有任何要求,常見的使用數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)次氨。
四大組件是否可以開啟多進(jìn)程
上面對(duì)Android的四大組件的作用進(jìn)行了一個(gè)大致的說明薇组,由于篇幅原因罪佳,這里只簡(jiǎn)單描述了下 Activity 和 Service 啟動(dòng)過程,找機(jī)會(huì)再整理下 BroadcastReceiver 和 ContentProvider 的工作過程菩暗。
我們知道,Android 應(yīng)用是可以開啟多個(gè)進(jìn)程的岗屏,就是在 AndroidManifest 中使用 android:process 屬性,比如要給某一 Activity 指定運(yùn)行進(jìn)程咐刨,則在其 <activity> 標(biāo)簽中添加 android:process 屬性即可材原。那么,其它的三種組件是否也可以為其指定運(yùn)行進(jìn)程呢兼搏?也就是說,Android的四大組件是否都可以開啟多進(jìn)程绑莺?
這里我大膽假設(shè)小心求證下谚殊,我先假設(shè)都可以絮记,并且開啟方式都和 Activity 相同,如果不行的話设褐,再根據(jù)問題進(jìn)行相應(yīng)調(diào)整,并最終得出結(jié)論巫玻。
demo:
AndroidManifest中注冊(cè)
<!--Activity注冊(cè)-->
<activity android:name=".SampleActivity"
android:process=":SampleActivity">
<intent-filter>
<action android:name="sampleActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!--Service注冊(cè)-->
<service
android:name=".SampleService"
android:process=":SampleService"/>
<!--BroadcastReceiver靜態(tài)注冊(cè)-->
<receiver
android:name=".SampleReceiver"
android:process=":SampleReceiver">
<intent-filter>
<action android:name="com.cy.receiver.sample"/>
</intent-filter>
</receiver>
<!--ContentProvider注冊(cè)-->
<provider
android:name=".SampleContentProvider"
android:process=":SampleContentProvider"
android:authorities="com.cy.multiprocess.SampleContentProvider"
android:exported="true">
</provider>
MainActivity中啟動(dòng) Activity笛厦、Service言缤,發(fā)送廣播管挟,訪問ContentProvider
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "主進(jìn)程號(hào)為:${getCurProcessName()}")
// 啟動(dòng) Activity
startActivity(Intent(this, SampleActivity::class.java))
// 啟動(dòng) 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 的擴(kuò)展方法:獲取當(dāng)前進(jìn)程號(hào)
*/
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
}
最后在各自生命周期方法中打印進(jìn)程名,運(yùn)行結(jié)果如下:
由圖可知弄捕,每個(gè)組件所運(yùn)行的進(jìn)程id僻孝、進(jìn)程名和主進(jìn)程都不一樣,所以結(jié)論是:Android的四大組件都可以開啟多進(jìn)程守谓。