Android四大組件的作用以及是否可以開啟多進程

Android四大組件是指 Activity、Service(服務)、BroadcastReceiver(廣播)、ContentProvider。

在注冊方面,Activity、Service、ContentProvider 必須在 AndroidManifest 中注冊,而 BroadcastReceiver 既可以在 AndroidManifest 中注冊,也可以通過代碼來注冊。如下圖所示:


Android四大組件注冊方法

在調(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) 方法開始:

  1. startActivity(ForResult) 方法有多種重載方式,但它們最終都會調(diào)用 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)
  2. 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)部類。
  3. 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 中。
  4. AMS 的 startActivity 方法調(diào)用 ActivityStackSupervisor 的 startActivityMayWait 方法。startActivityMayWait 中又調(diào)用 startActivityLocked 方法。startActivityLocked 又調(diào)用了 startActivityUncheckedLocked 方法。接著 startActivityUncheckedLocked 又調(diào)用了 ActivityStack 的 resumeTopActivitiesLocked 方法。
  5. 這個時候啟動過程已經(jīng)從 ActivityStackSupervisor 轉移到了 ActivityStack,ActivityStack 的 resumeTopActivitiesLocked 又會調(diào)用 resumeTopActivityInnerLocked 方法,resumeTopActivityInnerLocked 又調(diào)用了 ActivityStackSupervisor 的 startSpecificActivityLocked 方法。
  6. 此時,啟動過程又回到了 ActivityStackSupervisor,在 startSpecificActivityLocked 方法中,調(diào)用 realStartActivityLocked 方法。
  7. 在 realStartActivityLocked 中,有這樣一段代碼 app.thread.scheduleLaunchActivity(**),app.thread 的類型為 IApplicationThread,其實現(xiàn)者是 ActivityThread 中的內(nèi)部類 ApplicationThread。饒了一大圈,Activity 的啟動過程最終回到了 ApplicationThread 中,ApplicationThread 通過 scheduleLaunchActivity 方法來啟動 Activity。
  8. scheduleLaunchActivity 做的事情也很簡單,就是發(fā)送一個啟動 Activity 的消息交由 Handler 處理,這個 Handler 有一個很簡潔的名字:H。
  9. H 對 Activity 啟動消息的處理是調(diào)用 ActivityThread 的 handleLaunchActivity 方法來實現(xiàn) Activity 的啟動。
  10. 最終,在 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 方法開始:

  1. startService 方法調(diào)用 ContextImpl 的 startService 方法。
  2. 在 ContextImpl 中,startService 方法會調(diào)用 startServiceCommon 方法,而 startServiceCommon 方法又通過調(diào)用 ActivityManagerNative.getDefault().startService(**) 來啟動一個 Service。對于 ActivityManagerNative.getDefault() 這個對象,在 Activity 的啟動過程中第 3 點分析了,它實際上就是 AMS (ActivityManagerService),這里就不在重復說明了。需要注意的是,通過 AMS 來啟動 Service 的行為是一個遠程過程調(diào)用。
  3. 在 AMS 的 startService 中,會通過 ActiveServices 對象來完成 Service 后續(xù)的啟動過程。也就是調(diào)用 ActiveServices 的 startServiceLocked 方法。
  4. 在 ActiveServices 的 startServiceLocked 方法尾部會調(diào)用 startServiceInnerLocked 方法,startServiceInnerLocked 又調(diào)用了 realStartServiceLocked 方法。
  5. 在 realStartServiceLocked 方法中,首先通過 app.thread 的 scheduleCreateService 方法來創(chuàng)建并調(diào)用其 onCreate,接著再調(diào)用 sendServiceArgsLocked 方法來調(diào)用 Service 的其它方法,比如 onStartCommand,這兩個過程均是進程間通信。
  6. 由 Activity 的啟動過程第 10 點可知,app.thread 是 ApplicationThread 的實例,也就是調(diào)用了 ApplicationThread 的 scheduleCreateService 方法來創(chuàng)建 Service。這個過程和 Activity 的啟動過程是類似的,都是通過發(fā)送消息給 Handler H 來完成。
  7. H 通過 ActivityThread 的 handleCreateService 方法來完成 Service 的最終啟動。handleCreateService 主要完成如下幾件事:
    1. 通過類加載器創(chuàng)建 Service 的實例。
    2. 創(chuàng)建 Application 對象并調(diào)用其 onCreate,當然 Application 的創(chuàng)建過程只有一次。
    3. 創(chuàng)建 ContextImpl 對象并通過 Service 的 attach 方法建立二者之間的聯(lián)系,這個過程和 Activity 實際上是類似的,畢竟 Service 和 Activity 都是一個 Context。
    4. 最后調(diào)用 Service 的 onCreate 方法并將 Service 對象存儲到 ActivityThread 中的一個列表。
    5. 由于 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 開始的:

  1. bindService 方法調(diào)用 ContextImpl 的 bindService 方法,然后 bindService 方法調(diào)用 bindServiceCommon 方法。
  2. 在 bindServiceCommon 中,首先將客戶端的 ServiceConnection 對象轉化為 ServiceDispatcher.InnerConnection 對象。接著通過 AMS 來完成 Service 的具體綁定過程,方式是調(diào)用ActivityManagerNative.getDefault().bindService(**)。
  3. AMS 的 bindService 方法調(diào)用 ActiveServices 的 bindServiceLocked 方法,bindServiceLocked 方法再調(diào)用 bringUpServiceLocked,bringUpServiceLocked 又會調(diào)用 realStartServiceLocked 方法。
  4. realStartServiceLocked 方法的邏輯和2.2.1中的邏輯類似,最終都是通過 ApplicationThread 來完成 Service 實例的創(chuàng)建并執(zhí)行其 onCreate 方法。和 startService 方式不同的是,Service 的綁定過程會調(diào)用 app.thread的 scheduleBindService 方法。
  5. 和上述類似,scheduleBindService 也是通過發(fā)送消息給 Handler H 來中轉的。
  6. H 接收到綁定 Service 的消息時,調(diào)用 ActivityThread 的 handleBindService 方法來處理。
  7. 在 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
}

最后在各自生命周期方法中打印進程名,運行結果如下:

Android四大組件開啟多進程運行結果

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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容