本文首發(fā)于微信公眾號——世界上有意思的事,搬運轉(zhuǎn)載請注明出處,否則將追究版權(quán)責(zé)任妈橄。微信號:a1018998632庶近,交流qq群:859640274
一、概述
閱讀須知
- 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 表示方法需要傳入的變量類型圃泡。
二碟案、用處
- 1.Context的實現(xiàn)類有很多,但是 CI 是唯一做具體工作的颇蜡,其他實現(xiàn)都是對 CI 做代理价说。
- 2.CI 中有一些成員對象,先來看看這些對象的用處:
- 1.mSharedPrefsPaths风秤、sSharedPrefsCache:這兩個對象是用于獲取 SharedPreferences 的鳖目,在我前一篇博客里面有講到。全面剖析SharedPreferences唁情。
- 2.mMainThread(AT):這個對象是一個 App 進程的主線程疑苔,一個 App 的 Framework 層就是從這里啟動的甫匹。
- 3.mPackageInfo(LA):在 AT 初始化 App 的主線程的時候甸鸟,會將 Apk 加載到內(nèi)存中,Apk 在內(nèi)存中就是以這個對象的形式存在的兵迅,該對象可以加載 Apk 的資源和 Dex 文件抢韭。
- 4.mUser(UserHandle):多用戶相關(guān)。
- 5.mContentResolver( ApplicationContentResolver):繼承于 CR恍箭,主要功能是通過 Uri 來獲取文件挪钓、數(shù)據(jù)庫边锁、Asset蓬坡、Res 等數(shù)據(jù)苛败,還有就是通過 ContentProvider 來獲取其他應(yīng)用和本機數(shù)據(jù)。
- 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 相當(dāng)于做了一個聚合把兔。最常用的就是 getResource() 了沪伙。
- 2.獲取成員對象的成員對象:即為了方便,CI 封裝了一些常用的獲取成員對象中的信息的方法县好。例如getPackageName()围橡,是通過 PM 來獲取的。
- 3.關(guān)于 SP 的操作:我們知道 SP 其實就是 Xml 文件缕贡,所以這里的操作有:獲取翁授、移動、刪除晾咪。
- 4.文件操作:增刪移文件收擦、打開文件流、獲取 App 私有文件夾地址等等谍倦。
- 5.數(shù)據(jù)庫操作:我們知道 Sqlite 其實是一種文件型數(shù)據(jù)庫塞赂,所以有:打開、創(chuàng)建昼蛀、移動宴猾、刪除、獲取數(shù)據(jù)庫文件路徑叼旋,等操作仇哆。
- 6.壁紙相關(guān)操作:這個不是成員變量提供的,WallpaperManager 是系統(tǒng) Service 一種夫植,所以是SystemService 提供的讹剔。
- 7.啟動Activity:包括一般啟動 Acitivyt、多用戶啟動 Activity、啟動多個 Activity延欠。
- 8.廣播操作:發(fā)送普通廣播陌兑、發(fā)送需要權(quán)限的廣播、發(fā)送有序廣播由捎、發(fā)送粘連廣播诀紊、發(fā)送有序粘連廣播、多用戶廣播隅俘、移除各種廣播邻奠、注冊各種廣播、取消注冊各種廣播为居。
- 9.Service 操作:啟動碌宴、停止、重啟蒙畴、綁定贰镣、解綁、獲取系統(tǒng)服務(wù)以及多用戶操作膳凝。
- 10.權(quán)限操作:檢查本 App 是否有某種權(quán)限碑隆、檢查某 App 是否有某種權(quán)限、檢查Uri權(quán)限蹬音、授予權(quán)限等等上煤。
- 11.各種情況下創(chuàng)建 CI,這個比較重要:
- 1.createSystemContext(AT):在 SystemService 創(chuàng)建的時候為其創(chuàng)建一個 CI
- 2.create App Context(AT著淆,LA):在 App lication/Service創(chuàng)建的時候為其創(chuàng)建一個 CI
- 3.createActivityContext(ActivityThread mainThread劫狠,LA, IBinder永部, int displayId独泞,Configuration):在 Activity 創(chuàng)建的時候為其創(chuàng)建一個 CI。
三苔埋、四大組件以及 App lication初始化與Context的關(guān)系
在了解 Binder 的時候有如下要注意的點
1.Activity初始化
1.CI.startActivity():將調(diào)用交給 Instrumentation (負(fù)責(zé)監(jiān)控 Activity 和 AMS 的交互懦砂,所有 Activity 的本地進程到遠(yuǎn)端進程的調(diào)用轉(zhuǎn)換都是其來執(zhí)行),
2.Instrumentation.execStartActivity():傳入一個 APT 然后通過 Binder 機制將調(diào)用過程轉(zhuǎn)移到(后稱AMS)所AMS 在的系統(tǒng)服務(wù)進程组橄,本地主線程則繼續(xù)運行荞膘,不過本地主線程后續(xù)也沒別的操作了。接下來就是本地的MessageQueue 等待 AMS 服務(wù)運行完畢晨炕,發(fā)送消息將 Activity 的啟動重新交給本地主線程衫画。
-
3.AMS.startActivity():從這里開始會調(diào)用會按順序在 ActivityStarter毫炉、ActivityStackSupervisor瓮栗、ActivityStack 這三個類之間進行調(diào)用,主要會進行下面這些操作,不按順序:
- 1.對 Intent 的內(nèi)容進行解析费奸,獲取目標(biāo) Activity 的信息弥激。
- 2.根據(jù)傳入的 APT 獲取被調(diào)用 App 的信息封裝成 PR。
- 3.將1愿阐、2和其他信息結(jié)合微服,將源 Activity 和目標(biāo) Activity 封裝成兩個 AR
- 4.解析 Activity 的啟動模式 和當(dāng)前的 Activity 棧狀態(tài),判斷是否需要創(chuàng)建棧和 Activity 缨历。(注意這里的AR 有著 App 中的 Activity 的全部信息以蕴,可以將其看成系統(tǒng)服務(wù)里面的 Activity 的化身)
- 5.獲取到 Activity 和 Activity 棧之后,接下來要判斷是否要將當(dāng)前 Activity 執(zhí)行 onPause() 以及讓使用Binder 執(zhí)行目標(biāo) Activity 的 onCreate() 和 onResume(注意這里 onStart() 會在 Binder 遠(yuǎn)程調(diào)用onCreate() 的時候直接執(zhí)行)辛孵,這里 AMS 進程會使用 APT 調(diào)用 App 進程的 Activity 執(zhí)行相應(yīng)的生命周期丛肮。
- 6.在 AMS 中前置準(zhǔn)備一切就緒之后,會通過 APT 使用 Handler 的形式調(diào)用到 App 進程的 AT 中魄缚。
- 7.最終到了ActivityStackSupervisor.realStartActivityLocked()中會使用 APT 將調(diào)用交給 App 進程——>AT.scheduleLaunchActivity()——>AT.handleLaunchActivity()
-
4.AT.handleLaunchActivity():將有以下操作
- 1.AT.performLaunchActivity:這個方法有以下操作
- 1.創(chuàng)建對象LA(一個 App 只加載一次)
- 2.創(chuàng)建對象 Activity
- 3.創(chuàng)建對象 App lication(一個 App 宝与,只創(chuàng)建一次)
- 4.**創(chuàng)建對象 CI **:CI.createActivityContext()
- 5.Application、CI都 attach 到 Activity 對象:Activity.attach()
- 6.執(zhí)行 onCreate():Instrumentation.callActivityOnCreate()——>Activity.performCreate()——>Activity.onCreate()
- 7.執(zhí)行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.從上面我們可以總結(jié)一下:在 AMS 將調(diào)用交給 App 進程之后习劫,三個生命周期都是在 App 進程被回調(diào)的,并且在 onResume() 之后View才進行繪制
- 1.AT.performLaunchActivity:這個方法有以下操作
2.Service初始化
- 1.CI.startService()——>CI.startServiceCommon():在這里傳入一個 APT嚼隘,類似 Activity 啟動時的第二步诽里,將調(diào)用過程轉(zhuǎn)移到 AMS 中,本地主線程繼續(xù)運行飞蛹,等待 APT 從 AMS 進程將調(diào)用轉(zhuǎn)移到本地主線程中须肆。
- 2.AMS.startService():到了 AMS 進程之后,Service 的啟動就會全權(quán)交給 AS(這是 AMS 用來管理 Service 的成員變量)
- 3.AS.startServiceLocked():這里做了以下操作
- 1.根據(jù)傳入的 APT 獲取被調(diào)用 App 的信息封裝成 PR
- 2.解析 Intent 等參數(shù)獲取到 Service 的信息桩皿,封裝成 SR(這個類可以看做是 Service 在系統(tǒng)服務(wù)的化身豌汇,記錄了 Service 的一切信息)
- 3.再進過一系列調(diào)用:AS.startServiceInnerLocked()——>AS.bringUpServiceLocked()——>AS.realStartServiceLocked() 到這里才是真正在 App 進程啟動 Service 的流程。
- 4.AS.realStartServiceLocked():這里會有以下操作:
- 1.SR.APT.scheduleCreateService():這里會將調(diào)用轉(zhuǎn)到 App 進程泄隔,但是當(dāng)前的進程還會繼續(xù)執(zhí)行拒贱,這里就到了 App 線程的APT,這個方法里有以下操作
- 1.通過 Handler 轉(zhuǎn)到 AT.handleCreateService()
- 2.創(chuàng)建對象 LA(一個 App 只加載一次)
- 3.創(chuàng)建對象 Service
- 4.創(chuàng)建對象 CI
- 5.創(chuàng)建對象 Application(一個 App 只創(chuàng)建一次)
- 6.Application佛嬉、CI分別 attach 到 Service 對象
- 7.執(zhí)行 Service.onCreate() 回調(diào)
- 8.此時 Service 已經(jīng)啟動了
- 2.AS.sendServiceArgsLocked()——>SR. App.APT.scheduleServiceArgs():這里就轉(zhuǎn)到了 App 進程的 APT 中逻澳,這里會有以下操作:
- 1.APT.scheduleServiceArgs()
- 2.AT.handleServiceArgs()
- 3.Service.onStartCommand()
- 4.此時我們需要在 Service 中進行的操作將會執(zhí)行。
- 1.SR.APT.scheduleCreateService():這里會將調(diào)用轉(zhuǎn)到 App 進程泄隔,但是當(dāng)前的進程還會繼續(xù)執(zhí)行拒贱,這里就到了 App 線程的APT,這個方法里有以下操作
3.ContentProvider初始化
- 1.AT.main()——>AT.attach()——>AMS.attach App lication():傳入一個 APT暖呕,調(diào)用轉(zhuǎn)到了 AMS 進程
- 2.AMS.attachApplicationLocked():獲取到 ApplicationInfo 和 ProviderInfo 列表之后通過 APT 將調(diào)用轉(zhuǎn)回 App 進程斜做。
- 3.APT.bindApplication()——>AT.handleBindApplication()——>AT.installContentProviders():到這里之后將會循環(huán)初始化 ContentProvider。
- 4.AT.installProvider():這個方法里面有以下操作
- 1.創(chuàng)建對象LA:CI.createPackageContext()中
- 2.創(chuàng)建對象CI:CI.createPackageContext()中
- 3.創(chuàng)建對象ContentProvider:ClassLoader創(chuàng)建
- 4.CI attach到ContentProvider對象:ContentProvider.attachInfo()中
- 5.執(zhí)行onCreate回調(diào):ContentProvider.attachInfo()中
4.BroadCastReceiver靜態(tài)初始化
因為動態(tài)廣播的注冊時進程已創(chuàng)建湾揽, 基本對象已創(chuàng)建完成瓤逼,只需要回調(diào)BroadcastReceiver 的 onReceive() 方法即可笼吟,所以這里不分析
1.當(dāng)收到廣播時會調(diào)用AT.handleReceiver()
2.創(chuàng)建對象LA(一個 App 只加載一次)
3.創(chuàng)建對象BroadcastReceiver
4.創(chuàng)建對象 Application
5.從創(chuàng)建的 Application中獲取 CI
6.執(zhí)行 onReceive() 回調(diào)
-
5.Application初始化:由上面四個組件的初始化我們可以知道,當(dāng) App 還沒啟動的時候喚醒任意組件都會創(chuàng)建一個 Application霸旗,而這里分析的是正常情況啟動一個 App 的時候創(chuàng)建 Application的流程贷帮。
- 1.這里的流程其實就是包含了ContentProvider 初始化的流程,所以前面都差不多
- 2.最后到了AT.handleBindApplication()中诱告,這里有以下操作:
- 1.創(chuàng)建對象 LA
- 2.創(chuàng)建對象 CI
- 3.創(chuàng)建對象 Instrumentation
- 4.創(chuàng)建對象 Application;
- 5.安裝 providers
- 6.執(zhí)行 Create 回調(diào)
四撵枢、四大組件以及 App lication綁定Context的方法
由上一節(jié)我們可以知道,四大組件以及 App lication在初始化的時候都會進行Context的綁定或者創(chuàng)建精居,這節(jié)就來講講各個組件是如何對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谆沃,其自身并不創(chuàng)建Context
- 4.ContentProvider:
- 1.AT.installProvider()
- 2.Context.createPackageContext()——>CI.createPackageContext()——>CI.createPackageContextAsUser():這里是通過一個 App lication的Context創(chuàng)建的Context钝凶,所以可以看做是 App lication的Context的一個復(fù)制。
五唁影、總結(jié)
1.組件初始化會創(chuàng)建的對象
- 1.LoadedApk:所有組件在初始化的時候耕陷,如果LA沒被初始化都會初始化一遍
- 2.Context:
- 1.只有Activity的CI有上一個Activity的Token
- 2.Receiver的Context是繼承于ContextWr App er 的 ReceiverRestrictedContext,不可綁定Service据沈。
-
- App lication:
- 1.Receiver使用的Context是ReceiverRestrictedContext包裝的 App lication的Context哟沫,所以其可以通過Context獲取到 App lication
- 2.ContentProvider一般是在 App 初始化的時候在初始化 App lication的過程中加載的,此時 App lication會被加載锌介。但是如果是多個 App 共享進程嗜诀,第二個 App 由ContentProvider調(diào)起,那么 App lication不會被初始化孔祸。
2.Context使用場景
說明: (圖中第一列代表不同的 Context隆敢, √代表允許在該 Context 執(zhí)行相應(yīng)的操作; ×代表不允許; -代表分情況討論)
- 1.當(dāng) Context 為 Receiver的情況下:
- 1.不允許執(zhí)行 bindService() 操作, 由于限制性上下文(ReceiverRestrictedContext)所決定的崔慧,會直接拋出異常.
- 2.registerReceiver 是否允許取決于 receiver;
- 3.當(dāng) receiver == null 用于獲取 sticky 廣播拂蝎, 允許使用。否則不允許使用registerReceiver惶室。
- 2.縱向來看 startActivity 操作
- 1.當(dāng)為 ActivityContext 則可直接使用;
- 2.當(dāng)為其他 Context温自, 要啟動的 Activity 不屬于任何 Activity 棧玄货,所以必須帶上 FLAG_ACTIVITY_NEW_TASK flags 才能使用
3.getApplication() 和 getApplicationContext()
絕大多數(shù)情況下, getApplication() 和 getApplicationContext() 這兩個方法完全一致捣作, 返回值也相同; 那么兩者到底有什么區(qū)別呢? 真正理解這個問題的人非常少. 接下來徹底地回答下這個問題:
- 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. 絕大多數(shù)情況下沒有問題慢哈,但是有可能會出現(xiàn)空指針的問題。情況如下:當(dāng)同一個進程有多個 Apk 的情況下永票, 對于第二個 Apk 是由Provider 方式拉起的卵贱, 前面介紹過 Provider 創(chuàng)建過程并不會初始化所在 Application, 此時執(zhí) getContext().get ApplicationContext() 返回的結(jié)果便是 NULL侣集,所以對于這種情況要做好判空键俱。
參考文章
- 1.Context全面理解
不販賣焦慮,也不標(biāo)題黨世分。分享一些這個世界上有意思的事情编振。題材包括且不限于:科幻、科學(xué)臭埋、科技踪央、互聯(lián)網(wǎng)、程序員瓢阴、計算機編程畅蹂。下面是我的微信公眾號:世界上有意思的事,干貨多多等你來看荣恐。