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总结》 Context继承关系图

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

《Context总结》 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总结》 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)对象是同一个。

    原文作者:Android那些事儿
    原文地址: https://www.jianshu.com/p/45de7ed06a92
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞