天天看點

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

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才進行繪制
  • 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()
    • 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使用場景

說明: (圖中第一列代表不同的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. 是以對于這種情況要做好判空.
參考文章: Context全面了解