Android上下文Context的那些小事

前言

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

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

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

理解Context

Context关联类

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

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。ui

Context的类型

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

  • 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的建立过程。spa

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的建立以及相关类的关联和使用有了更加清晰的了解。在开发中用到的时候可以使用准确。

相关文章
相关标签/搜索