Android 內核--Context對象


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對象;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM