天天看點

Context的建立過程

一、Context

Context是Android中的上下文環境,頁面跳轉、開啟服務、發送廣播、彈框、通路資源、擷取系統服務等等,很多的操作都需要Context。其中Activity、Service、Application都是Context的子類,一個應用中Context執行個體個數=Activity執行個體個數+Service執行個體個數+1(Application)。平時在開發過程中都是直接擷取并使用上下文的,很少去思考過它們的真身,這裡就整理一下這三種Context的建立過程,以便于更加深入的去了解Context。

二、Activity的Context執行個體建立

在Activity啟動的時候《Activity的啟動過程》會調用到performLaunchActivity方法裡,部分關鍵代碼如下(ActivityThread類中):

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

       //注釋1
	   ContextImpl appContext = createBaseContextForActivity(r);
	   appContext.setOuterContext(activity);

	   //注釋2
	   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);   
	                        
}
           

由注釋1處可知,createBaseContextForActivity方法傳回一個ContextImpl對象,在注釋2處将該對象作為activity的attach方法的參數,createBaseContextForActivity方法是如何建立ContextImpl對象的呢,部分關鍵代碼如下:

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {

		//注釋3
		ContextImpl appContext = ContextImpl.createActivityContext(
		                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
		                
	    return appContext;
}
           

由注釋3處可以看到,調用了ContextImpl的createActivityContext方法,跟到這個方法裡面檢視,部分關鍵代碼如下(ContextImpl類中):

static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
            
          //注釋4
          ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
              activityToken, null, 0, classLoader);
}
           

由注釋4處可以看到,在這裡真正的建立了ContextImpl對象。那麼回到注釋2處可知activity的attach方法中的第一個參數便是該ContextImpl執行個體對象,接着進入到activity中的attach方法裡發現又調用了attachBaseContext方法,其中關鍵代碼如下(Activity中):

@Override
protected void attachBaseContext(Context newBase) {

	  //注釋5
      super.attachBaseContext(newBase);
      
      if (newBase != null) {
          newBase.setAutofillClient(this);
      }
 }
           

在注釋5處,調用了Activity的父類ContextThemeWrapper的方法,繼而又調用了ContextThemeWrapper的父類ContextWrapper的attachBaseContext方法,在包裝類ContextWrapper的attachBaseContext方法中,将建立的ContextImpl執行個體對象指派給成員變量mBase儲存起來,mBase的具體實作類是ContextImpl,是以在Activity裡使用的Context對象就是mBase,調用的Context的方法的具體實作是在ContextImpl類中的,部分關鍵代碼如下(ContextWrapper中):

Context mBase;

protected void attachBaseContext(Context base) {
       if (mBase != null) {
           throw new IllegalStateException("Base context already set");
       }
       mBase = base;
}
           
三、Service的Context建立

在建立service服務的時候《Service的啟動過程》會調用到ActivityThread的handleCreateService方法,關鍵代碼如下:

private void handleCreateService(CreateServiceData data) {

		//注釋6
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        
        context.setOuterContext(service);
        Application app = packageInfo.makeApplication(false, mInstrumentation);

		//注釋7
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
                
        service.onCreate();
}
           

在注釋6處調用了ContextImpl的createAppContext方法建立ContextImpl執行個體對象,關鍵代碼如下(ContextImpl中):

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");

		//注釋8
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                null);
                
        context.setResources(packageInfo.getResources());
        return context;
}
           

在注釋8處,建立了ContextImpl執行個體對象,并将該對象作為注釋7處service.attach方法的一個參數,下面看下Service的attach方法,關鍵代碼如下(Service中):

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
            
        //注釋9
        attachBaseContext(context);
}
           

在注釋9處調用了Service的attachBaseContext方法,由于Service繼承了ContextWrapper,其attachBaseContext方法會調用ContextWrapper的attachBaseContext方法,并将context指派給mBase作為成員變量儲存起來,mBase的具體實作類同樣是ContextImpl,在Service裡使用的Context對象就是mBase,調用的Context方法的具體實作是在ContextImpl類中的。

四、Application的Context建立

Android應用程式的入口方法是ActivityThread的main方法,在main方法裡會調用到ActivityThread的attach方法,如果不是系統程序的話,第一個參數為false,也就是說我們隻關心if裡面的代碼即可,attach的部分關鍵代碼:

private void attach(boolean system, long startSeq) {
	 final IActivityManager mgr = ActivityManager.getService();
	            try {
					//注釋10
	                mgr.attachApplication(mAppThread, startSeq);
	            } catch (RemoteException ex) {
	                throw ex.rethrowFromSystemServer();
	            }
}
           

mgr是AMS的一個binder代理對象,是以在注釋10處調用的attachApplication方法具體實作是在AMS裡,下面看下具體實作的代碼(AMS中):

@Override
 public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            
			//注釋11
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            
            Binder.restoreCallingIdentity(origId);
        }
 }
           

在注釋11處調用了attachApplicationLocked方法,在attachApplicationLocked方法中又調用了IApplicationThread的bindApplication方法,IApplicationThread的具體實作是ApplicationThread類,那麼bindApplication的具體實作是:

public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, boolean autofillCompatibilityEnabled) {
                

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;

			//注釋12
            sendMessage(H.BIND_APPLICATION, data);
        }
}
           

在bindApplication方法中,将包名等App資訊封裝為AppBindData資料,在注釋12處發送一個handler消息給H處理,H類中收到BIND_APPLICATION的消息,會調用ActivityThread的handleBindApplication方法:

private void handleBindApplication(AppBindData data) {
 
	 Application app;
	 
	 //注釋13
	 app = data.info.makeApplication(data.restrictedBackupMode, null);
}
           

注釋13處的data.info是LoadedApk對象,即調用了LoadedApk的makeApplication方法,部分關鍵代碼:

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
            
            Application app = null;

			//注釋14
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

        	//注釋15
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
}
           

在注釋14處調用了ContextImpl的createAppContext方法建立一個ContextImpl 執行個體對象:

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                null);
        context.setResources(packageInfo.getResources());
        return context;
}
           

在注釋15處調用了Instrumentation的newApplication方法,并将建立的ContextImpl 執行個體對象作為newApplication的參數傳遞進去:

public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);

		//注釋16
        app.attach(context);
        return app;
}
           

在注釋16處調用了Application的attach方法,并将context對象傳遞過去:

final void attach(Context context) {

		//注釋17
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
           

在注釋17處調用了Application的attachBaseContext方法,然後會調用Application的父類ContextWrapper的attachBaseContext方法,并将context指派給mBase作為成員變量儲存起來,mBase的具體實作類同樣是ContextImpl。

當需要擷取全局的上下文對象ApplicationContext時,會調用getApplicationContext()方法或者getApplication方法,不管是調用getApplicationContext()還是getApplication,最終傳回的都是同一個Application對象,這裡以getApplicationContext為例,來分析下ApplicationContext的擷取和使用,至于getApplication的調用過程感興趣的同學可以自行檢視下源碼,這裡就不在進行分析了。不管是在activity中,還是在service中,還是通過context直接調用,最後都會調用到ContextWrapper的getApplicationContext方法:

@Override
public Context getApplicationContext() {

	  //注釋18
      return mBase.getApplicationContext();
}
           

在注釋18處調用了mBase的getApplicationContext()方法,而mBase的具體實作是ContextImpl,ContextImpl的getApplicationContext方法如下:

@Override
public Context getApplicationContext() {

	//注釋19
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}
           

在注釋19處mPackageInfo是LoadedApk對象,從前面的調用鍊可知LoadedApk對象不為空,那麼就會調用LoadedApk的getApplication方法:

Application getApplication() {
    return mApplication;
}
           

LoadedApk的getApplication方法會傳回其儲存的mApplication對象,這個對象是全局唯一的。綜上所述, 開發中調用的getApplication()和 getApplicationContext()傳回的是同一個對象,那就是全局唯一的Application對象,Application是Context的子類,當調用Application的方法時,其實就是在調用Context的方法,比如getApplicationContext().startActivity(),最終的調用的是ContextWrapper的mBase的方法,由注釋17可知,mBase是在Application的attach方法中儲存的一個ContextImpl對象執行個體,是以最終調用的是ContextImpl的startActivity方法。相比于另外兩種activity和service的上下文對象,這個ContextImpl執行個體是全局唯一的,其生命周期跟應用程式的生命周期一緻。