Context及其子类源码分析(一)

看不明白UML的人,请移步不会用StarUML画类图的程序媛不是优秀的程序媛(一)

(一)Context类及其子类关系图如下图:

我们看源码会发现Context是个纯抽象类,ContextImpl和ContextWrapper两个类实现了Context的抽象方法。

Context提供了关于应用环境全局信息的接口。从Android系统角度来理解,Context是一个场景,代表与操作系统交互的一个过程,Context允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。从代码角度来看,Context是一个纯抽象类。

ContextImpl是Context的具体实现类,源码位于android.app包中。但它只允许与它同包的类才能调用它。原本我写了如下代码,但是提示不存在ContextImpl这个类。


当然了,实际在Android中,Activity并不是这样简单一句话就创建的。在后面我们会有所分析。

ContextWrapper是Context类的实现,ContextWrapper中实现Context的方法全是通过mBase来实现的。这样ContextWrapper派生出的子类就可以在不改变原始Context(mBase)的情况下扩展Context的行为。即ContextWrapper构造函数中包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。(多理解两遍这段话)

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

ContextThemeWrapper内部包含了与主题Theme相关的接口。这个类扩展的功能就是允许我们去修改或者替换包装的Context的主题。而Activity、Service和Application中,只有Activity需要用到Theme,所以Activity继承自ContextThemeWrapper,其他两个直接继承于ContextWrapper。

/**
 * A context wrapper that allows you to modify or replace the theme of the
 * wrapped context.
 */
public class ContextThemeWrapper extends ContextWrapper {
           ...
}

(二)Activity中ContextImpl实例化源码分析

以下分析内容全部来自于Android应用Context详解及源码解析
因为原作者这部分内容实在写的太精彩了,无法删减任何一句话,因此在这表达对原作者的感谢!
Context的真正实现是ContextImpl,Activity、Service和Application的创建都是在ActivityThread中完成的。ActivityThread的handleLaunchActivity()方法会调用performLaunchActivity()方法去创建一个Activity实例。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
            //已经创建好新的activity实例
            if (activity != null) {
                //创建一个Context对象
                Context appContext = createBaseContextForActivity(r, activity);
                ......
                //将上面创建的appContext传入到activity的attach方法
                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);
                //通过createBaseContextForActivity(r, activity);
                //创建appContext,然后通过activity.attach设置值。
                ...
            }
        ...
        return activity;
    }  
    private Context createBaseContextForActivity(ActivityClientRecord r,
            final Activity activity) {
        //实质就是new一个ContextImpl对象,调运ContextImpl的有参构造初始化一些参数    
        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
        //特别特别留意这里!!!
        //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Activity对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Activity)。
        appContext.setOuterContext(activity);
        //创建返回值并且赋值
        Context baseContext = appContext;
        ......
        //返回ContextImpl对象
        return baseContext;
    }

再来看看activity.attach,也就是Activity中的attach方法,如下:

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) {
        //特别特别留意这里!!!
        //与上面createBaseContextForActivity方法中setOuterContext语句类似,不同的在于:
        //通过ContextThemeWrapper类的attachBaseContext方法,将createBaseContextForActivity中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

通过上面Activity的Context实例化分析再结合上面Context继承关系可以看出:

Activity通过ContextWrapper的成员mBase来引用了一个ContextImpl对象,这样,Activity组件以后就可以通过这个ContextImpl对象来执行一些具体的操作(启动Service等);同时ContextImpl类又通过自己的成员mOuterContext引用了与它关联的Activity,这样ContextImpl类也可以操作Activity。
下一篇:装饰者模式之Context应用(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容