Android 是基于组件的应用设计模式,组件的运行要有一个完整的 Android 工程环境。在这个环境下,Activity、Service 等系统组件才能够正常工作,而这些组件并不能采用普通的 Java 对象创建方式,而是要有它们各自的上下文环境。Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。
源码基于 sdk-30/R/11.0
Context 分类
Context: Context 是一个抽象类,表示应用运行的基本环境。
ContextImpl: Context 的公用实现类。
ContextWrapper: 对 Context 的包装类,是 Context 的代理类。
ContextThemeWrapper: 提供主题相关的功能。唯一实现类是 Activity。
Application: 表示一个应用程序,单例对象,是应用最先启动的组件。提供全局上下文。
Service: 服务组件。
Context 类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 public abstract class Context { public abstract Context createApplicationContext (ApplicationInfo application, int flags) ; public abstract Context getApplicationContext () ; }
Context 是一个抽象类,代表应用程序需要的全局信息,由 Android 系统实现。能够访问应用程序中的资源,启动 Activity,注册广播和服务等。
Activity 的 Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public final class ActivityThread { private Activity performLaunchActivity (ActivityClientRecord r, Intent customIntent) { ContextImpl appContext = createBaseContextForActivity(r); Application app = r.packageInfo.makeApplication(false , mInstrumentation); appContext.setOuterContext(activity); activity.attach(appContext, app, ...); } private ContextImpl createBaseContextForActivity (ActivityClientRecord r) { ContextImpl appContext = ContextImpl.createActivityContext(this , ...); return appContext } } public class Activity extends ContextThemeWrapper ...{ private Application mApplication; final void attach (Context context, Application application,...) { attachBaseContext(context); mApplication = application; } @Override protected void attachBaseContext (Context newBase) { super .attachBaseContext(newBase); if (newBase != null ) { newBase.setAutofillClient(this ); newBase.setContentCaptureOptions(getContentCaptureOptions()); } } public final Application getApplication () { return mApplication; } }
Activity 本身就是 Context。
Activity 的 mBase Context 对象为创建的 ContextImpl,在 Activity#attach() 方法中赋值。
Activity 的全局上下文在 Activity#attach() 方法中赋值。
Service 的 Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final class ActivityThread { private void handleCreateService (CreateServiceData data) { ContextImpl context = ContextImpl.createAppContext(this , packageInfo); Application app = packageInfo.makeApplication(false , mInstrumentation); context.setOuterContext(service); service.attach(context, app, ...); } } public abstract class Service extends ContextWrapper ...{ private Application mApplication = null ; public final void attach (Context context, Application application, ...) { attachBaseContext(context); mApplication = application; } @Override protected void attachBaseContext (Context newBase) { super .attachBaseContext(newBase); if (newBase != null ) { newBase.setContentCaptureOptions(getContentCaptureOptions()); } } }
Service 的 Context 和 Activity 中的类似。
ContextProvider 的 Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public final class ActivityThread { private void handleBindApplication (AppBindData data) { Application app = data.info.makeApplication(data.restrictedBackupMode, null ); installContentProviders(app, data.providers); } private void installContentProviders ( Context context, List<ProviderInfo> providers) { ContentProviderHolder cph = installProvider(context, null , cpi, ...); } private ContentProviderHolder installProvider (Context context,...) { ContentProvider localProvider = null ; IContentProvider provider; Context c = null ; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } else { } localProvider = packageInfo.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); localProvider.attachInfo(c, info); } } public abstract class ContentProvider { private void attachInfo (Context context, ProviderInfo info, boolean testing) { mContext = context; } public final @Nullable Context getContext () { return mContext; } public final Context requireContext () { final Context ctx = getContext(); if (ctx == null ) { throw new IllegalStateException ("Cannot find context from the provider." ); } return ctx; } }
应用启动时会注册所有的 ContextProvider,ContentProvider#getContext() 获取的是 Application 对象。
BroadcastReceiver 的 Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class ContextImpl extends Context { @Override public Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver,..., getOuterContext(), 0 ); } } public final class LoadedApk { static final class ReceiverDispatcher { final Context mContext; ReceiverDispatcher(BroadcastReceiver receiver, Context context,...) { mContext = context; } final class Args extends BroadcastReceiver .PendingResult { public final Runnable getRunnable () { final BroadcastReceiver receiver = mReceiver; receiver.onReceive(mContext, intent); } } } }
通过 Activity 注册广播,在 onReceive(mContext, intent) 回调中传递的是 Activity 的 mOuterContext 对象,即 Activity 对象。如果通过 Service 注册,回调方法中的 context 即为 Service 对象。
Context 作用域 由于 Context 的具体实现是由 ContextImpl 完成的,因此在绝大多数场景下,Activity、Service 和 Application 这三种类型的 Context 可以通用。
如果用 Application Context 去启动一个 LaunchMode 为 standard 的 Activity 时会报错。 **android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?**。 这是因为非 Activity 类型的 Context 并没有任务栈,所以待启动的 Activity 就找不到栈了。解决这个问题的方法就是为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK
标记位,这样启动的时候就为它创建一个新的任务栈,而此时的 Activity 是以 singleTask 模式启动的。
在 Application 和 Service 中使用 LayoutInflate 会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。
在 Service 中显示 Dialog 会报 java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity. 异常。
1 2 3 4 5 if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) { a.recycle(); throw new IllegalStateException ( "You need to use a Theme.AppCompat theme (or descendant) with this activity." ); }
获取 Context
Activity.this
返回当前的 Activity 实例,如果是 UI 控件需要使用 Activity 作为 Context 对象,但是默认的 Toast 实际上使用 ApplicationContext 也可以。
Activity.getApplicationContext()
获取当前 Activity 所在的进程的全局 Context 对象。
View.getContext()
返回当前 View 对象的 Context 对象,通常是当前正在展示的 Activity 对象。
获取 Application Application 对象是在创建进程的时候通过 LoadedApk#makeApplication()
方法实例化。
Activity 或 Service: 通过调用 getApplication()
方法获取 mApplication,mApplication 在 attach(Context context,...)
方法中赋值。
BroadcastReceiver: 通过 onReceive(Context context, Intent intent)
方法获取到 Context 对象,进而通过 Context#getApplicationContext() 方法获取到 Application 对象;
ContentProvider: 通过 getContext() 获取当前的 Application。