四大組件以及 Application和Context的全面理解

本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。我的微信號:a1018998632,交流qq羣:859640274android

1、概述

Context抽象結構

閱讀須知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

  • 1.當收到廣播時會調用AT.handleReceiver()線程

  • 2.建立對象LA(一個 App 只加載一次)3d

  • 3.建立對象BroadcastReceiver

  • 4.建立對象 Application

  • 5.從建立的 Application中獲取 CI

  • 6.執行 onReceive() 回調

  • 5.Application初始化:由上面四個組件的初始化咱們能夠知道,當 App 還沒啓動的時候喚醒任意組件都會建立一個 Application,而這裏分析的是正常狀況啓動一個 App 的時候建立 Application的流程。

    • 1.這裏的流程其實就是包含了ContentProvider 初始化的流程,因此前面都差很少
    • 2.最後到了AT.handleBindApplication()中,這裏有如下操做:
      • 1.建立對象 LA
      • 2.建立對象 CI
      • 3.建立對象 Instrumentation
      • 4.建立對象 Application;
      • 5.安裝 providers
      • 6.執行 Create 回調

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.組件初始化會建立的對象

image.png

  • 1.LoadedApk:全部組件在初始化的時候,若是LA沒被初始化都會初始化一遍
  • 2.Context:
    • 1.只有Activity的CI有上一個Activity的Token
    • 2.Receiver的Context是繼承於ContextWr App er 的 ReceiverRestrictedContext,不可綁定Service。
    1. 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使用場景

image.png

說明: (圖中第一列表明不一樣的 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,因此對於這種狀況要作好判空。

參考文章

不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。

世界上有意思的事
相關文章
相關標籤/搜索