本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。我的微信號:a1018998632,交流qq羣:859640274android
1、概述
閱讀須知git
- 1.文章中的縮寫指代的意思:CI——>ContextImpl、AT——>ActivityThread、LA——>LoadedApk、CR——>ContentResolver、PM——>PackageManager、SP——>SharedPreferences、APT——>ApplicationThread、AMS——>ActivityManagerService、PR——>ProcessRecord、AR——>ActivityRecord、AS——>ActiveServices、SR——>ServicecRecord。
- 2.文章中的變量 mXXX(yyy),括號中 yyy 表示該變量的類型。
- 3.文章中的方法 xxx(yyy),括號中 yyy 表示方法須要傳入的變量類型。
2、用處
- 1.Context的實現類有不少,可是 CI 是惟一作具體工做的,其餘實現都是對 CI 作代理。
- 2.CI 中有一些成員對象,先來看看這些對象的用處:
- 1.mSharedPrefsPaths、sSharedPrefsCache:這兩個對象是用於獲取 SharedPreferences 的,在我前一篇博客裏面有講到。全面剖析SharedPreferences。
- 2.mMainThread(AT):這個對象是一個 App 進程的主線程,一個 App 的 Framework 層就是從這裏啓動的。
- 3.mPackageInfo(LA):在 AT 初始化 App 的主線程的時候,會將 Apk 加載到內存中,Apk 在內存中就是以這個對象的形式存在的,該對象能夠加載 Apk 的資源和 Dex 文件。
- 4.mUser(UserHandle):多用戶相關。
- 5.mContentResolver( ApplicationContentResolver):繼承於 CR,主要功能是經過 Uri 來獲取文件、數據庫、Asset、Res 等數據,還有就是經過 ContentProvider 來獲取其餘應用和本機數據。
- 6.mResourcesManager(ResourcesManager):單例,由於一個 Apk 不一樣機型的適配資源,因此用來加載Resource對象,以保證一個 App 中全部的 CI 使用的都是同一份資源。
- 7.mResources(Resources):獲取 Apk 中 Res 資源的對象。
- 8.mOuterContext(Context):用於指向代理本對象的 Context,例如 Activity、Service 等。
- 9.mTheme(Resources.Theme):主題
- 10.mPackageManager(PM):包管理類,不只能夠獲取咱們apk包的信息,還能獲取本機apk包的信息。
- 3.CI 中有不少 Api,我將這些 Api 歸了一下類
- 1.獲取成員對象:即獲取上面我列出來的那些對象,這些對象獲取到了以後又有更多 Api 暴露出來,在這裏 CI 至關於作了一個聚合。最經常使用的就是 getResource() 了。
- 2.獲取成員對象的成員對象:即爲了方便,CI 封裝了一些經常使用的獲取成員對象中的信息的方法。例如getPackageName(),是經過 PM 來獲取的。
- 3.關於 SP 的操做:咱們知道 SP 其實就是 Xml 文件,因此這裏的操做有:獲取、移動、刪除。
- 4.文件操做:增刪移文件、打開文件流、獲取 App 私有文件夾地址等等。
- 5.數據庫操做:咱們知道 Sqlite 實際上是一種文件型數據庫,因此有:打開、建立、移動、刪除、獲取數據庫文件路徑,等操做。
- 6.壁紙相關操做:這個不是成員變量提供的,WallpaperManager 是系統 Service 一種,因此是SystemService 提供的。
- 7.啓動Activity:包括通常啓動 Acitivyt、多用戶啓動 Activity、啓動多個 Activity。
- 8.廣播操做:發送普通廣播、發送須要權限的廣播、發送有序廣播、發送粘連廣播、發送有序粘連廣播、多用戶廣播、移除各類廣播、註冊各類廣播、取消註冊各類廣播。
- 9.Service 操做:啓動、中止、重啓、綁定、解綁、獲取系統服務以及多用戶操做。
- 10.權限操做:檢查本 App 是否有某種權限、檢查某 App 是否有某種權限、檢查Uri權限、授予權限等等。
- 11.各類狀況下建立 CI,這個比較重要:
- 1.createSystemContext(AT):在 SystemService 建立的時候爲其建立一個 CI
- 2.create App Context(AT,LA):在 App lication/Service建立的時候爲其建立一個 CI
- 3.createActivityContext(ActivityThread mainThread,LA, IBinder, int displayId,Configuration):在 Activity 建立的時候爲其建立一個 CI。
3、四大組件以及 App lication初始化與Context的關係
在瞭解 Binder 的時候有以下要注意的點程序員
1.Activity初始化
-
1.CI.startActivity():將調用交給 Instrumentation (負責監控 Activity 和 AMS 的交互,全部 Activity 的本地進程到遠端進程的調用轉換都是其來執行),數據庫
-
2.Instrumentation.execStartActivity():傳入一個 APT 而後經過 Binder 機制將調用過程轉移到(後稱AMS)所AMS 在的系統服務進程,本地主線程則繼續運行,不過本地主線程後續也沒別的操做了。接下來就是本地的MessageQueue 等待 AMS 服務運行完畢,發送消息將 Activity 的啓動從新交給本地主線程。編程
-
3.AMS.startActivity():從這裏開始會調用會按順序在 ActivityStarter、ActivityStackSupervisor、ActivityStack 這三個類之間進行調用,主要會進行下面這些操做,不按順序:微信
- 1.對 Intent 的內容進行解析,獲取目標 Activity 的信息。
- 2.根據傳入的 APT 獲取被調用 App 的信息封裝成 PR。
- 3.將一、2和其餘信息結合,將源 Activity 和目標 Activity 封裝成兩個 AR
- 4.解析 Activity 的啓動模式 和當前的 Activity 棧狀態,判斷是否須要建立棧和 Activity 。(注意這裏的AR 有着 App 中的 Activity 的所有信息,能夠將其當作系統服務裏面的 Activity 的化身)
- 5.獲取到 Activity 和 Activity 棧以後,接下來要判斷是否要將當前 Activity 執行 onPause() 以及讓使用Binder 執行目標 Activity 的 onCreate() 和 onResume(注意這裏 onStart() 會在 Binder 遠程調用onCreate() 的時候直接執行),這裏 AMS 進程會使用 APT 調用 App 進程的 Activity 執行相應的生命週期。
- 6.在 AMS 中前置準備一切就緒以後,會經過 APT 使用 Handler 的形式調用到 App 進程的 AT 中。
- 7.最終到了ActivityStackSupervisor.realStartActivityLocked()中會使用 APT 將調用交給 App 進程——>AT.scheduleLaunchActivity()——>AT.handleLaunchActivity()
-
4.AT.handleLaunchActivity():將有如下操做ide
- 1.AT.performLaunchActivity:這個方法有如下操做
- 1.建立對象LA(一個 App 只加載一次)
- 2.建立對象 Activity
- 3.建立對象 App lication(一個 App ,只建立一次)
- 4.**建立對象 CI **:CI.createActivityContext()
- 5.Application、CI都 attach 到 Activity 對象:Activity.attach()
- 6.執行 onCreate():Instrumentation.callActivityOnCreate()——>Activity.performCreate()——>Activity.onCreate()
- 7.執行onStart():AT.performLaunchActivity——>Activity.performStart()——>Instrumentation.callActivityOnStart()——>Activity.onStart()
- 2.AT.handleResumeActivity()
- 1.AT.performResumeActivity()——>Activity.performResume()——>Instrumentation.callActivityOnResume()——>Activity.onResume()
- 2.Activity.makeVisible()——>WindowManager.addView():開始進行View的繪製流程。
- 3.從上面咱們能夠總結一下:在 AMS 將調用交給 App 進程以後,三個生命週期都是在 App 進程被回調的,而且在 onResume() 以後View才進行繪製
2.Service初始化
- 1.CI.startService()——>CI.startServiceCommon():在這裏傳入一個 APT,相似 Activity 啓動時的第二步,將調用過程轉移到 AMS 中,本地主線程繼續運行,等待 APT 從 AMS 進程將調用轉移到本地主線程中。
- 2.AMS.startService():到了 AMS 進程以後,Service 的啓動就會全權交給 AS(這是 AMS 用來管理 Service 的成員變量)
- 3.AS.startServiceLocked():這裏作了如下操做
- 1.根據傳入的 APT 獲取被調用 App 的信息封裝成 PR
- 2.解析 Intent 等參數獲取到 Service 的信息,封裝成 SR(這個類能夠看作是 Service 在系統服務的化身,記錄了 Service 的一切信息)
- 3.再進過一系列調用:AS.startServiceInnerLocked()——>AS.bringUpServiceLocked()——>AS.realStartServiceLocked() 到這裏纔是真正在 App 進程啓動 Service 的流程。
- 4.AS.realStartServiceLocked():這裏會有如下操做:
- 1.SR.APT.scheduleCreateService():這裏會將調用轉到 App 進程,可是當前的進程還會繼續執行,這裏就到了 App 線程的APT,這個方法裏有如下操做
- 1.經過 Handler 轉到 AT.handleCreateService()
- 2.建立對象 LA(一個 App 只加載一次)
- 3.建立對象 Service
- 4.建立對象 CI
- 5.建立對象 Application(一個 App 只建立一次)
- 6.Application、CI分別 attach 到 Service 對象
- 7.執行 Service.onCreate() 回調
- 8.此時 Service 已經啓動了
- 2.AS.sendServiceArgsLocked()——>SR. App.APT.scheduleServiceArgs():這裏就轉到了 App 進程的 APT 中,這裏會有如下操做:
- 1.APT.scheduleServiceArgs()
- 2.AT.handleServiceArgs()
- 3.Service.onStartCommand()
- 4.此時咱們須要在 Service 中進行的操做將會執行。
3.ContentProvider初始化
- 1.AT.main()——>AT.attach()——>AMS.attach App lication():傳入一個 APT,調用轉到了 AMS 進程
- 2.AMS.attachApplicationLocked():獲取到 ApplicationInfo 和 ProviderInfo 列表以後經過 APT 將調用轉回 App 進程。
- 3.APT.bindApplication()——>AT.handleBindApplication()——>AT.installContentProviders():到這裏以後將會循環初始化 ContentProvider。
- 4.AT.installProvider():這個方法裏面有如下操做
- 1.建立對象LA:CI.createPackageContext()中
- 2.建立對象CI:CI.createPackageContext()中
- 3.建立對象ContentProvider:ClassLoader建立
- 4.CI attach到ContentProvider對象:ContentProvider.attachInfo()中
- 5.執行onCreate回調:ContentProvider.attachInfo()中
4.BroadCastReceiver靜態初始化
由於動態廣播的註冊時進程已建立, 基本對象已建立完成,只須要回調BroadcastReceiver 的 onReceive() 方法便可,因此這裏不分析post
4、四大組件以及 App lication綁定Context的方法
由上一節咱們能夠知道,四大組件以及 App lication在初始化的時候都會進行Context的綁定或者建立,這節就來說講各個組件是如何對context進程賦值的。
- 1.Activity:
- 1.AT.performLaunchActivity()
- 2.AT.createBaseContextForActivity(ActivityClientRecord , Activity)
- 3.CI.createActivityContext(ActivityThread , LoadedApk , IBinder , int displayId , Configuration)
- 4.CI():被賦值了 ActivityThread、LoadedApk、IBinder activityToken、Configuration
- 2.Service/ App lication:
- 1.AT.handleCreateService()
- 2.CI.create App Context(ActivityThread , LoadedApk)
- 3.new CI():被賦值了 ActivityThread、LoadedApk
- 3.BroadCastReceiver:在AT.handleReceiver()中直接獲取 App lication的Context,其自身並不建立Context
- 4.ContentProvider:
- 1.AT.installProvider()
- 2.Context.createPackageContext()——>CI.createPackageContext()——>CI.createPackageContextAsUser():這裏是經過一個 App lication的Context建立的Context,因此能夠看作是 App lication的Context的一個複製。
5、總結
1.組件初始化會建立的對象
- 1.LoadedApk:全部組件在初始化的時候,若是LA沒被初始化都會初始化一遍
- 2.Context:
- 1.只有Activity的CI有上一個Activity的Token
- 2.Receiver的Context是繼承於ContextWr App er 的 ReceiverRestrictedContext,不可綁定Service。
-
- App lication:
- 1.Receiver使用的Context是ReceiverRestrictedContext包裝的 App lication的Context,因此其能夠經過Context獲取到 App lication
- 2.ContentProvider通常是在 App 初始化的時候在初始化 App lication的過程當中加載的,此時 App lication會被加載。可是若是是多個 App 共享進程,第二個 App 由ContentProvider調起,那麼 App lication不會被初始化。
2.Context使用場景
說明: (圖中第一列表明不一樣的 Context, √表明容許在該 Context 執行相應的操做; ×表明不容許; -表明分狀況討論)
- 1.當 Context 爲 Receiver的狀況下:
- 1.不容許執行 bindService() 操做, 因爲限制性上下文(ReceiverRestrictedContext)所決定的,會直接拋出異常.
- 2.registerReceiver 是否容許取決於 receiver;
- 3.當 receiver == null 用於獲取 sticky 廣播, 容許使用。不然不容許使用registerReceiver。
- 2.縱向來看 startActivity 操做
- 1.當爲 ActivityContext 則可直接使用;
- 2.當爲其餘 Context, 要啓動的 Activity 不屬於任何 Activity 棧,因此必須帶上 FLAG_ACTIVITY_NEW_TASK flags 才能使用
3.getApplication() 和 getApplicationContext()
絕大多數狀況下, getApplication() 和 getApplicationContext() 這兩個方法徹底一致, 返回值也相同; 那麼二者到底有什麼區別呢? 真正理解這個問題的人很是少. 接下來完全地回答下這個問題:
- 1.getApplicationContext() 這個的存在是 Android 歷史緣由. 咱們都知道 getApplication() 只存在於 Activity 和Service 對象,那麼對於 BroadcastReceiver 和 ContentProvider 卻沒法獲取 Application, 這時就須要一個能在 Context 上下文直接使用的方法, 那即是 getApplicationContext().
- 2.對於 Activity/Service 來講, getApplication() 和 getApplicationContext() 的返回值徹底相同,除非廠商修改過接口。
- 3.BroadcastReceiver 在 onReceive 的過程,能使用 getBaseContext().getApplicationContext 獲取所在 App lication,而沒法使用 getApplication;
- 4.ContentProvider 能使用 getContext().getApplicationContext() 獲取所在 Application. 絕大多數狀況下沒有問題,可是有可能會出現空指針的問題。狀況以下:當同一個進程有多個 Apk 的狀況下, 對於第二個 Apk 是由Provider 方式拉起的, 前面介紹過 Provider 建立過程並不會初始化所在 Application, 此時執 getContext().get ApplicationContext() 返回的結果即是 NULL,因此對於這種狀況要作好判空。
參考文章
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。