Context(在Android中翻譯為場景):一個Activity就是一個Context,一個Service也是一個Context,應用程序中有多少個Activity或者Service,就會有多少個Context對象; Android把"場景"抽象為Context類,用戶和操作系統的每一次交互都是一個場景; 如打電話為有界面的場景,后台服務service為沒界面的場景;
Context類本身是一個純abstract類,為了使用方便,定義了ContextWrapper類,這只是一個包裝而已,它的構造函數中必須包含一個真正的Context引用,同時它提供了attachBaseContext()用於給ContextWrapper對象中指定真正的Context對象,調用ContextWrapper的方法都會被轉向其所包含的真正的Context對象。 ContextThemeWrapper類,內部包含了主題相關的接口,在此說的主題是指在Manifest.xml中通過android:theme為Application元素或者Activity元素指定的主題;
ContextImpl類真正實現了Context中所有函數,應用程序中調用的各種Context類的方法,其實現均來自於該類; 每一個應用程序都是從ActivityThread類開始的,創建Context對象也是在該類中完成,具體創建ContexImpl類的地方一共有7處;
- PackageInfo.makeApplication()
performLaunchActivity()
handleCreateBackupAgent()
handleCreateService()
handleBindApplication()
handleBindApplication()
attach()
attach()方法僅在Framework進程(system_server)啟動時調用,應用程序運行時不會調用到該方法,需要明確的是system_server進程本身也是一個應用程序,所以其入口也是ActivityThread類,只是這個ActivityThread和一些系統服務運行在同一個進程空間中而已。
attach()方法中創建ContextImpl對象的基本邏輯如同以下三小點:
1、Application對應的Context
每個應用程序在第一次啟動時,都會首先創建一個Application對象,默認的Application是應用程序的包名,用戶可以重載默認的Application; 程序第一次啟動時,會輾轉調用到handleBindApplication()方法中,該方法中有兩處創建了ContextImpl對象,但都是在if(data.instrumentationName!=null)條件中,該條件在一般的程序執行時都不會被執行到,只有當創建一個Android UnitTest工程時,相應的Test程序才能滿足這個條件;
而如果不是測試工程的話,則調用makeApplication()方法
// If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. Application app = data.info.makeApplication(data.restrictedBackupMode, null);
下面是makeApplication函數處理方法代碼
java.lang.ClassLoader cl = getClassLoader(); ContextImpl appContext = new ContextImpl(); appContext.init(this, null, mActivityThread); app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext); appContext.setOuterContext(app);
即創建一個ContextImpl對象,然后調用init方法,其中第一個參數this指的是當前PackageInfo對象{LoadedApk.Java},該對象將賦值給ContextImpl類中的重要成員變量mPackageInfo。
需要注意的是PackageInfo對象從什么地方來的?就需要回溯到是誰調用了makeApplication()方法;
首先,AmS通過遠程調用到ActivityThread的bindApplication()方法,該方法的參數包括兩種,一種是ApplicationInfo,這是實現了Parcelable接口的數據類,意味着這個對象是由AmS創建的,通過IPC傳遞到ActivityThread中,另一種是其他的相關參數在bindApplication()方法中,
public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, String profileFile, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, int debugMode, boolean isRestrictedBackupMode, Configuration config, Map<String, IBinder> services) { ////// AppBindData data = new AppBindData(); ////// }
bindApplication方法創建一個本地AppBindData數據類,然后再去調用handleBindApplication。在調用handleBindApplication()的時候,AppBindData對象中的info變量還是空值,然后會使用data.info = getPackageInfoNoChecked方法為info變量賦值,
private final void handleBindApplication(AppBindData data) { mBoundApplication = data; ///// data.info = getPackageInfoNoCheck(data.appInfo);
/////
getPackageInfoNoCheck方法如下
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) { return getPackageInfo(ai, null, false, true); }
getPackageInfo方法如下:
private final LoadedApk getPackageInfo(ApplicationInfo aInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { LoadedApk packageInfo = ref != null ? ref.get() : null; if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) { //...if (includeCode) { mPackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } } return packageInfo; }
這個方法的內部實際上會根據AppBindData中的ApplicationInfo中的mPackageName創建一個PackageInfo對象,並把這個對象保存為ActivityThread類的全局對象,顯然,一個應用程序中的所有Activity或者Application對象對應的mPackageName都一樣,即為包的名稱,所以ActivityThread中會產生一個全局PackageInfo對象。
接下來,回到data.info.makeApplication()方法,這是PackageInfo對象的來源;以上流程可以總結如下
2、Activity對象中獲取Context對象
啟動Activity時,AmS會通過IPC調用到ActivityThread的scheduleLaunchActivity方法,該方法包含一個參數ActivityInfo,這是一個實現了Parcelable接口的數據類,意味着該對象是AmS創建的,並通過IPC傳遞到ActivityThread;另一些參數如下。
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) { ActivityClientRecord r = new ActivityClientRecord();
......
}
scheduleLaunchActivity方法中會創建一個本地ActivityRecord數據類,ActivityThread內部會為每一個Activity創建一個ActivityRecord對象,並使用這些數據來管理Activity。
接下來會調用handleLaunchActivity(),然后再調用performLaunchActivity(),該方法中創建ContextImpl代碼如下:
ContextImpl appContext = new ContextImpl(); appContext.init(r.packageInfo, r.token, this); appContext.setOuterContext(activity);
ContextImpl.init方法中的第一個參數來源即為performLaunchActivity中
ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, Context.CONTEXT_INCLUDE_CODE); }
即在performLaunchActiviy開始執行時,首先為r.packageInfo賦值,getpackageInfo方法的執行邏輯和getPackageInfoNoChecked()基本相同,所以,r.packageInfo對象的PackageInfo對象和Application對應的packageInfo對象是同一個。
3、Service對應的Context
啟動Service時,AmS首先會通過IPC調用到ActivityThread的scheduleCreateService()方法,該方法包含兩個參數。第一種是ServiceInfo,這是實現了一個Parcelable接口的類,意味着該對象由Ams創建,並通過IPC傳遞到ActivityThread內部,第二種是其他參數。
public final void scheduleCreateService(IBinder token, ServiceInfo info) { CreateServiceData s = new CreateServiceData(); // ...... s.token = token; s.info = info; queueOrSendMessage(H.CREATE_SERVICE, s); }
在scheduleCreateService()方法中,會創建一個CreateServiceData的數據對象,ActivityThread會為其所包含的每一個Service創建該數據對象,並通過這些對象來管理Service。
接着,會執行handleCreateService()方法,其中創建ContextImpl對象的代碼如下:
ContextImpl context = new ContextImpl(); context.init(packageInfo, null, this); context.setOuterContext(agent); agent.attach(context);
同樣context.init()方法中packageInfo的賦值操作為handleCreateService方法中通過如下代碼實現
LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
賦值代碼同樣使用了getPackageInfoNoCheck()方法,意味着Service對應的Context對象內部的mPackageInfo與Activity、Application中是完全相同的;
總結流程圖如下:
4、Context之間的關系
從以上三節可以看出,創建Context對象的過程基本上是相同的,包括代碼結構也很類似,不同的是針對Application、Activity、Service使用了不同的數據對象
不同Context子類中PackageInfo的來源如上表
一個應用程序中包含的Context個數為:Service個數 + Activity個數 + 1(Application類本身對應一個Context對象)
應用程序中包含多個ContextImpl對象,而其內部變量mPackageInfo指向同一個PackageInfo對象,這種設計意味着ContextImpl是一種輕量級,而PackageInfo是一個重量級;ContextImpl中的大多數進行包操作的重量級函數實際上都轉向了mPackageInfo對象相應的方法,即事實上是調用了同一個PackageInfo對象;