1. 程式人生 > >(4.1.54)Android Context詳解

(4.1.54)Android Context詳解

Context意為上下文或者場景,是一個應用程式環境資訊的介面。

從某種意義上,它就是一個萬能介面百寶箱,譬如我們啟動Acitivity需要Instruction,啟動Service需要IActivityManager,獲取pack需要PMS

如果讓程式設計師自己去和這些東西打交道簡直太麻煩了,那麼能不能我寫個百寶箱的介面,只要是常用的操作我就丟裡邊,然後它的實現類統一去和那些其他亂七八糟的東西打交道呢?

這就是Context

當然,針對不同的持有者,需要有不同的建立方式,也就是說Context的實現類中會存有持有者的資訊

一、 Context 介面的方法

Context是個抽象類,不管,會有實現的

通過它我們可以獲取應用程式的資源和類(包括應用級別操作,如啟動Activity,發廣播,接受Intent等)

  1. 啟動Activity
    • 藉助Instrumentation間接使用IActivityManager
  2. 啟動和停止Service
    • 直接藉助 IActivityManager
  3. 傳送廣播訊息(Intent)
  4. 註冊廣播訊息(Intent)接收者
    • 直接藉助 IActivityManager
  5. 可以訪問APK中各種資源(如Resources和AssetManager等)
  6. 可以訪問Package的相關資訊
  7. APK的各種許可權管理
/** 應用程式環境的全域性資訊的介面。 
* 它是一個抽象類,它的執行被Android系統所提供。
* 它允許獲取以應用為特徵的資源和型別,是一個統領一些資源(應用程式環境變數等)的上下文。
*/
public abstract class Context {

	// 獲取應用程式包的AssetManager例項
	public abstract AssetManager getAssets();

	// 獲取應用程式包的Resources例項
	public abstract Resources getResources();

	// 獲取PackageManager例項,以檢視全域性package資訊
	public abstract PackageManager getPackageManager();

	// 獲取應用程式包的ContentResolver例項
	public abstract ContentResolver getContentResolver();

	// 它返回當前程序的主執行緒的Looper,此執行緒分發呼叫給應用元件(activities, services等)
	public abstract Looper getMainLooper();

	// 返回當前程序的單例項全域性Application物件的Context
	public abstract Context getApplicationContext();

	// 從string表中獲取本地化的、格式化的字元序列
	public final CharSequence getText(int resId) {
	return getResources().getText(resId);
	}

	// 從string表中獲取本地化的字串
	public final String getString(int resId) {
	return getResources().getString(resId);
	}

	public final String getString(int resId, Object... formatArgs) {
	return getResources().getString(resId, formatArgs);
	}

	// 返回一個可用於獲取包中類資訊的class loader
	public abstract ClassLoader getClassLoader();

	// 返回應用程式包名
	public abstract String getPackageName();

	// 返回應用程式資訊
	public abstract ApplicationInfo getApplicationInfo();

	// 根據檔名獲取SharedPreferences
	public abstract SharedPreferences getSharedPreferences(String name,
	int mode);

	// 其根目錄為: Environment.getExternalStorageDirectory()
	public abstract File getExternalFilesDir(String type);

	// 返回應用程式obb檔案路徑
	public abstract File getObbDir();

	// 啟動一個新的activity
	public abstract void startActivity(Intent intent);

	// 啟動一個新的activity
	public void startActivityAsUser(Intent intent, UserHandle user) {
	throw new RuntimeException("Not implemented. Must override in a subclass.");
	}

	// 啟動一個新的activity
	// intent: 將被啟動的activity的描述資訊
	// options: 描述activity將如何被啟動
	public abstract void startActivity(Intent intent, Bundle options);

	// 啟動多個新的activity
	public abstract void startActivities(Intent[] intents);

	// 啟動多個新的activity
	public abstract void startActivities(Intent[] intents, Bundle options);

	// 廣播一個intent給所有感興趣的接收者,非同步機制
	public abstract void sendBroadcast(Intent intent);

	// 廣播一個intent給所有感興趣的接收者,非同步機制
	public abstract void sendBroadcast(Intent intent,String receiverPermission);
	//傳送有序廣播
	public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);

	public abstract void sendOrderedBroadcast(Intent intent,
	String receiverPermission, BroadcastReceiver resultReceiver,
	Handler scheduler, int initialCode, String initialData,
	Bundle initialExtras);

	public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);

	public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,
	String receiverPermission);

	// 註冊一個BroadcastReceiver,且它將在主activity執行緒中執行
	public abstract Intent registerReceiver(BroadcastReceiver receiver,
	IntentFilter filter);
	//取消註冊BroadcastReceiver
	public abstract Intent registerReceiver(BroadcastReceiver receiver,
	IntentFilter filter, String broadcastPermission, Handler scheduler);

	public abstract void unregisterReceiver(BroadcastReceiver receiver);

	// 請求啟動一個application service
	public abstract ComponentName startService(Intent service);

	// 請求停止一個application service
	public abstract boolean stopService(Intent service);

	// 連線一個應用服務,它定義了application和service間的依賴關係
	public abstract boolean bindService(Intent service, ServiceConnection conn,
	int flags);

	// 斷開一個應用服務,當服務重新開始時,將不再接收到呼叫,
	// 且服務允許隨時停止
	public abstract void unbindService(ServiceConnection conn);

	// 返回系統級service
	public abstract Object getSystemService(String name);
	//檢查許可權
	public abstract int checkPermission(String permission, int pid, int uid);

	// 返回一個新的與application name對應的Context物件
	public abstract Context createPackageContext(String packageName,
	int flags) throws PackageManager.NameNotFoundException;

	// 返回基於當前Context物件的新物件,其資源與display相匹配
	public abstract Context createDisplayContext(Display display);

}

二、Context 應用場景

在開發中我們經常會使用Context,它的使用場景總的來說分為兩大類,它們分別是:

  1. 使用Context呼叫方法,比如:啟動Activity、訪問資源、呼叫系統級服務等。
  2. 呼叫方法時傳入Context,比如:彈出Toast、建立Dialog等。

因為Context的具體能力是由ContextImpl類去實現的,所以在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。

不過有幾種場景比較特殊:

  1. 啟動Activity
    • 出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧
  2. 彈出Dialog。。
    • Dialog則必須在一個Activity上面彈出(除非是System Alert型別的Dialog),因此在這種場景下,我們只能使用Activity型別的Context,否則將會出錯

在這裡插入圖片描述 【Context的應用場景圖】

大家注意看到有一些NO上添加了一些數字,其實這些從能力上來說是YES,但是為什麼說是NO呢?下面一個一個解釋:

  1. 數字1:啟動Activity在這些類中是可以的,但是需要建立一個新的task。一般情況不推薦。
  2. 數字2:在這些類中去layout inflate是合法的,但是會使用系統預設的主題樣式,如果你自定義了某些樣式可能不會被使用。
  3. 數字3:在receiver為null時允許,在4.2或以上的版本中,用於獲取黏性廣播的當前值。(可以無視)

實際上,只要把握住一點,凡是跟UI相關的,都應該使用Activity做為Context來處理;其他的一些操作,Service,Activity,Application等例項都可以

三、Context 如何獲取

通常我們想要獲取Context物件,主要有以下四種方法

  1. View.getContext,返回當前View物件的Context物件,通常是當前正在展示的Activity物件。
  2. Activity.getApplicationContext,獲取當前Activity所在的(應用)程序的Context物件,通常我們使用Context物件時,要優先考慮這個全域性的程序Context。
  3. ContextWrapper.getBaseContext():用來獲取一個ContextWrapper進行裝飾之前的Context,可以使用這個方法,這個方法在實際開發中使用並不多,也不建議使用。
  4. Activity.this 返回當前的Activity例項,如果是UI控制元件需要使用Activity作為Context物件,但是預設的Toast實際上使用ApplicationContext也可以。
public class MyActivity extends Activity {
    Context mContext;
    public void method() {

        mContext = this; //獲取當前Activity的上下文,如果需要繫結Activity的生命週期,使用它

        mContext=MyActivity.this;//獲取當前MyActivity的上下文,不方便使用this的時候推薦使用這種方式

        //呼叫Activity.getApplicationContext()
        mContext = getApplicationContext();//獲取當前Application的上下文,如果需要繫結應用的生命週期,使用它

        //Activity.getApplication()
        mContext = getApplication();//獲取當前Application的上下文,

        //呼叫ContextWrapper.getBaseContext()
        mContext = getBaseContext();//從上下文A內上下文訪問上下文A,不建議使用,如果需要,推薦使用XxxClass.this直接指出上下文
    }
}


public class MyView extends View {
    Context mContext;
    public void method() {

        //呼叫View.getContext()
        mContext = getContext(); //獲取這個View執行所在地的上下文
    }
}
  • this和getBaseContext()
    • this:代表當前,在Activity當中就是代表當前的Activity,換句話說就是Activity.this在Activity當中可以縮寫為this。Activity.this的context 返回當前activity的上下文,屬於activity ,activity 摧毀他就摧毀。
    • getBaseContext() 返回由建構函式指定或setBaseContext()設定的上下文。
  • getApplicationContext()和getApplication()
    • getApplicationContext 取得的是當前app所使用的application,這在AndroidManifest中唯一指定。意味著,在當前app的任意位置使用這個函式得到的是同一個Context
      • 返回應用的上下文,生命週期是整個應用,應用摧毀,它才摧毀。
    • getApplication():andorid 開發中共享全域性資料;
      • 只能在Activity和Service裡使用,指向的是Application物件,因為Application也是Context的一個子類,所以getApplication()可以被用來指向Context。

3.1 getApplicationContext()和getApplication()

Log.i("dyl", "getApplication is = " + myApp);
Log.i("dyl", "getApplicationContext is = " + appContext);

通過上面的程式碼,列印得出兩者的記憶體地址都是相同的,看來它們是同一個物件。

其實這個結果也很好理解,因為前面已經說過了,Application本身就是一個Context,所以這裡獲取getApplicationContext()得到的結果就是Application本身的例項。

那麼問題來了,既然這兩個方法得到的結果都是相同的,那麼Android為什麼要提供兩個功能重複的方法呢?

實際上這兩個方法在作用域上有比較大的區別:

  1. getApplication()方法的語義性非常強,一看就知道是用來獲取Application例項的 ** 但是這個方法只有在Activity和Service中才能呼叫的到。那麼也許在絕大多數情況下我們都是在Activity或者Service中使用Application的**
  2. 如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的例項,這時就可以藉助getApplicationContext()方法了

我們來看下原始碼的分析。

getApplicationContext方法的實現在ContextWrapper中,如下所示。

//frameworks/base/core/java/android/content/ContextWrapper.java
@Override
public Context getApplicationContext() {
    return mBase.getApplicationContext();
}

//mBase指的是ContextImpl,我們來檢視 ContextImpl的getApplicationContext方法:
//frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Context getApplicationContext() {
	//如果LoadedApk不為null,則呼叫LoadedApk的getApplication方法,否則呼叫AvtivityThread的getApplication方法。
	// 由於應用程式這時已經啟動,因此LoadedApk不會為null,則會呼叫LoadedApk的getApplication方法:
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}

//這裡的mApplication在下文LoadedApk的makeApplication方法的註釋5處被賦值
Application getApplication() {
     return mApplication;
 }

四、一個應用的Context個數

APP Context總數 = Application數(1) + Activity數(Customer) + Service數(Customer);

Context是一個抽象類,它的內部定義了很多方法以及靜態常量,它的具體實現類為ContextImpl。

和Context相關聯的類,除了ContextImpl還有ContextWrapper、ContextThemeWrapper和Activity等等,下面給出Context的關係圖。

在這裡插入圖片描述 [一個應用的Context個數]

從圖中我們可以看出:

  1. ContextImpl和ContextWrapper繼承自Context
    1. ContextImpl 實現了Context類的所有API。
    2. ContextWrapper內部包含有Context型別的mBase物件,mBase具體指向的是ContextImpl
      • ContextImpl提供了很多功能,但是外界需要使用並拓展ContextImpl的功能,因此設計上使用了裝飾模式,ContextWrapper是裝飾類,它對ContextImpl進行包裝,ContextWrapper主要是起了方法傳遞作用,ContextWrapper中幾乎所有的方法實現都是呼叫ContextImpl的相應方法來實現的
  2. ContextThemeWrapper、Service和Application都繼承自ContextWrapper,這樣他們都可以通過mBase來使用Context的方法,同時它們也是裝飾類,在ContextWrapper的基礎上又添加了不同的功能
    • ContextThemeWrapper中包含和主題相關的方法(比如: getTheme方法),因此,需要主題的Activity繼承ContextThemeWrapper,而不需要主題的Service則繼承ContextWrapper

4.1 ContextImpl

實現了Context類的所有API。

class ContextImpl extends Context {

    //所有Application程式公用一個mPackageInfo物件  
    /*package*/ 
    ActivityThread.PackageInfo mPackageInfo;  

    private Context mOuterContext;

    ......
}

4.2 ContextWrapper

ContextImpl的代理,該類的建構函式包含了一個真正的Context(ContextImpl物件)引用,ContextImpl的裝飾者模式

public class ContextWrapper extends Context {

    //該屬性指向一個ContextIml例項,一般在建立Application、Service、Activity時賦值
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    /**
     * 建立Application、Service、Activity,會呼叫該方法給mBase屬性賦值
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ......
}

4.2.1 ContextThemeWrapper

該類內部包含了主題Theme相關的介面,即android:theme屬性指定的。

/**
 * A ContextWrapper that allows you to modify the theme from what is in the 
 * wrapped context. 
 */
public class ContextThemeWrapper extends ContextWrapper {
   //該屬性指向一個ContextIml例項,一般在建立Application、Service、Activity時賦值  

     private Context mBase;  
    //mBase賦值方式同樣有一下兩種  
     public ContextThemeWrapper(Context base, int themeres) {  
            super(base);  
            mBase = base;  
            mThemeResource = themeres;  
     }  

     @Override  
     protected void attachBaseContext(Context newBase) {  
            super.attachBaseContext(newBase);  
            mBase = newBase;  
     }  
}

4.2.2 Activity、Service、Application類的繼承關係

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    ......
}


public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
    ......
}

public class Application extends ContextWrapper implements ComponentCallbacks2 {
    ......
}

五、Activity的Context建立過程

在這裡插入圖片描述 【Activity中ContextImpl例項化】

  1. 通過startActivity啟動一個新的Activity—>回撥ActivityThread的handleLaunchActivity()方法—>內部會呼叫performLaunchActivity()方法
  2. performLaunchActivity()方法—>呼叫createBaseContextForActivity(xx,xx)方法
  3. createBaseContextForActivity(xx,xx )中建立了ContextImpl物件,並且呼叫了contextImpl的setOuterContext(activity),將當前的Activity物件賦值給了內部成員變數mOuterContext
    • 所以到了這一步,ContextImpl類關聯了Activity
  4. 最後通過呼叫Activity.attach( xx,xx,·····)方法,將createBaseContextForActivity返回的ContextImpl物件傳入到ContextWrapper類的mBase變數
    • 這樣,ContextWrapper類的成員mBase就被例項化l

原始碼分析:

  • 【入口】:ActivityThread是應用程式程序的核心類,它的內部類ApplicationThread會呼叫scheduleLaunchActivity方法來啟動Activity
@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();
        r.token = token;
        ...
        sendMessage(H.LAUNCH_ACTIVITY, r);
}
  • 【步驟1】scheduleLaunchActivity方法會將啟動Activity的引數封裝成ActivityClientRecord ,sendMessage方法向H類傳送型別為LAUNCH_ACTIVITY的訊息,並將ActivityClientRecord 傳遞過去。
    • sendMessage方法的目的是將啟動Activity的邏輯放在主執行緒中的訊息佇列中,這樣啟動Activity的邏輯就會在主執行緒中執行
  • 【步驟2】H類的handleMessage方法中會對LAUNCH_ACTIVITY型別的訊息進行處理,其中呼叫了handleLaunchActivity方法,而handleLaunchActivity方法中又呼叫performLaunchActivity方法
    • 啟動流程一文中我們已經講過
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ...
      Activity activity = null;
      try {
          java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
          activity = mInstrumentation.newActivity(
                  cl, component.getClassName(), r.intent);//【1】
           ...
          }
      } catch (Exception e) {
         ...
      }

      try {
        ...
          if (activity != null) {
              Context appContext = createBaseContextForActivity(r, activity);//【2】
              ...
              /**
              *【3】
              */
              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); 
              ...
              if (r.isPersistable()) {
                  mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);//【4】
              } else {
                  mInstrumentation.callActivityOnCreate(activity, r.state);
              }
             ...
      }

      return activity;
  }
  • 【步驟3】performLaunchActivity
    • 註釋1處用來建立Activity的例項
    • 註釋2處通過createBaseContextForActivity方法用來建立Activity的ContextImpl
    • 將ContextImpl傳入註釋3處的activity的attach方法中
    • 註釋4處Instrumentation的callActivityOnCreate方法中會呼叫Activity的onCreate方法。
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
      ...
      ContextImpl appContext = ContextImpl.createActivityContext(
              this, r.packageInfo, r.token, displayId, r.overrideConfig);//1
      appContext.setOuterContext(activity);//2
      Context baseContext = appContext;
      ...
      return baseContext;
  }
  • 【步驟3.2】createBaseContextForActivity(xx,xx )中建立了ContextImpl物件,並且呼叫了contextImpl的setOuterContext(activity),將當前的Activity物件賦值給了內部成員變數mOuterContext
    • 在註釋1處呼叫ContextImpl的createActivityContext方法來建立ContextImpl
    • 註釋2處呼叫了ContextImpl的setOuterContext方法,將此前建立的Activity 例項賦值給ContextImpl的成員變數mOuterContext,這樣ContextImpl也可以訪問Activity的變數和方法
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) {
       attachBaseContext(context);//1
       mFragments.attachHost(null /*parent*/);
       mWindow = new PhoneWindow(this, window);//2
       mWindow.setWindowControllerCallback(this);
       mWindow.setCallback(this);//3
       mWindow.setOnWindowDismissedCallback(this);
       ...
       mWindow.setWindowManager(
               (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
               mToken, mComponent.flattenToString(),
               (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//4
       if (mParent != null) {
           mWindow.setContainer(mParent.getWindow());
       }
       mWindowManager = mWindow.getWindowManager();//5
       mCurrentConfig = config;
   }
  • 【步驟3.3】
    • 在註釋1處呼叫了ContextThemeWrapper的attachBaseContext方法
      • attachBaseContext方法接著呼叫ContextThemeWrapper的父類ContextWrapper的attachBaseContext方法
      • 將context賦值給ContextWrapper的成員變數mBase。這樣ContextWrapper的功能就可以交由ContextImpl處理
    • 在註釋2處建立PhoneWindow,它代表應用程式視窗。PhoneWindow在執行中會間接觸發很多事件,比如點選事件、選單彈出、螢幕焦點變化等事件,這些事件需要轉發給與PhoneWindow關聯的Actvity,轉發操作通過Window.Callback介面實現,Actvity實現了這個介面
    • 在註釋3處將當前Activity通過Window的setCallback方法傳遞給PhoneWindow。
    • 註釋4處給PhoneWindow設定WindowManager
    • 在註釋5處獲取WindowManager並賦值給Activity的成員變數mWindowManager ,這樣在Activity中就可以通過getWindowManager方法來獲取WindowManager。

在這裡插入圖片描述 【ActivityThread到ContextWrapper的呼叫時序圖】

六、Service的Context建立過程

Service的Context建立過程與Activity的Context建立過程類似,也是在Service的啟動過程中被建立

  1. 通過startService啟動一個新的Activity—>回撥ActivityThread的handleCreateService()方法建立了ContextImpl物件,並賦值mOuterContext,產生關聯。
  2. 呼叫Service.attach方法,將ContextImpl物件傳入到ContextWrapper類的mBase變數

原始碼分析:

  • 【入口】ActivityThread的內部類ApplicationThread會呼叫scheduleCreateService方法來啟動Service
public final void scheduleCreateService(IBinder token,
         ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
     ...
     sendMessage(H.CREATE_SERVICE, s);
 }
  • 【步驟1】sendMessage方法向H類傳送CREATE_SERVICE型別的訊息,H類的handleMessage方法中會對CREATE_SERVICE型別的訊息進行處理,其中呼叫了handleCreateService方法
    • 在註釋1處建立了ContextImpl
    • 並將該ContextImpl傳入註釋2處service的attach方法中
private void handleCreateService(CreateServiceData data) {
     ...
       try {
           if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
           ContextImpl context = ContextImpl.createAppContext(this, packageInfo);//【1】
           context.setOuterContext(service);
           Application app = packageInfo.makeApplication(false, mInstrumentation);
           service.attach(context, this, data.info.name, data.token, app,
                   ActivityManagerNative.getDefault());//【2】
           service.onCreate();
         ...
       } catch (Exception e) {
         ... 
       }
   }

七、Application Context的建立過程

看到這裡,基本你已經發現了ContextImpl例項化基本都會遇到的步驟:

  1. 在ActivityThread執行緒回撥中建立ContextImpl物件,並把自身賦值給內部的mOuterContext物件,產生關聯
  2. 最後經過裡面一系列的方法傳遞,呼叫相應的attach,將ContextImpl物件傳入到ContextWrapper類的mBase變數

Application Context的建立過程也是類似的:

  1. 一個APP以後每次重新啟動時都會首先建立Application物件(每個APP都有一個唯一的全域性Application物件,與整個APP的生命週期相同)
  2. 建立Application—>回撥ActivityThread的handleBindApplication()方法
  3. 呼叫該方法中的LoadedApk類的makeApplication方法建立ContextImpl物件,,並賦值mOuterContext,產生關聯。
  4. 中間還有一系列的attach傳遞
  5. 最後呼叫Application類的attach方法,ContextImpl物件傳入到ContextWrapper類的mBase變數
  • 【入口】ActivityThread啟動Activity。ActivityThread作為應用程式程序的核心類,它會呼叫它的內部類ApplicationThread的scheduleLaunchActivity方法來啟動Activity,如下所示。
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);
    }
 ...   
}
  • 【步驟1】在ApplicationThread的scheduleLaunchActivity方法中向H類傳送LAUNCH_ACTIVITY型別的訊息,目的是將啟動Activity的邏輯放在主執行緒中的訊息佇列中,這樣啟動Activity的邏輯會在主執行緒中執行。

我們接著檢視H類的handleMessage方法對LAUNCH_ACTIVITY型別的訊息的處理


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;
            
            ...
}
  • 【步驟2】H繼承自Handler ,是ActivityThread的內部類。
    • 在註釋1處通過getPackageInfoNoCheck方法獲得LoadedApk型別的物件,並將該物件賦值給ActivityClientRecord 的成員變數packageInfo,其中LoadedApk用來描述已載入的APK檔案。
    • 在註釋2處呼叫handleLaunchActivity方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
     Activity a = performLaunchActivity(r, customIntent);
    ...
 }
  • 【步驟3】performLaunchActivity方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
       ...
    } 
    ...
    return activity;
}
  • 【步驟4】這裡ActivityClientRecord 的成員變數packageInfo是LoadedApk型別的,我們接著來檢視LoadedApk的makeApplication方法
    • 註釋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
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;
}
  • 【步驟4.3】newApplication
    • 註釋1處通過反射來建立Application,並呼叫了Application的attach方法,並將ContextImpl傳進去
static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();//1
    app.attach(context);
    return app;
}

在這裡插入圖片描述 【Application Context建立過程的時序圖】

參考文獻