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

chamwarren發表於2018-11-28

1.概述

四大元件以及Application和Context的全面理解
Context抽象結構

2.用處

  • 1.Context的實現類有很多,但是ContextImpl(後稱CI)是唯一做具體工作的,其他實現都是對CI做代理。
  • 2.CI中有一些成員物件,先來看看這些物件的用處
    • 1.mSharedPrefsPaths(ArrayMap<String, File>)、sSharedPrefsCache(ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>>):這兩個物件是用於獲取SharedPreferences的,在我前一篇部落格裡面有講到。全面剖析SharedPreferences
    • 2.mMainThread(ActivityThread(後稱AT)):這個物件是一個app程式的主執行緒,一個app的framework層就是從這裡啟動的。
    • 3.mPackageInfo(LoadedApk(後稱LA)):在AT初始化app的主執行緒的時候,會將APK載入到記憶體中,apk在記憶體中就是以這個物件的形式存在的,該物件可以載入apk的資源和dex檔案。
    • 4.mUser(UserHandle):多使用者相關
    • 5.mContentResolver(ApplicationContentResolver):繼承於ContentResolver(後稱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(PackageManager(後稱PM)):包管理類,不僅可以獲取我們apk包的資訊,還能獲取本機apk包的資訊。
  • 3.CI中有很多api,我將這些api歸了一下類
    • 1.獲取成員物件:即獲取上面我列出來的那些物件,這些物件獲取到了之後又有更多api暴露出來,在這裡CI相當於做了一個聚合。最常用的就是getResource()了。
    • 2.獲取成員物件的成員物件:即為了方便,CI封裝了一些常用的獲取成員物件中的資訊的方法。例如getPackageName(),是通過PM來獲取的。
    • 3.關於SharedPreferences(後稱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(ActivityThread mainThread):在SystemService建立的時候為其建立一個CI
      • 2.createAppContext(ActivityThread mainThread, LoadedApk packageInfo):在Application/Service建立的時候為其建立一個CI
      • 3.createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration):在Activity建立的時候為其建立一個CI。

3.四大元件以及Application初始化與Context的關係

在瞭解Binder的時候有如下要注意的點

四大元件以及Application和Context的全面理解
圖片

  • 1.Activity初始化:
    • 1.CI.startActivity():將呼叫交給Instrumentation(負責監控Activity和AMS的互動,所有Activity的本地程式到遠端程式的呼叫轉換都是其來執行),
    • 2.Instrumentation.execStartActivity():傳入一個ApplicationThread(後稱APT)然後通過Binder機制將呼叫過程轉移到ActivityManagerService(後稱AMS)所在的系統服務程式,本地主執行緒則繼續執行,不過本地主執行緒後續也沒別的操作了,接下來就是本地的MessageQueue等待AMS服務執行完畢,傳送訊息將Activity的啟動重新交給本地主執行緒。
    • 3.AMS.startActivity():從這裡開始會呼叫會按順序在 ActivityStarter、ActivityStackSupervisor、ActivityStack 這三個類之間進行呼叫,主要會進行下面這些操作,不按順序:
      • 1.對Intent的內容進行解析,獲取目標Activity的資訊。
      • 2.根據傳入的APT獲取被呼叫app的資訊封裝成 ProcessRecord(後稱PR)。
      • 3.將1、2和其他資訊結合,將源Activity和目標Activity封裝成兩個ActivityRecord(後稱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():將有以下操作
      • 1.AT.performLaunchActivity:這個方法有以下操作
        • 1.建立物件LoadedApk(後稱LA,一個app只載入一次)
        • 2.建立物件Activity
        • 3.建立物件Application(一個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的啟動就會全權交給ActiveServices(後稱AS,這是AMS用來管理Service的成員變數)
    • 3.AS.startServiceLocked():這裡做了以下操作
      • 1.根據傳入的APT獲取被呼叫app的資訊封裝成 PR
      • 2.解析Intent等引數獲取到Service的資訊,封裝成ServicecRecord(後稱SR,這個類可以看做是Service在系統服務的化身,記錄了Service的一切資訊)
      • 3.再進過一系列呼叫:AS.startServiceInnerLocked()-->AS.bringUpServiceLocked()-->AS.realStartServiceLocked()到這裡才是真正在app程式啟動Service的流程。
    • 4.AS.realStartServiceLocked():這裡會有以下操作:
      • 1.SR.thread.scheduleCreateService():thread就是APT,這裡會將呼叫轉到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.thread.scheduleServiceArgs():這裡就轉到了app程式的APT中,這裡會有以下操作:
        • 1.APT.scheduleServiceArgs()
        • 2.AT.handleServiceArgs()
        • 3.Service.onStartCommand()
        • 4.此時我們需要在Service中進行的操作將會執行。
  • 3.ContentProvider初始化:
    • 1.AT.main()-->AT.attach()-->AMS.attachApplication():傳入一個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()方法即可,所以這裡不分析
    • 1.當收到廣播時會呼叫AT.handleReceiver()
    • 2.建立物件LA(一個app只載入一次)
    • 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.四大元件以及Application繫結Context的方法

由上一節我們可以知道,四大元件以及Application在初始化的時候都會進行Context的繫結或者建立,這節就來講講各個元件是如何對context程式賦值的。

  • 1.Activity:
    • 1.AT.performLaunchActivity()
    • 2.AT.createBaseContextForActivity(ActivityClientRecord , Activity)
    • 3.ContextImpl.createActivityContext(ActivityThread , LoadedApk , IBinder , int displayId , Configuration)
    • 4.ContextImpl():被賦值了 ActivityThread、LoadedApk、IBinder activityToken、Configuration
  • 2.Service/Application:
    • 1.AT.handleCreateService()
    • 2.ContextImpl.createAppContext(ActivityThread , LoadedApk)
    • 3.new ContextImpl():被賦值了 ActivityThread、LoadedApk
  • 3.BroadCastReceiver:在AT.handleReceiver()中直接獲取Application的Context,其自身並不建立Context
  • 4.ContentProvider:
    • 1.AT.installProvider()
    • 2.Context.createPackageContext()-->CI.createPackageContext()-->CI.createPackageContextAsUser():這裡是通過一個Application的Context建立的Context,所以可以看做是Application的Context的一個複製。

5.總結

1.元件初始化會建立的物件:

四大元件以及Application和Context的全面理解
image.png
  • 1.LoadedApk:所有元件在初始化的時候,如果LA沒被初始化都會初始化一遍
  • 2.Context:
    • 1.只有Activity的CI有上一個Activity的Token
    • 2.Receiver的Context是繼承於ContextWrapper 的 ReceiverRestrictedContext,不可繫結Service。
  • 3.Application:
    • 1.Receiver使用的Context是ReceiverRestrictedContext包裝的Application的Context,所以其可以通過Context獲取到Application
    • 2.ContentProvider一般是在app初始化的時候在初始化Application的過程中載入的,此時Application會被載入。但是如果是多個app共享程式,第二個app由ContentProvider調起,那麼Application不會被初始化。

2.Context使用場景

四大元件以及Application和Context的全面理解
image.png

說明: (圖中第一列代表不同的Context, √代表允許在該Context執行相應的操作; ×代表不允許; -代表分情況討論)

  • 1.當Context為Receiver的情況下:
    • 1.不允許執行bindService()操作, 由於限制性上下文(ReceiverRestrictedContext)所決定的,會直接丟擲異常.
    • 2.registerReceiver是否允許取決於receiver;
    • 3.當receiver == null用於獲取sticky廣播, 允許使用;否則不允許使用registerReceiver;
  • 2.縱向來看startActivity操作
    • 1.當為Activity Context則可直接使用;
    • 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獲取所在Application, 而無法使用getApplication;
  • 4.ContentProvider能使用getContext().getApplicationContext()獲取所在Application. 絕大多數情況下沒有問題, 但是有可能會出現空指標的問題, 情況如下:當同一個程式有多個apk的情況下, 對於第二個apk是由provider方式拉起的, 前面介紹過provider建立過程並不會初始化所在application, 此時執getContext().getApplicationContext()返回的結果便是NULL. 所以對於這種情況要做好判空.


作者:何時夕
連結:https://www.jianshu.com/p/f499afd8d0ab
來源:簡書


相關文章