Context,ContextImpl等的一些理解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.jianshu.com/p/674682b1976f

背景:我们经常在Activity中会得到一些系统服务或者得到Resources,比如ACTIVITY_SERVICE,常常获取的方式有好几种,如下:

//得到ACTIVITY_SERVICE
getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
this.getSystemService(Context.ACTIVITY_SERVICE);
getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
//得到Resources
getResources();
getBaseContext().getResources();
getApplicationContext().getResources();

这时候就有一种疑问,最后返回的是同一个对象吗,如果不是,又会有什么差异。
要想弄清上面的问题,先得明白Context,ContextImpl,ContextWrapper,ContextThemeWrapper,Activity,Application等等的关系。

public abstract class Context {
        public abstract Resources getResources();
        public abstract Object getSystemService(@ServiceName @NonNull String name);
}

class ContextImpl extends Context {
    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
    @Override
    public Resources getResources() {
        return mResources;
    }
}

public class ContextWrapper extends Context {
    Context mBase;
    @Override
    public Resources getResources() {
        return mBase.getResources();
    }
    @Override
    public Object getSystemService(String name) {
        return mBase.getSystemService(name);
    }
}

public class ContextThemeWrapper extends ContextWrapper {
    @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }
}

public class Activity extends ContextThemeWrapper{
}

从上面的代码可以知道,Context是一个抽象类,它的很多方法,比如getResources,getSystemService等都是抽象方法。ContextWrapper类和ContextImpl类都继承了Context,但是他们的实现方法不一样。ContextWrapper中主要是调用mBase对应的方法,而mBase也是一个Context对象。
其实通过研读ActivityThread的performLaunchActivity(参考https://www.jianshu.com/p/76f94c6452c0)我们不难明白,Activity中对应的Context实例对象是其实都是ContextImpl对象,ContextImpl也是真正实现Context的唯一对象。且ContextWrapper中的mBase对象实际上也是ContextImpl。ContextWrapper顾名思义,它只是Context的一个包裹,最终调用到的也是ContextImpl中的实现。而Activity也继承了ContextWrapper,所以最后殊途同归,所有的调用最终调用到了ContextImpl中。
那Application又合Context有什么关系呢,如下:

public class Application extends ContextWrapper{
}

可以看到Application也继承ContextWrapper,ContextWrapper继承Context,所以通过调用getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);最终调用到的是Application中的mBase的对应的getSystemService方法,mBase它是Context对象,也是ContextImpl实例,那它是如何设置为ContextImpl实例的呢,Application中有一attach函数,参数为context,它的调用是LoadedApk中的makeApplication时调用的

    final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
            Application app = null;
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
}

public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

所以可以看到Application中最终也是调用到了ContextImpl中去。
至此,我们明白了getSystemService,无论是Activity的直接getSystemService()调用,还是通过getBaseContext调用还是getApplicationContext()调用,最终都是调用到了ContextImpl中去。我们来看看具体的代码跟踪。
getSystemService代码跟踪,Activity中有WINDOW_SERVICE和SEARCH_SERVICE的缓存,直接返回

@Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }
//super.getSystemService(name)
@Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

可以看到最后是调用到了getBaseContext()的getSystemService,getBaseContext返回的是mBase对象,mBase对象是ContextImpl(Context)对象。它是在Activity的attach的时候设置的(参考https://www.jianshu.com/p/76f94c6452c0

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) {
        attachBaseContext(context);
}

所以最终调用到了ContextImpl的getSystemService。
在看ContextImpl的getSystemService之前,先来看看通过Application的调用

//Activity中
getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);

@Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

我们知道Activity中的mBase是ContextImpl对象,那么它的getApplicationContext返回什么对象呢

final LoadedApk mPackageInfo;
@Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

mPackageInfo是一个LoadedApk对象,它返回的mApplication对象就是调用makeApplication生成的Application对象。(见上面讲解代码)

private Application mApplication;
Application getApplication() {
        return mApplication;
    }

即也就是调用到了Application中的getSystemService。而Application继承ContextWrapper,所以又是调用到了mBase的getSystemService。mBase为ContextImpl对象,所以最终调用到了ContextImpl中去。
下面我们来看看最终ContextImpl的调用

@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
//SystemServiceRegistry中的getSystemService
public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

最终就是调用到了SystemServiceRegistry中注册的系统服务了。具体可以参考SystemServiceRegistry的源代码。这个就是属于注册系统服务的范畴了。非本篇讨论范围。
最后是不是会有一个疑问,一个应用中到底会有多少个ContextImpl对象实例,一个还是多个。如果是多个对象,如何保证调用的最后归一呢。多个对象岂不是会占用很多内存。
说到这些,就不得不提ContextImpl中比较重要的两个成员变量了

final ActivityThread mMainThread;
final LoadedApk mPackageInfo;

同时,我们看到ContextImpl中有很多创建Context相关的函数,比如

static ContextImpl createSystemContext(ActivityThread mainThread) {
        LoadedApk packageInfo = new LoadedApk(mainThread);
        ContextImpl context = new ContextImpl(null, mainThread,
                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
        context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                context.mResourcesManager.getDisplayMetrics());
        return context;
    }

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread,
                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
    }

    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
                null, overrideConfiguration, displayId);
    }
@Override
    public Context createApplicationContext(ApplicationInfo application, int flags)
            throws NameNotFoundException {
        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE);
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
                    new UserHandle(UserHandle.getUserId(application.uid)), flags,
                    mDisplay, null, Display.INVALID_DISPLAY);
            if (c.mResources != null) {
                return c;
            }
        }

        throw new PackageManager.NameNotFoundException(
                "Application package " + application.packageName + " not found");
    }
@Override
    public Context createPackageContext(String packageName, int flags)
            throws NameNotFoundException {
        return createPackageContextAsUser(packageName, flags,
                mUser != null ? mUser : Process.myUserHandle());
    }
@Override
    public Context createConfigurationContext(Configuration overrideConfiguration) {
        if (overrideConfiguration == null) {
            throw new IllegalArgumentException("overrideConfiguration must not be null");
        }

        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
                mUser, mFlags, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
    }
``
可以看到ContextImpl是会创建很多次的,它是一个轻量级的对象,虽然ContextImpl会被创建多个,但是它的成员变量mMainThread和mPackageInfo的对象引用是唯一的。这也是一个android进程里面比较重要的两个类。也只会生成唯一的对象。从中我们也可以看到google工程师设计android框架的一些巧妙之处和原理,希望能从中学习一二。


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