Fork me on GitHub

Context笔记


meaning:上下文,语境;
—– 一个Android应用并不像Java程序那样有个main()方法就能跑,而是需要一个完整的工作环境,在这样大的环境下,我们有Activity、Service、BroadcastReceiver等系统组件,这些组件都拥有自己独立的场景来实现,这个场景就是Context,它用来维持Android程序中各组件能够正常工作的一个核心功能类。


官方文档
`/**

  • 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类是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。

继承关系
悄悄(借)…来的图

—– ContextWrapper是上下文功能的封装类,ContextImpl则是上下文功能的实现类,ContextThemeWrapper是一个带主题的封装类(android:theme=….Activity提供UI显示,所以需要主题)。

—–context数量=activity数量+service数量+1(全局的application对象)。

—–Context的具体能力是由ContextImpl类去实现的,
ContextWrapper类:

/**
 * 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;
}


public Context getBaseContext() {
    return mBase;
}
 @Override
public Resources getResources()
{
    return mBase.getResources();
}
 @Override
public SharedPreferences getSharedPreferences(String name, int mode) {
    return mBase.getSharedPreferences(name, mode);
}
.
.
.

—–在attachBaseContext()方法中传入了一个base参数,并把这个参数赋值给了mBase对象。而attachBaseContext()方法其实是由系统来调用的,它会把ContextImpl对象作为参数传递到attachBaseContext()方法当中,从而赋值给mBase对象,之后ContextWrapper中的所有方法其实都是通过这种委托的机制交由ContextImpl去具体实现的,所以说ContextImpl是上下文功能的实现类是非常准确的。

—–在创建Activity、Service和Application的时候,都会在ActivityThread中调用相关的函数将信息传给ContextImpl,也通过这种绑定赋予Context。

—–因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

Context作用域

Context的作用太多了,比如弹出Toast、启动Activity、启动Service、发送广播、操作数据库、调用各种资源等等都需要用到Context。但是不同的Context作用适用的范围不同。

此处输入图片的描述

可见Activity的context作用域最广,而且牵扯到UI操作的context最好都用activity。

获得Context

通常我们想要获取Context对象,主要有以下四种方法
—1:View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
—2:Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
—3:ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
—4:Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使用ApplicationContext也可以。

正确的使用Context

Context使用最容易出现的问题就是内存泄漏(动态分配的内存没有及时的回收),就是当Context要销毁的时候,却因为引用导致销毁失败。

使用Context的正确姿势:
—-当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
—不要让生命周期长于Activity的对象持有到Activity的引用。