本系列分四篇文章详细介绍Context, 这是第一篇
- Android深入理解Context–Application中Context的创建过程
- Android深入理解Context–Activity中Context的创建过程
- Android深入理解Context–Service中Context的创建过程
- Android深入理解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有两个直接的子类分别是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(); }
- 这里
mPackageInfo
是LoadedApk
, 这里肯定不为null, 从本文最开始ActivityThread.performLaunchActivity
中开始,
先判断if (r.packageInfo == null)
的话就会给其赋值,而这里的mPackageInfo
就是在创建ContextImpl
的时候从那里传过来的。
这样我们就进入到mPackageInfo.getApplication()
中:Application getApplication() { return mApplication; }
- 这里的
mApplication
就是我们在makeApplication
中注释5
处的赋值,这样我们通过getApplicationContext()
就得到了全局唯一的mApplication
。