1.概述
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的時候有如下要注意的點
圖片
- 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才進行繪製
- 1.AT.performLaunchActivity:這個方法有以下操作
- 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中進行的操作將會執行。
- 1.SR.thread.scheduleCreateService():thread就是APT,這裡會將呼叫轉到app程式,但是當前的程式還會繼續執行,這裡就到了app執行緒的APT,這個方法裡有以下操作
- 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.元件初始化會建立的物件:
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使用場景
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
來源:簡書