Context总结

文章摘要
Activity、Service等本质上是Context,Android开发者应对我们经常打交道的Context对象有一个基本的认识。主要阐述了如下认识:
1、如何认识Context。
2、Context的实例化对象ContextImpl对象是如何和Application、Activity、Service发生联系的。
3、Context对象的getResources方法,在一个应用程序中,无论是Application或者不同的Activity界面,返回的是同一个对象。


1、Context认知。
Context译为场景,一个应用程序可以认为是一个工作环境,在这个工作环境中可以存在许多场景,coding代码的场景 ,打电话的场景,开会的场景。这些场景可以类比不同的Activity,service。Activity可以理解为可视化的场景,Service理解为后台工作的场景。

2、从两个角度认识Context。

第一:Activity继承自Context,同时Activity还实现了其他的interface,我们可以这样看,activity在语法上extends了Context,其本质上是一个Context,但同时其实现了许多interface,扩充了Context的功能,扩充之后的类成为Activity或者Service。
第二:Context本质上包含了场景的所有元素,故而设定其为abstract,Activity和Service继承自Context,它们本质上可以认为就是Context。

3、Context继承关系图。

Context是抽象类,ContextWrapper以及ContextThemeWrapper是其包装类,不同的是ContextThemeWrapper需要传入主题。

 public ContextThemeWrapper(Context base, int themeres) {
        super(base);
        mThemeResource = themeres;
    }

ContextImpl是真正用来干活的实现类。会通过一些方法将该实现传入到Context中。
ps:关于Wrapper的作用:

  • 为其他对象提供一种代理以控制对这个对象的访问。
  • 子类中重写实现而不改变原来的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继承关系图

4、ContextImpl的初始化以及如何与Actvity以及Service等产生联系的。
如下是ContextImpl的初始化调用的地方,有兴趣的可以详细看下。

Context初始化

以Activity为例:
首先,activity的创建,在创建过程中调用attach方法,将Context传入到Activity中。
frameworks/base/core/java/android/app/ActivityThread.java
performLaunchActivity方法,创建Activity对象。

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.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);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

接着:调用createBaseContextForActivity得到Context实例化对象:

private Context createBaseContextForActivity(ActivityClientRecord r,
            final Activity activity) {
        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;
}

最后,调用Activity的attach方法,将Context对象,传入Activity中。

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                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);
}

5、一个应用程序中包括多少Context对象。
Context对象个数 = Activty对象个数+Service对象对象个数+1(Application).。

Context实现

6、Context对象的getResources对象,在一个应用程序中,无论是Application或者不同的Activity界面,返回的是同一个对象。

@Override
    public Resources getResources() {
        return mResources;
    }

mResources对象是在ContextImpl实例化对象中,传递过来的,故而:

    private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration)
Resources resources = packageInfo.getResources(mainThread);
mResources = resources;
}

从第4步,可以知道,packageInfo来自:r.packageInfo。而r.packageInfo的赋值逻辑如下:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
}

getPackageInfo方法中,会将创建的packageInfo按照以包名为Key的方式保存在ActivityThread.java集合中。

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

                if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }

总结:
其他Application(getPackageInfoNoCheck)或者
Service(getPackageInfoNoCheck)对象或者
Receiver(getPackageInfoNoCheck),在获取packageInfo的逻辑上,方法不同但都会操作的集合都是mResourcePackages对象。
即:在一个应用程序中r.packageInfo(LoadedApk packageInfo)对象是同一个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • context继承结构 context的直接子类有2个,一个是ContextWrapper,一个是ContextI...
    Lazy1阅读 743评论 0 0
  • 一、什么是Context二、Context的创建时机和获取1. Context的创建时机2. Context的获取...
    叛逆的青春不回头阅读 2,605评论 0 11
  • Activity mActivity =new Activity() 作为Android开发者,不知道你有没有思考...
    尹star阅读 176,521评论 99 1,166
  • 比例偏见:贵和便宜是相对的,我一直认为便宜没好货,好货不便宜,价格和商品都是成对比的,今天学习比例偏见是指促销时,...
    茜茜_e661阅读 292评论 0 1
  • 从来没有好好思考过孤独这个问题, 可能因为我一直都处于一种自己都不知道是孤独的状态里。 在这个世界上自己陪伴自己的...
    勇敢Planet阅读 228评论 0 0

友情链接更多精彩内容