Android深刻理解Context(一)Context關聯類和Application Context建立過程

前言

Context也就是上下文對象,是Android較爲經常使用的類,可是對於Context,不少人都停留在會用的階段,這個系列會帶你們從源碼角度來分析Context,從而更加深刻的理解它。html

1.Context概述

Context意爲上下文或者場景,是一個應用程序環境信息的接口。
在開發中咱們常常會使用Context,它的使用場景總的來講分爲兩大類,它們分別是:java

  • 使用Context調用方法,好比:啓動Activity、訪問資源、調用系統級服務等。
  • 調用方法時傳入Context,好比:彈出Toast、建立Dialog等。

Activity、Service和Application都是間接的繼承自Context的,所以,咱們能夠計算出一個應用程序進程中有多少個Context,這個數量等於Activity和Service的總個數加1,1指的是Application的數量。android

Context是一個抽象類,它的內部定義了不少方法以及靜態常量,它的具體實現類爲ContextImpl。和Context相關聯的類,除了ContextImpl還有ContextWrapper、ContextThemeWrapper和Activity等等,下面給出Context的關係圖。
app

從圖中咱們能夠看出,ContextImpl和ContextWrapper繼承自Context,ContextWrapper內部包含有Context類型的mBase對象,mBase具體指向的是ContextImpl。ContextImpl提供了不少功能,可是外界須要使用並拓展ContextImpl的功能,所以設計上使用了裝飾模式,ContextWrapper是裝飾類,它對ContextImpl進行包裝,ContextWrapper主要是起了方法傳遞做用,ContextWrapper中幾乎全部的方法實現都是調用ContextImpl的相應方法來實現的。
ContextThemeWrapper、Service和Application都繼承自ContextWrapper,這樣他們均可以經過mBase來使用Context的方法,同時它們也是裝飾類,在ContextWrapper的基礎上又添加了不一樣的功能。
ContextThemeWrapper中包含和主題相關的方法(好比: getTheme方法),所以,須要主題的Activity繼承ContextThemeWrapper,而不須要主題的Service則繼承ContextWrapper。ide

2.Application Context的建立過程

咱們經過調用getApplicationContext來獲取應用程序的全局的Application Context,那麼Application Context是如何建立的呢?
當一個應用程序啓動完成後,應用程序就會有一個全局的Application Context。那麼咱們就從應用程序啓動過程開始着手。this

Android深刻四大組件(一)應用程序啓動過程(後篇)這篇文章的最後講了ActivityThread啓動Activity。ActivityThread做爲應用程序進程的核心類,它會調用它的內部類ApplicationThread的scheduleLaunchActivity方法來啓動Activity,以下所示。spa

frameworks/base/core/java/android/app/ActivityThread.java線程

private class ApplicationThread extends ApplicationThreadNative {
 ...
   @Override
    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
            int procState, Bundle state, PersistableBundle persistentState,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            updateProcessState(procState, false);
            ActivityClientRecord r = new ActivityClientRecord();
            ...
            sendMessage(H.LAUNCH_ACTIVITY, r);
    }
 ...   
}
View Code

在ApplicationThread的scheduleLaunchActivity方法中向H類發送LAUNCH_ACTIVITY類型的消息,目的是將啓動Activity的邏輯放在主線程中的消息隊列中,這樣啓動Activity的邏輯會在主線程中執行。咱們接着查看H類的handleMessage方法對LAUNCH_ACTIVITY類型的消息的處理。設計

frameworks/base/core/java/android/app/ActivityThread.java3d

private class H extends Handler {
      public static final int LAUNCH_ACTIVITY         = 100;
...
public void handleMessage(Message msg) {
          if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
          switch (msg.what) {
              case LAUNCH_ACTIVITY: {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                  final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                  r.packageInfo = getPackageInfoNoCheck(
                          r.activityInfo.applicationInfo, r.compatInfo);//1
                  handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");//2
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              } break;
            
            ...
}
View Code

H繼承自Handler ,是ActivityThread的內部類。在註釋1處經過getPackageInfoNoCheck方法得到LoadedApk類型的對象,並將該對象賦值給ActivityClientRecord 的成員變量packageInfo,其中LoadedApk用來描述已加載的APK文件。在註釋2處調用handleLaunchActivity方法,以下所示。
frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
     Activity a = performLaunchActivity(r, customIntent);
    ...
 }
View Code

咱們接着查看performLaunchActivity方法:
frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
       ...
    } 
    ...
    return activity;
}
View Code

performLaunchActivity方法中有不少重要的邏輯,這裏只保留了Application Context相關的邏輯,想要更多瞭解performLaunchActivity方法中的邏輯請查看Android深刻四大組件(一)應用程序啓動過程(後篇)這篇文章的第二小節。這裏ActivityClientRecord 的成員變量packageInfo是LoadedApk類型的,咱們接着來查看LoadedApk的makeApplication方法,以下所示。
frameworks/base/core/java/android/app/LoadedApk.java

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {//1
        return mApplication;
    }
    ...
    try {
      ...
       java.lang.ClassLoader cl = getClassLoader();
      ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//2
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);//3
        appContext.setOuterContext(app);//4
    } catch (Exception e) {
       ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;//5
    ...
    return app;
}
View Code

註釋1處若是mApplication不爲null則返回mApplication,這裏假設是第一次啓動應用程序,所以mApplication爲null。在註釋2處經過ContextImpl的createAppContext方法來建立ContextImpl。註釋3處的代碼用來建立Application,在Instrumentation的newApplication方法中傳入了ClassLoader類型的對象以及註釋2處建立的ContextImpl 。在註釋4處將Application賦值給ContextImpl的Context類型的成員變量mOuterContext。註釋5處將Application賦值給LoadedApk的成員變量mApplication,在Application Context的獲取過程當中咱們會再次用到mApplication。咱們來查看註釋3處的Application是如何建立的,Instrumentation的newApplication方法以下所示。
frameworks/base/core/java/android/app/Instrumentation.java

static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();//1
    app.attach(context);
    return app;
}
View Code

Instrumentation中有兩個newApplication重載方法,最終會調用上面這個重載方法。註釋1處經過反射來建立Application,並調用了Application的attach方法,並將ContextImpl傳進去:
frameworks/base/core/java/android/app/Application.java

/* package */ final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
View Code

attach方法中調用了attachBaseContext方法,它的實如今Application的父類ContextWrapper中,代碼以下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

protected void attachBaseContext(Context base) {
     if (mBase != null) {
         throw new IllegalStateException("Base context already set");
     }
     mBase = base;
 }
View Code

從上文咱們得知,這個base指的是ContextImpl,將ContextImpl賦值給ContextWrapper的Context類型的成員變量mBase。Application Context的建立過程就講到這裏,最後給出Application Context建立過程的時序圖。
繪圖1_副本.png繪圖1_副本.png

3.Application Context的獲取過程

當咱們熟知了Application Context的建立過程,那麼它的獲取過程會很是好理解。咱們經過調用getApplicationContext方法來得到Application Context,getApplicationContext方法的實如今ContextWrapper中,以下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public Context getApplicationContext() {
    return mBase.getApplicationContext();
}
View Code

從上文咱們得知,mBase指的是ContextImpl,咱們來查看 ContextImpl的getApplicationContext方法:
frameworks/base/core/java/android/app/ContextImpl.java

@Override
public Context getApplicationContext() {
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}
View Code

若是LoadedApk不爲null,則調用LoadedApk的getApplication方法,不然調用AvtivityThread的getApplication方法。因爲應用程序這時已經啓動,所以LoadedApk不會爲null,則會調用LoadedApk的getApplication方法:
frameworks/base/core/java/android/app/LoadedApk.java

Application getApplication() {
     return mApplication;
 }
View Code

這裏的mApplication咱們應該很熟悉,它在上文LoadedApk的makeApplication方法的註釋5處被賦值。這樣咱們經過getApplicationContext方法就獲取到了Application Context。

相關文章
相關標籤/搜索