一、Context
1. 定义
Context,翻译为上下文,也可以理解为环境,是提供一些程序的运行环境基础信息。和java程序不同,Android程序需要有一个完整的Android工程环境,在这个环境下,有像Activity、Service、BroadcastReceiver等系统组件,而这些组件要有它们各自的上下文环境,也就是Context。可以说Context是维持Android程序中各组件能够正常工作的一个核心功能类。
它是一个抽象类,是一个应用程序环境信息的接口。里面定义了各种抽象方法,包括获取系统资源、获取系统服务,发送广播,启动Activity,Service等。所以从源码角度看Context就是抽象出一个App应用所有功能的集合。
2. Context的使用场景
使用Context调用方法,比如启动Activity、访问资源、调用系统级服务等
调用方法时传入Context,比如弹出toast,创建dialog等等。
3. Context的关联类
它的关联类(直接子类)有:ContextWrapper(Context的封装类) ContextImpl(Context的实现类),接下来的类都是继承自ContextWrapper这个装饰类的具体装饰类,包括ContextThemeWrapper、 Application和Service。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity。也就是说,Context一共三种类型,包括Application、Service和Activity。
下面这张图展示了Context的关联类之间的关系:
Context的几个子类在能力上的区别
数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。
ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。
从表格中可以看到,和UI相关的方法基本都不建议或者不可使用 Application,并且,前三个操作基本不可能在Application中出现。实际上,凡是跟UI相关的,都应该使用 Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以。
Context可以实现很多功能,例如弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等都需要用到Context。
TextView tv = new TextView(getContext());
ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);getApplicationContext().getSharedPreferences(name, mode);
getApplicationContext().getContentResolver().query(uri, ...);
getContext().getResources().getDisplayMetrics().widthPixels * 5 / 8;
getContext().startActivity(intent);
getContext().startService(intent);
getContext().sendBroadcast(intent);
由于Context的具体能力是由ContextImpl类去实现的,所以在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity, 还有弹出Dialog、导入布局文件。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。
ps:在Activity中某个地方需要弹出对话框时,通常会构造一个AlertDialog然后show一下,其中在构造AlertDialog的时候,传递了当前的Activity作为上下文context。
二、源码中的Context
1. Context类
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
......
}
从源码注释可以看出,Context提供了关于应用程序环境的全局信息的接口。Context是一个抽象类,其实现由Android系统提供。通过它我们可以访问特定于应用程序的资源和类【以及对应用程序级操作(如启动活动、广播和接收intents)的调用)】
抽象类,提供了一组通用的API。
2. ContextImpl实现类
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private Context mOuterContext;
......
}
注释说明:ContextImpl是对Context类的所有API的通用实现,并且为Activity和其他应用程序的组件提供了base Context object。(提供了Context类的对象mBase)
3. ContextWrapper包装类
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
......
}
Context的代理实现,这里的实现只是简单的把它所有的调用委派给另一个context(也就是ContextWrapper源码里面的所有方法实现都是通过调用mBase的同名方法,也就是委派给ContextImpl)。可以在不更改原始Context类的情况下,子类化以修改行为(添加新的方法或者是删除某些方法)。
ContextWrapper类的构造函数包含了一个真正的Context引用(ContextImpl对象),然后就变成了ContextImpl的装饰者模式。
4. ContextWrapper的子类ContextThemeWrapper
* A ContextWrapper that allows you to modify the theme from what is in the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
......
}
在Wrapperd context的基础上允许修改主题;该类的内部包含了主题Theme相关的接口。
三、Context的创建
1. Application context的创建过程
-
ActivityThead类(应用程序进程的主线程管理类),会调用其内部类ApplicationThread的scheduleLaunchActivity方法来启动Activity。
scheduleLaunchActivity方法内容:
updateProcessState(); ActivityClientRecord r=new ActivityClientRecord(); //新建一个记录对象,用来存放启动信息 ... //给r赋值 sendMessage(H.LAUNCH_ACTIVITY,r); //向H类发送r消息
ActivityClientRecord是activity的一个记录类,记录了很多启动activity的信息,暂且不管。
H 是 ActivityThread 内部类,继承自 Handler。
H类接收到消息之后,在handleMessage方法中对它进行了处理。
handleMessage方法主要代码:r.packageInfo=getPackageInfoNoCheck(r.activityInfo.applicationInfo,r.compatInfo); handleLaunchActivity(r,null,"LAUNCH_ACTIVITY");
第一行通过一个get方法从消息r中获取到LoadedApk类型的对象,并且把值赋给ActivityClientRecord记录类r的成员变量packageInfo, LoadedApk用来描述已加载的APK文件。
第二行调用了ActivityThread中的handleLaunchActivity方法,其具体内容如下:Activity a=performLauchActivity(r, customIntent);
上面同样是调用了ActivityThread中的另一个方法:performLauchActivity,其具体内容如下:
//此方法返回一个Activity类的对象 try{ Application app=r.packageInfo.makeApplication(false, mInstrumentation);//r.packageInfo上面已经赋值了 ... } ... return activity;
可以看到,上面调用了LoadedApk的makeApplication方法,其内容如下:
//返回一个Application类的对象 if(mApplication!=null) { return mApplication; //这里是第一次启动,所以为空,不会返回;但是如果不是第一次启动,就会直接返回 } ContextImpl appContext=ContextImpl.createAppContext(mActivityThread,this); //2 app=mActivityThread.mInstrumentation.newApplication(cl,appClass,appContext);//3 appContext.setOuterContext(app);//4 mApplication=app; //5
注释2处通过ContextImpl的createAppContext方法来创建ContextImpl。
注释3处代码用于创建application, 在Instrumentation的newApplication方法中传入了ClassLoader类型的对象以及注释2处创建的ContextImpl。
在注释4处把注释3中创建出来的Application赋值给ContextImpl的Context类型的成员变量mOuterContext,也就是让ContextImpl内部持有外部Application类的引用,用于注册系统服务或者是其他方法。
最后,通过mApplication=app 将Application赋值给LoadedApk的成员变量mApplication。
接下来,看一下注释3处的Application是如何创建的,Instrumentation的newApplication方法:Application app=(Application)clazz.newInstance(); app.attach(context); return app;
通过反射来创建Application,并且调用了Application中的attach方法,将ContextImpl传进去,最后返回Application。
attach方法的主要内容是:attachBaseContext(context); mLoadedApk=ContextImpl.getImpl(context).mPackInfo;
attchBaseContext方法在Application的父类ContextWrapper中实现,代码如下:
protected void attachBaseContext(Context base){ if(mBase!=null){ throw new IllegalStateException("Base context already set"); } mBase=base; //把ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase }
这个base一路传递过来指的是ContextImpl,它是Context的实现类。
Application通过父类ContextWrapper类的成员变量mBase指向了ContextImpl,让Application类真正实现了其祖父类Context抽象类中的所有抽象方法,这个过程是通过attachBaseContext方法来实现的。
把ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase,这样在ContextWrapper中就可以使用Context的方法,而Application继承自ContextWrapper,同样可以使用Context的方法。
2. Activity context的创建过程
如果想要在Activity中使用Context提供的方法,务必要先创建Context。Activity的Context会在Activity的启动过程中被创建。
上面已经说过,ActivityThread会调用scheduleLaunchActivity方法来启动Activity,此方法把启动Activity的参数封装成ActivityClientRecord,sendMessage方法向H类发送类型为LAUNCH_ACTIVITY的消息,并将ActivityClientRecord传递过去。sendMessage方法的目的将启动Activity的逻辑放在主线程的消息队列中,这样启动Activity的逻辑就会在主线程中执行。然后H类的handleMessage方法会对LAUNCH_ACTIVITY类型的消息进行处理,其中调用了ActivityThread的handleLaunchActivity方法,而在handleLaunchActivity方法中又调用了ActivityThread的performLaunchActivity方法,在此方法中首先通过createBaseContextForActivity方法来创建Activity的ContextImpl,然后再将ComImpl传入activity的attach方法中,并且调用了ContextImpl的setOuterContext方法,将此前创建的Activity实例赋值给ContextImpl的成员变量mOuterContext,这样ContextImpl也可以访问Activity的变量和方法。
Activity的attchBaseContext方法其父类ContextThemeWrapper中实现,然后此方法接着调用ContextThemeWrapper的父类ContextWrapper的attachBaseContext方法。
总结:在启动Activity的过程中创建ContextImpl, 并赋值给ContextWrapper的成员变量mBase。Activity继承自ContextWrapper的子类ContextThemeWrapper, 这样在Activity中就可以使用Context中定义的方法了。
3. Service的Context创建过程
与Activity的Context创建过程类似,是在Service的启动过程中被创建的。
总结:在Service的启动过程中创建ContextImpl,并且将ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase, 这样可以在ContextWrapper中就可以使用Context的方法,而Service继承自ContextWrapper,同样可以使用Context的方法。