Android深入理解Context--Application中Context的创建过程

本系列分四篇文章详细介绍Context, 这是第一篇

前言

Context是我们在开发中经常使用到的一个类,它的功能非常丰富,但是很多人可能仅仅知道怎么用,
或者说是比较迷茫的使用, 不知道Context内部的逻辑,这一次我们就从源码的角度来分析一下Context。

在讲解之前,首先抛出几个问题, 等到这一系列的文章讲完之后,这些问题就迎刃而解了:

  • 1 一个应用程序里面包含多少个Context?
  • 2 getApplication()和getApplicationContext()的区别?
  • 3 Context在Application, Activity, Service中是如何创建的?
  • 4 使用Context的误区?

Context概述

  • Context可以理解为上下文或者代码所在的环境,在开发中我们要么直接使用Context,调用context内部的方法,比如启动startActivity, startService, sendBroadcast等;或者间接传入context,比如Toast, Notification, Dialog等。
  • 一般我们混用是没事的,但是有一些情况就得注意了,比如我们启动一个Activity的时候,不是从一个Activity类型的Context调用的startActivity的话,就会报出异常android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity,我们可以通过intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)解决这个问题。
  • 如果想要从BroadcastReceiver弹出一个Dialog,但是弹出dialog(系统类型的除外,比如Toast)的context不是Activity类型,会报错IllegalStateException you need to use a Theme.AppCompat theme(or descendant) with this activity,我们得为这个dialog创建一个Activity,可以设置activity是没有界面的。所以了解清楚context的内部结构还是很有必要的。

首先我们来看一下系统中的Context的继承关系

Context结构图

  • 从图中我们可以看出,Context有两个直接的子类分别是ContextImpl和ContextWrapper, ContextImpl从名字就可以看出是真正实现Context的类,ContextWrapper是Context的包装类,其中有一个成员变量mBase就是ContextImpl类型的,所以当调用ContextWrapper中的方法时候,大部分都是调用mBase这个ContextImpl来处理的。ContextThemeWrapper, Service, Application继承ContextWrapper,这样他们也可以使用mBase来调用ContextImpl中的实现方法。Activity继承ContextThemeWrapper,系统单独把Activity继承ContextThemeWrapper, 是因为Activity是唯一与用户直接交互的界面,用户能看到,有一个界面的内容,所以添加了一些Theme之类的功能。

  • 根据上图我们先来回答第一个问题,Context数量 = Activity数量 + Service数量 + Application数量(1)

Application的Context创建过程源码解析:

  • Activity启动流程源码分析的启动过程中, 我们详细分析Activity是如何启动的,最后一个重要的方法是performLaunchActivity, 源码如下:
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ActivityInfo aInfo = r.activityInfo;
            if (r.packageInfo == null) {
                r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                        Context.CONTEXT_INCLUDE_CODE);
            }
    
            ComponentName component = r.intent.getComponent();
            if (component == null) {
                component = r.intent.resolveActivity(
                    mInitialApplication.getPackageManager());
                r.intent.setComponent(component);
            }
    
            if (r.activityInfo.targetActivity != null) {
                component = new ComponentName(r.activityInfo.packageName,
                        r.activityInfo.targetActivity);
            }
    
            ContextImpl appContext = createBaseContextForActivity(r);
            Activity activity = null;
            try {
                java.lang.ClassLoader cl = appContext.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);
                }
            }
    
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
            return activity;
        }
    
  • 我们重点关注最后一行的r.packageInfo.makeApplication,进入内部:
    @UnsupportedAppUsage
        public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
            //-----1-----
            if (mApplication != null) {
                return mApplication;
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
    
            Application app = null;
    
            String appClass = mApplicationInfo.className;
            if (forceDefaultAppClass || (appClass == null)) {
                appClass = "android.app.Application";
            }
    
            try {
                java.lang.ClassLoader cl = getClassLoader();
                if (!mPackageName.equals("android")) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                            "initializeJavaContextClassLoader");
                    initializeJavaContextClassLoader();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                }
                //-----2-----
                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
                //-----3-----
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
                //-----4-----
                appContext.setOuterContext(app);
            } catch (Exception e) {
                if (!mActivityThread.mInstrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to instantiate application " + appClass
                        + ": " + e.toString(), e);
                }
            }
            mActivityThread.mAllApplications.add(app);
            //-----5-----
            mApplication = app;
    
            if (instrumentation != null) {
                try {
                // -----6-----
                    instrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!instrumentation.onException(app, e)) {
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                            + ": " + e.toString(), e);
                    }
                }
            }
            return app;
        }
    
  • 我们一步一步分析,注释1处,首先判断当前mApplication是否为null, 如果不为null就直接返回,由此可见我们的应用程序中只有一个application实例对象(多进程的情况不考虑), 因为是第一次启动,所以会接着进入注释2处;
  • 注释2这个地方调用了ContextImpl.createAppContext,创建了一个ContextImpl类型的appContext
  • 注释3处调用了mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext)方法,创建了一个app实例。
  • 注释4处将创建的app赋值给appContext中mOuterContext
  • 注释5处将创建的app赋值给成员变量mApplication
  • 注释6处最终就调用了application.onCreate()方法。
  • 我们进入注释3中的看看如何创建Application的:
    public Application newApplication(ClassLoader cl, String className, Context context)
                throws InstantiationException, IllegalAccessException,
                ClassNotFoundException {
            Application app = getFactory(context.getPackageName())
                    .instantiateApplication(cl, className);
            //-----1-----
            app.attach(context);
            return app;
    }
    
    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
                @NonNull String className)
                throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            return (Application) cl.loadClass(className).newInstance();
    }
    
  • 由上面可以看出最终是通过ClassLoader创建出Application实例的。我们接着进入上面注释1处的app.attach(context):
    @UnsupportedAppUsage
        /* package */ final void attach(Context context) {
            attachBaseContext(context);
            mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
    
  • 接着进入attachBaseContext(context), 因为Application继承ContextWrapper, 这里会进入ContextWrapper的attachBaseContext中, 将base赋值给ContextWrapper中的mBase。注意这里的context是在makeApplication方法中通过ContextImpl.createAppContext创建的,所以是一个ContextImpl类型的Context,这也印证了最开始说的mBase是ContextImpl类型, 到这里Application的Context创建就结束了。
    protected void attachBaseContext(Context base) {
            if (mBase != null) {
                throw new IllegalStateException("Base context already set");
            }
            mBase = base;
    }
    

Application中获取getApplicationContext()?

  • 在Application中调用getApplicationContext()实际进入到的是ContextWrapper中的getApplicationContext()
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    
  • 我们知道这个mBase就是ContextImpl, 所以我们进入到ContextImpl
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    
  • 这里mPackageInfoLoadedApk, 这里肯定不为null, 从本文最开始ActivityThread.performLaunchActivity中开始,
    先判断if (r.packageInfo == null)的话就会给其赋值,而这里的mPackageInfo就是在创建ContextImpl的时候从那里传过来的。
    这样我们就进入到mPackageInfo.getApplication()中:
    Application getApplication() {
        return mApplication;
    }
    
  • 这里的mApplication就是我们在makeApplication中注释5处的赋值,这样我们通过getApplicationContext()
    就得到了全局唯一的mApplication
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值