進階之路 | 奇妙的四大組件之旅

前言

本文已經收錄到我的Github個人博客,歡迎大佬們光臨寒舍:

我的GIthub博客

學習清單:

  • Activity的工作過程
  • Service的工作過程
  • Service的啟動過程
  • Service的綁定過程
  • BroadcastReceiver的工作過程
  • BroadcastReceiver的注冊過程
  • BroadcastReceiver的發送和接收過程
  • ContentProvider的工作過程

一.為什么要學習四大組件?

何為“四大”:

  • Activity
  • Service
  • BroadcastReceiver
  • ContentProvider

談到四大組件,相信在座各位都再熟悉不過了,光聞其名,未見其聲,“四大”二字一出,足見其在安卓系統中的地位,可謂是安卓界的F4。

其地位之崇高,在某種程度上也可以體現他的重要性,所以說,光會使用四大組件還是不能體現我們對他的重視(ai hu)的,我們還要分析其工作過程,能夠更好地理解系統內部的運行機制,從而加深對Android體系結構的認識;同時,四大組件還是面試必問的知識點之一。

綜上,掌握好四大組件相關的知識,對于一個Android開發者來說是非常重要的!

以下內容緊張赤雞,請系好保險帶,我們要開車(hu you)了。— No picture,say a J8!

老司機開車

二.核心知識點歸納

2.1 概述

2.1.1 Activity

  • 類型:展示型組件

  • 作用:展示一個界面并和用戶交互

  • 使用:

    A.需要在AndroidManifest中注冊

    B.需要借助Intent啟動,兩種方式:

  • 顯示Intent:

    Intent intent=new Intent(xxx.this,xxx.class); startActivity(intent);

  • 隱式Intent:

    Intent intent=new Intent(); intent.setAction(xxx); intent.addCategory(xxx); startActivity(intent);

  • 四種啟動模式:
  • standard:標準模式
  • singleTop:棧頂復用模式
  • singleTask:棧內復用模式
  • singleInstance:單實例模式

想了解啟動模式的讀者,可以看下筆者寫的一篇文章:進階之路 | 奇妙的Activity之旅中的2.2部分

  • 通過finish()結束一個Activity

2.1.2 Service

  • 類型:計算型組件

  • 作用:在后臺執行一系列計算任務,耗時的后臺計算建議在單獨的線程中執行

  • 使用:

    A.需要在AndroidManifest中注冊

    B.需要借助Intent啟動:Intent intent = new Intent(xxx.this, xxx.class); startService(intent);

    C.兩種運行狀態:

  • 啟動狀態:通過startService()
  • 綁定狀態:通過bindService()

? D.停止方式:unBindService();stopService();

2.1.3 BroadcastReceiver

  • 類型:消息型組件
  • 作用:在不同的組件乃至不同的應用之間傳遞消息
  • 使用:
  • 兩種注冊方式:

    A.動態注冊:通過Context.registerReceiver()& Context.unRegisterReceiver(),必須要啟動應用才能注冊并接收廣播。

    B.靜態注冊:在AndroidManifest文件中注冊,不需要啟動應用即可接收廣播。

  • 需要借助Intent發送廣播:Intent intent = new Intent("xxx"); sendBroadcast(intent);

  • 四種廣播類型:

    A.普通廣播

    B.有序廣播

    C.本地廣播

    D.粘性廣播

  • 沒有停止概念

2.1.4 ContentProvider

  • 類型:共享型組件
  • 作用:向其他組件乃至其他應用共享數據(安卓IPC的一種方式)
  • 使用:
  • 需要在AndroidManifest中注冊

  • 無需借助Intent啟動

  • 四種操作:注意需要處理好線程同步(因為這些操作運行在Binder線程)

    A.insert():添加數據

    B.update():更新數據

    C.delete():刪除數據

    D.query():查詢數據

  • 無需手動停止

想詳細了解IPC機制的讀者,可以看下筆者寫的一篇文章:進階之路 | 奇妙的 IPC 之旅

2.2 工作過程

差不多該進入今天的主題了,為了逼格,為了高薪,大伙往前沖!

2.2.1 Activity

Activity啟動過程流程圖:

一眼看上去有點暈暈的,墻裂建議配合源碼一起服用,效果極佳,筆者推薦一篇文章:圖解Activity啟動流程,進階高級

啟動過程

Q1:結論:

  • ActivityManagerService、ApplicationThread都是Binder
  • Application的創建也是通過Instrumentation來完成的,這個過程和Activity對象一樣,都是通過類加載器來實現的
  • Activity的啟動過程最終回到ApplicationThread中,通過ApplicationThread.scheduleLaunchActivity() 將啟動Activity的消息發送并交由Handler H處理。
  • Handler H對消息的處理會調用handleLaunchActivity()->performLaunchActivity()得以最終完成Activity的創建和啟動。

Q2:重點類

  • Instrumentation

instrumentationAndroid系統里面的一套控制方法或者”鉤子“。 這些鉤子可以在正常的生命周期(正常是由操作系統控制的)之外控制Android控件的運行;它們同時可以控制Android如何加載應用程序。

  • ActivityManagerService「AMS」

AMS是系統的引導服務,應用進程的啟動、切換和調度、四大組件的啟動和管理都需要AMS的支持。

  • ActivityStackSupervisor:
  • ActivityStackSupervisorAMS中的構造方法中被創建。

  • AMS 通過操作ActivityStackSupervisor來管理Activity

  • ActivityStack:
  • ActivityStack從名稱來看是跟棧相關的類,其實它是一個管理類,用來管理系統所有Activity的各種狀態
  • 它由ActivityStackSupervisor來進行管理的
  • ApplicationThread:
  • ActivityThread的私有內部類,也是一個Binder對象
  • 在此處它是作為IApplicationThread對象的Server端,等待Client端的請求然后進行處理,最大的Client就是AMS

2.2.2 Service

源碼流程分析:Service的工作過程

1.啟動過程:

Service啟動過程

2.綁定過程:

Service綁定過程

結論:

  • ContextImplContext的具體實現,通過Activity.attach()Activity建立關聯。Activity.attach()中還會完成Window的創建并和Activity&Window的關聯,由此事件可傳遞給Window。
  • ActivityServices是一個輔助ActivityManagerService(AMS)進行Service管理的類,包括Service的啟動、綁定和停止。
  • Activity類似的,Service的啟動/綁定過程最終回到ApplicationThread中,通過ActivityThread.handleCreateService()/ActivityThread.handleBindService完成Service的啟動/綁定,注意綁定Service的后續還必須告知客戶端已經成功連接Service的這一流程,由ActivityManagerService.publishService()去完成。

2.2.3 BroadcastReceiver

源碼流程分析:BroadcastReceiver 的工作過程分析

1.注冊

四大組件的靜態注冊都是在應用安裝時由PackageManagerService(PMS)解析注冊,當動態注冊BroadcastReceiver時流程為:

BroadcastReceiver動態注冊

2.發送和接收

發送和接收流程

結論:

  • 動態注冊廣播最終會跨進程交給AMS,并把遠程Receiver( 實際上傳的是IIntentReceiver,是個Binder 對象)和遠程IntentFilter保存起來,完成注冊任務
  • 發送廣播時,系統為intent添加了兩個標記位:
  • FLAG_EXCLUDE_STOPPED_PACKAGES :廣播不會發送給已經停止的APP(系統為所有廣播默認添加該標記)
  • FLAG_INCLUDE_STOPPED_PACKAGES :廣播也會發送到已經停止的APP(兩個標記共存時,以該標記為準
  • 最終在ReceiverDispatcher .performReceive ()里回調了ReceiveronReceive(),使得廣播得以接收并處理

Q2:實現原理:

從實現原理看上,廣播使用了觀察者模式,基于消息的發布/訂閱事件模型

具體實現流程要點粗略概括如下:

  • 廣播接收者BroadcastReceiver通過Binder機制向AMS進行注冊
  • 廣播發送者通過Binder機制向AMS發送廣播
  • AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(一般情況下是Activity)相應的消息循環隊列中
  • 消息循環執行拿到此廣播,回調BroadcastReceiver中的onReceive()方法

2.2.4 ContentProvider

1.啟動流程總概

ContentProvider啟動流程總概

  • 啟動的入口為ActivityThread.main():創建ActivityThread實例并創建主線程消息隊列
  • ActivityThread.attach():遠程調用AMS.attachApplication()并提供ApplicationThread用于和AMS的通信
  • AMS.attachApplication():通過ActivityThread.bindApplication()方法和Handler H來調回ActivityThread.handleBindApplication()
  • ActivityThread.handleBindApplication():先創建Application、再加載ContentProvider、最后回調Application.onCreate()
2.Query過程流程

insert()、delete()update()的實現原理和query()類似,限于篇幅,這里不展開,感興趣的讀者可以主動去探究

源碼流程分析:ContentProvider的工作過程

3gYU1K.jpg

結論:

  • ContentProvidermultiprocess屬性:ContentProvider是否是單例,一般用單例

  • 訪問ContentProvider需要ContentResolver,其真正實現類是ApplicationContentResolver。當ContentProvider所在進程未啟動時,第一次訪問它會觸發ContentProvider的創建以及進程啟動

  • ContentProvider所在的進程啟動時,會同時被啟動并被發布到AMS

注意:ContentProvider.onCreate()Application.onCreate()執行

  • 同樣的,最終通過ActivityThread.handleBindApplication()完成ContentProvider的創建。

三.課堂小測試

恭喜你!已經看完了前面的文章,相信你對四大組件已經有一定深度的了解,下面,進行一下課堂小測試,驗證一下自己的學習成果吧!

Q1:為什么要使用ContentProvider?它和SQL在實現上有什么區別?

  • ContentProvider 屏蔽了數據存儲的細節,內部實現透明化,用戶只需關心URI即可(是否匹配)
  • ContentProvider能實現不同APP的數據共享,SQL只能是自己程序才能訪問
  • ContentProvider還能增刪本地的文件,XML等信息

Q2:Android引入四大組件的用意

這個問題在筆者剛開始學習Android的時候就一直困惑,直到看了一篇Google Android 團隊:Dianne Hackborn發表在Google+上的一篇post譯文

見解:Google Android Framework團隊決定,不要讓一個明確的Main方法作為APP的入口,因為需要讓系統對APP怎樣運行有更多的控制權,在該系統中,用戶永遠不需要考慮開啟和停止一個APP,而把這些事交給系統去管理。所以他們設計了四大組件以作為APP功能的載體和入口

  • Activity

一個APP與用戶交互的入口

  • BroadcastReceiver
  • 一種讓系統在正常的用戶流(user flow)之外,傳遞事件給APP的機制。
  • 最重要的是,因為這是另一個被精心定義的APP的入口,即使APP當前并不在運行,系統也可以將Broadcasts傳遞給APP。
  • Service

APP由于各種各樣的原因需要在后臺運行時,Service就是一個這樣的入口

  • ContentProvider
  • 人們通?;嶠弊鞫允菘獾某橄?,因為有許多的API和支持庫就是這樣使用ContentProvider
  • 但是從系統設計的角度,這并不是ContentProvider的初衷。對于系統來說,ContentProvider實際上是一個入口,用于獲取一個APP內部的公開的被命名的數據項(data items),每個數據項都被一個URI scheme所標識。

如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力

本文參考鏈接:

posted @ 2020-03-03 09:09  許朋友愛玩  閱讀(...)  評論(...編輯  收藏