Context总结

作者: Android那些事儿 | 来源:发表于2016-11-26 23:11 被阅读92次

    文章摘要
    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)对象是同一个。

    相关文章

      网友评论

        本文标题:Context总结

        本文链接:https://www.haomeiwen.com/subject/ivvgpttx.html