Android中關於Context的三言兩語

前言

今天咱們來分析一下 Context 的源碼,在 APP 開發中,咱們會常常用到 Context ,那麼什麼是 Context 呢?它的常規語義是「上下文」那麼這個「上下文」究竟是什麼呢?經過源碼分析,咱們能對這個Context有個基本的認識。android

類繼承圖

咱們來看下關於 Context 的類繼承圖,咱們經過查看源碼得知,Context 是一個抽象類,因此它確定有其實現類,查閱得知它的實現類爲 ContextWrapper 和 ContextImpl ,因此它的繼承圖以下:bash

在這裏插入圖片描述

以上的 Context 類繼承關係清晰簡潔,能夠得知,Application 、 Service 、Activity 都是繼承的 Context 類,因此從這裏咱們能夠得知:app

Context 數量 = Activity 數量 + Service 數量 + 1
複製代碼

另外,咱們能夠看到 Application 和 Service 都是直接繼承 ContextWrapper 的而 Activity 倒是繼承 ContextThemeWrapper 的,這是爲什麼?其實 ContextThemeWrapper 是關於主題類的,Activity 是有界面的,而 Application 和 Service 卻沒有。接下來咱們來詳細看下它們的源碼實現。ide

ContextWrapper

咱們進入到 ContextWrapper 源碼中能夠發現,它其實調用了 mBase 裏面的方法,而 mBase 實際上是 ContextImpl ,因此最終仍是得調用它的實現類 ContextImpl 類裏面的方法。oop

public class ContextWrapper extends Context {
    Context mBase;
    public ContextWrapper(Context base) {
        mBase = base;
    }
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    //其他的都是覆蓋Context裏面的方法
}
複製代碼

咱們能夠按照上面的類的繼承圖進行依次分析,由上面能夠知道 ContextWrapper 實際上是調用 ContextImpl 裏面的方法,因此 Application 和 Service 還有 Activity 它們應該都跟 ContextImpl 有關的。究竟是不是這樣的呢?咱們追蹤源碼進行分析。源碼分析

Application

相似於 Java 的 main 啓動方法程序,Android 也有一個相似的方法,那就是在 ActivityThread 類中也有一個 main ,這是開始的地方,咱們從這裏進行一點一點跟蹤:學習

ActivityThread#mainui

//省略部分代碼...
	  Looper.prepareMainLooper();
      ActivityThread thread = new ActivityThread();
      thread.attach(false);
      //省略部分代碼...
      Looper.loop();      
      //省略部分代碼...
複製代碼

咱們找到 ActivityThread 的 main 方法,省略無關代碼,這個 main 方法就是不斷的從消息隊列中獲取消息,而後進行處理。咱們本次不分析 Looper 相關的東西,只分析跟 Context 有關的內容,繼續進入 attach 方法,this

Android 分析源碼,不能一頭扎進去,咱們應該主要分析它的流程。idea

ActivityThread#attach

//省略部分代碼...
				mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
				//Application的實例建立
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                
                //調用Application裏面的生命週期方法onCreate
                mInitialApplication.onCreate();
//省略部分代碼...
複製代碼

這裏面出現了 ContextImpl ,因此下面應該會跟 Application 扯上關係,因此進入到 makeApplication 方法中繼續往下追蹤,

LoadedApk#makeApplication

//省略部分代碼...
  Application app = null;
 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
//省略部分代碼...
複製代碼

最終又進入到 Instrumentation#newApplication 方法裏面

Instrumentation#newApplication

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }
複製代碼

Application#attach

/**
    * @hide
    */
   /* package */ 
   final void attach(Context context) {
       attachBaseContext(context);
       mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
   }
複製代碼

走到這裏就很明清晰了,最終將會調用 ContextWrapper 的 attachBaseContext 方法。從上面到這裏,如預料的同樣,分析到這裏,記住了多少?是否是隻知道 Application 裏面最終會調用 attachBaseContext 這個方法?這樣的話就對了,不能一頭扎進代碼的海洋裏,處處遨遊,那樣會迷失方向的,Android 源碼那麼大,那麼多,一一細節分析根本是不大可能的,因此只能把握流程,而後再針對性的分析實現過程。接着分析 Service 裏面相關的方法。

Service

對於 Service ,咱們在 ActivityThread 中能夠發現有個方法叫 handleCreateService ,這裏面有關於 Service 和 ContextImpl 之間的聯繫。

ActivityThread#handleCreateService

Service service = null;
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
           context.setOuterContext(service);
           Application app = packageInfo.makeApplication(false, mInstrumentation);
           service.attach(context, this, data.info.name, data.token, app,
                   ActivityManager.getService());
           service.onCreate();
複製代碼

對於 Application 的那段代碼咱們能夠發現,這二者及其相似,咱們進入到 attach 方法中查看相關代碼,發現

/**
    * @hide
    */
   public final void attach(
           Context context,
           ActivityThread thread, String className, IBinder token,
           Application application, Object activityManager) {
	//調用attachBaseContext方法
       attachBaseContext(context);
       mThread = thread;           // NOTE:  unused - remove?
       mClassName = className;
       mToken = token;
       mApplication = application;
       mActivityManager = (IActivityManager)activityManager;
       mStartCompatibility = getApplicationInfo().targetSdkVersion
               < Build.VERSION_CODES.ECLAIR;
   }
複製代碼

代碼很簡單,就是這樣跟 ContextImpl 扯上關係的。由於 Service 和 Application 都是繼承的 ContextWrapper 類,接下來咱們來分析一下關於 Activity 的代碼。

Activity

在這裏說明一下爲何 Service 和 Application 都是繼承的 ContextWrapper 類而 Activity 倒是繼承 ContextThemeWrapper 那是由於 Activity 是帶有界面顯示的,而 Service 和 Application 卻沒有,因此從名字咱們能夠看到 ContextThemeWrapper 包含主題的信息,同時 ContextThemeWrapper 卻又是繼承自 ContextWrapper ,分析 ContextThemeWrapper 源碼咱們能夠看到,裏面基本都是關於 theme 的方法,同時它也覆蓋了 attachBaseContext 方法。

咱們進入 Activity 源碼也發現它也有和 Service 相似的 attach 方法

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
		//省略部分代碼...
        attachBaseContext(context);
複製代碼

接下來咱們來分析一下 Activity 在哪裏和這個扯上關係的。

ActivityThread#performLaunchActivity

performLaunchActivity 這個方法其實就是啓動 Activity 的方法 ,咱們之後再來學習關於這個方法的內容,如今先分析 Context 的內容。咱們進入到這個方法查看:

//省略部分代碼...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
//省略部分代碼...
  activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
//省略部分代碼...
複製代碼

首先經過 createBaseContextForActivity 方法建立ContextImpl 而後直接有 Activity attach 進去。到此爲止,關於 Application 、Service 和 Activity 關於Context 的源碼基本就差很少了。接下來咱們來解決一些實際的內容。

實例理解

既然 Application、Service 和 Activity 都有 Context 那麼它們之間到底有啥區別呢?同時 getApplicationContext 和 getApplication() 又有什麼區別呢?接下來咱們經過代碼進行驗證。

咱們如今的項目通常都有自定義 Application 的類進行一些初始化操做,本例中也新建一個 MyApplication 的類繼承自 Application,而後在Manifest.xml中進行註冊,代碼以下:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("androidos_analysis", "getApplicationContext()——> " + getApplicationContext());
        Log.d("androidos_analysis", "getBaseContext() ——> " + getBaseContext());
    }
}
複製代碼

打印結果以下:

getApplicationContext()——> com.ihidea.androidosanalysis.MyApp@9831cf9
getBaseContext()       ——> android.app.ContextImpl@13d643e
複製代碼

咱們發現當咱們經過 getApplicationContext 獲取的是咱們申明的 Application 實例,而經過 getBaseContext 獲取到的倒是 ContextImpl 這是爲何呢?咱們查看它們的實現發現

ContextWrapper#getBaseContext

/**
   * @return the base context as set by the constructor or setBaseContext
   */
  public Context getBaseContext() {
      return mBase;
  }
複製代碼

其實在上文咱們已經分析過了它們的源碼,咱們知道其實這個mBase就是 ContextImpl 了。而 getApplicationContext

ContextWrapper#getApplicationContext

@Override
  public Context getApplicationContext() {
      return mBase.getApplicationContext();
  }
複製代碼

經過上面分析咱們知道 其實 Application 它自己也是一個 Context 因此,這個們返回的就是它本身了。因此這裏獲取getApplicationContext()獲得的結果就是MyApplication自己的實例。

有時候咱們代碼裏面也會有關於 getApplication 的用法,那麼 這個跟 getApplicationContext 又有什麼區別呢?咱們再來log一下就知道了。

咱們建立一個 MainActivity 而後在裏面打印兩行代碼:

MainActivity#onCreate

Log.d("androidos_analysis", "getApplicationContext()——> " + getApplicationContext());
Log.d("androidos_analysis", "getApplication() ——> " + getApplication());
複製代碼

在這裏插入圖片描述

咱們能夠發現 這兩個返回的結果都是 同樣的,其實不難理解,

Activity#getApplication

/** Return the application that owns this activity. */
   public final Application getApplication() {
       return mApplication;
   }
複製代碼

其實 getApplication 返回的就是 Application 因此這二者是同樣的了。可是都是返回的 Application ,Android 爲何要存在這兩個方法呢?這就涉及到做用域的問題了,咱們能夠發現使用 getApplication 的方法的做用範圍是 Activity 和 Service ,可是咱們在其餘地方卻不能使用這個方法,這種狀況下咱們就可使用 getApplicationContext 來獲取 Application 了。什麼狀況下呢?譬如:BroadcastReceiver 咱們想在Receiver 中獲取 Application 的實例咱們就能夠經過這種方式來獲取:

public class MyReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) { 
        MyApplication myApp = (MyApplication) context.getApplicationContext();  
        //...
    }  
}
複製代碼

以上內容就是關於 Context 的部份內容。

關於做者

專一於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎你們關注,一塊兒交流學習~

在這裏插入圖片描述
相關文章
相關標籤/搜索