Android上下文Context的那些小事

1,512 阅读6分钟

前言

Context作为Android中的上下文对象,是Android常用的类。启动四大组件、创建视图、获取系统服务、访问资源等都要用到Context。

从Context有上下文的意思,结合Context的职能。可以看出,Context在Android中提供了一个“语境”的意义,它提供了应用程序环境的全局信息。在这个“语境”中可以通过Context使用相应的接口,做符合当前“语境”意义的事。比如,在Activity中创建Dialog、弹出Toast。

这篇文章将继续深入了解Context。

理解Context

Context关联类

上面说到,Context意为上下文,提供了应用程序环境信息。

Context作为一个抽象类,它的实现有Android系统提供。我们熟知的有ContextImpl、ContextWrapper、Application、Activity、Service等。

从Context的类图中可以看出,ContextImpl和ContextWrapper都是继承自Context,在ContextWrapper中依赖了ContextImpl对象(mBase)。这里使用了装饰者模式,ContextWrapper是装饰类,对ContextImpl进行包装,通过使用ContextImpl实现功能。

Application、Service、ContextThemeWrapper继承自ContextWrapper,它们也是通过ContextImpl实现功能,同时在ContextWrapper的基础上添加了自己的功能。此外ContextThemeWrapper中包含了主题相关的方法,Activity继承自ContextThemeWrapper。

Context的类型

在Context的关联类中可以看到,Context类有不同的实现,有Application、Activity、Service三种。除此之外,还有BroadcastReceiver以及ContentProvider。这两个组件不是Context的子类,但是对于BroadcastReceiver来说,在onReceive()时会接收一个Context。静态注册方式下的BroadReceiver在创建时传递的是ReceiverRestrictedContext。

  • Application-是运行在应用程序中的单例,每个应用进程中只存在一个Application实例。可以通过getApplication()方法在Activity或者Service中获取Application实例,或者通过getApplicationContext()方法在其他继承了Context的类中获取实例。
  • Activity-继承自ContextThemeWrapper,在ContextWrapper的基础上有增加了对主题相关的支持。
  • Service-继承自ContextWrapper,除了主题之外与Activity有相同的API。
  • BroadcastReceiver-本身不是一个Context,但在onReceive()方法中会接收一个Context。此外,不同的注册方式Context不一样。静态注册方式时接收的Context是ReceiverRestrictedContext。这个Context不支持registerReceiver()以及bindService()两个方法。动态注册方式的Context来自ContextImpl的mOutContext,这个取决于BroadcastReceiver的注册者,可能是Activity或者Application。
  • ContentProvider-本身不是Context,但在创建时可以通过getContext()访问。如果ContentProvider在相同应用程序进程运行,然后这将实际返回相同的Context。但是,如果两者(Content和Provider)都在单独的进程中,那么getContext()返回各自新创建的Context。

Context的创建过程

在Android程序中ActivityThread是应用进程的主线程管理类,Android四大组件最终都是从这里开始创建。一下就分析一下各个Context的创建过程。

Application Context的创建过程

在ActivityThread的performLaunchActivity()方法中,创建Application。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	try {
		//创建Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    }
}

makeApplication是类LoadedApk的成员方法。

/**LoadedApk.java*/
public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            //创建ContextImpl对象
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //创建Application对象
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //设置ContextImpl的mOuterContext成员变量
            appContext.setOuterContext(app);
        } catch (Exception e) {
            //......
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app; //赋值
		//..........
        return app;
    }

/**ContextImpl.java*/
//创建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;
    }

在makeApplication()方法中创建了Application对象,上面说到过Context都是依赖ContextImpl实现功能,在这里也创建了ContextImpl对象,将Application保存到了ContextImpl的成员变量mOuterContext,同时Application将mBase指向了ContextImpl对象。

//mBase绑定ContextImpl对象的过程
/**Application.java*/
final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
/**ContextWrapper.java*/
protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
//获取ContextImpl对象
public Context getBaseContext() {
    return mBase;
}

Activity Context的创建过程

在ActivityThread的performLaunchActivity()方法中,创建Activity。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	//创建ContextImpl对象
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
		//创建Activity
        java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
    }
    	//......
    	if (activity != null) {
                //......
                //设置ContextImpl的mOuterContext成员变量
                appContext.setOuterContext(activity);
            	//绑定ContextImpl
                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);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
                //......
            }
    
     //......
}
//创建ContextImpl对象
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        //......
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); //ContextImpl.createActivityContext()方法执行了创建过程
    	//......
        return appContext;
    }

可以看到在performLaunchActivity()方法中创建了Activity对象,同时也创建了用于Activity的ContextImpl对象。并且执行了Context的绑定过程。

/**Activity.java*/
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);
            }
//绑定ContextImpl对象
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
    newBase.setAutofillClient(this);
}
//获取ContextImpl对象
public Context getBaseContext() {
    return mBase;
}

Service Context的创建过程

在ActivityThread中的handleCreateService()方法中创建Service。

private void handleCreateService(CreateServiceData data) {
	try {
        //创建ContextImpl对象
		ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service); //设置ContextImpl的mOuterContext成员变量

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        //绑定ContextImpl对象
        service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        //......
    }
    //......
}
/**Service.java*/
public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context); //绑定ContextImpl到mBase
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

/**ContextWrapper.java*/
protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
//获取ContextImpl对象
public Context getBaseContext() {
        return mBase;
    }

可以看到在handleCreateService()方法中创建了Service对象,同时也创建了用于Service的ContextImpl对象。并且执行了Context的绑定过程。


以上讲到了Application、Activity、Service中Context的创建过程,可以看到Context的创建过程正好映照了Context的类图之间的关联。通过ContextImpl统一实现Context的功能。

Context的功能

Application Activity Service BroadcastReceiver ContentProvider
显示对话框 × × × ×
启动Activity - - - -
创建布局 × × - ×
启动Service
绑定Service -
发送广播 -
注册广播 -
获取资源

Tips:(√:表示可以执行;×:表示不可以执行;-:表示视情况而定)

  1. 从表中可以看出任何Context都是可以获取到资源数据的,这里的资源就是res目录下的资源,比如:字符串、颜色、尺寸等。

  2. 对于跟UI有关的,只有Activity的Context可以执行,比如:创建Dialog或者使用LayoutInflater创建布局。因为UI相关的需要使用主题(Theme),而只有Activity继承的ContextThemeWrapper拥有主题相关的方法。

  3. 对于启动Activity来说,除了Activity Context以外。其他的Context启动Activity时需要加入FLAG_ACTIVITY_NEW_TASK标志。因为在启动Activity需要获取到当前的栈信息。而其他的Context没有栈信息,所以这里加入FLAG_ACTIVITY_NEW_TASK,起到的作用是重新创建一个Activity栈。

  4. BroadcastReceiver分为静态注册和动态注册,这两种方式创建BroadCastReceiver实例的过程不一样。

    • 其中,静态注册的广播使用的是ReceiverRestrictedContext,这种Context不允许执行bindService(),会直接抛出ReceiverCallNotAllowedException异常。同时也对registerReceiver()有限制,当receiver == null用于获取sticky广播, 允许使用。否则也会抛出ReceiverCallNotAllowedException异常。
    • 动态注册的广播在onReceive()方法中接收到的context是来自ContextImpl的mOuterContext,所以取决于启动它的Context。

总结

通过上面对Contex的相关知识的学习,对Context的创建以及相关类的关联和使用有了更加清晰的了解。在开发中用到的时候能够使用准确。