四大組件以及Application和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.將一、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進程賦值的。sql

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

  • 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()這兩個方法徹底一致, 返回值也相同; 那麼二者到底有什麼區別呢? 真正理解這個問題的人很是少. 接下來完全地回答下這個問題:api

  • 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/f499afd8d0abapp

閱讀更多

近3年BAT面試真題整理合集ide

** 一招教你讀懂JVM和Dalvik之間的區別**學習

金9銀10的面試黃金季節,分享幾個重要的面試題線程

kotlin學習筆記-異常好玩的list集合總結3d

相信本身,沒有作不到的,只有想不到的

在這裏得到的不只僅是技術!代理

image
相關文章
相關標籤/搜索