1. 程式人生 > >進階之路 | 奇妙的四大元件之旅

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

前言

本文已經收錄到我的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:結論:

  • ActivityManagerServiceApplicationThread都是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.啟動過程:

2.繫結過程:

結論:

  • 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時流程為:

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.啟動流程總概

  • 啟動的入口為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的工作過程

結論:

  • 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所標識。

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

本文參考連結:

  • 《Android 開發藝術探索》
  • 進階之路 | 奇妙的Activity之旅
  • 進階之路 | 奇妙的 IPC 之旅
  • 圖解Activity啟動流程,進階高階
  • Service的工作過程
  • BroadcastReceiver 的工作過程分析
  • ContentProvider的工作過程
  • 關於Android四大元件最權威最深刻最準確的解讀
  • Android開發藝術探索讀書筆記(三)
  • 要點提煉|開發藝術之四大元件
  • 「Android」四大元件,你真的都掌握了?
  • "ContentProvider和資料庫"的區別和聯絡專題