Context的创建过程

Android 专栏收录该内容
49 篇文章 0 订阅
一、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实例是全局唯一的,其生命周期跟应用程序的生命周期一致。

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值