
本文共 38249 字,大约阅读时间需要 127 分钟。
一 概述
Context 相信几乎所有的 Android 开发人员基本上都非常熟悉,因为它太常见了。有大量的场景都离不开 Context,下面列举部分常见场景:

Context 是 Android 中用的非常多的一种概念,常被翻译成上下文,这种概念在其他的技术中也有所使用。Android 官方对它的解释,可以理解为应用程序环境中全局信息的接口,它整合了许多系统级的服务,可以用来获取应用中的类、资源,以及可以进行应用程序级的调起操作,比如启动 Activity、Service 等等,而且 Context 这个类是 abstract 的,不包含具体的函数实现。
1.1 Context结构
Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。
Context 本身是一个抽象类,其主要实现类为 ContextImpl,另外有直系子类两个:
- ContextWrapper
- ContextThemeWrapper
这两个子类都是 Context 的代理类,它们继承关系如下:

- ContextImpl ContextImpl 是 Context API 的常见实现,它为 Activity 和其他应用程序组件提供基本上下文对象,说的通俗一点就是 ContextImpl 实现了抽象类的方法,我们在使用 Context 的时候的方法就是它实现的
- ContextWrapper ContextWrapper 类代理 Context 的实现,将其所有调用简单地委托给另一个 Context 对象(ContextImpl),可以被分类为修饰行为而不更改原始 Context 的类,其实就 Context 类的修饰类。真正的实现类是 ContextImpl,ContextWrapper 里面的方法调用也是调用 ContextImpl 里面的方法
- ContextThemeWrapper 就是一个带有主题的封装类,比 ContextWrapper 多了主题,它的一个直接子类就是 Activity。
通过 Context 的继承关系图并结合我们开发中比较熟悉的类:Activity、Service、Application,所以我们可以认为 Context 一共有三种类型,分别是 Application、Activity 和 Service,他们分别承担不同的作用,但是都属于 Context,而他们具有 Context 的功能则是由 ContextImpl 类实现的。
简单流程是:Application,Activity 或 Service 通过 attach() 调用父类 ContextWrapper 的 attachBaseContext(),从而设置父类成员变量 mBase 为 ContextImpl 对象,从而核心的工作都交给 ContextImpl 来处理。
1.2 Context数量
根据上面的 Context 类型我们可以知道。Context 一共有 Application、Activity 和 Service 三种类型,因此在一个应用程序中 Context 数量的计算公式可以这样写:
- Context 数量 = Activity 数量 + Service 数量 + 1
上面的1代表着 Application 的数量,因为一个应用程序中可以有多个 Activity 和多个 Service,但是只能有一个 Application。
二 组件初始化
要想深入理解 Context,需要依次来看看四大组件的初始化过程。
2.1 performLaunchActivity
frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { //step 1: 创建LoadedApk对象 r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ...... //component初始化过程 //step 2:创建ContextImpl对象 ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); //step 3: 创建Activity对象 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) { ...... } try { //step 4: 创建Application对象 Application app = r.packageInfo.makeApplication(false, mInstrumentation); ...... if (activity != null) { ...... appContext.setOuterContext(activity);// 设置mOuterContext //step 5:将Application/ContextImpl都attach到Activity对象 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, ......); ...... activity.mCalled = false; if (r.isPersistable()) { //step 6: 执行回调onCreate mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { ...... } r.activity = activity; } r.setState(ON_CREATE); ..... } catch (SuperNotCalledException e) { ...... } catch (Exception e) { ...... } return activity; }
startActivity 的过程最终会在目标进程执行 performLaunchActivity() 方法,该方法主要功能:
- 创建对象 LoadedApk
- 创建对象 Activity
- 创建对象 Application
- 创建对象 ContextImpl
Application,ContextImpl 都 attach 到 Activity 对象并执行 onCreate() 等回调。
2.2 handleCreateService
frameworks/base/core/java/android/app/ActivityThread.java
private void handleCreateService(CreateServiceData data) { ...... //step 1: 创建LoadedApk LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); //step 2: 创建Service对象 service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent); } catch (Exception e) { ...... } try { //step 3: 创建ContextImpl对象 ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service);// 设置mOuterContext //step 4: 创建Application对象 Application app = packageInfo.makeApplication(false, mInstrumentation); //step 5: 将Application/ContextImpl都attach到service对象 service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService()); service.onCreate();//step 6: 执行onCreate回调 mServices.put(data.token, service); try { ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { ...... } }
2.3 handleReceiver
private void handleReceiver(ReceiverData data) { ...... //step 1: 创建LoadedApk对象 LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); IActivityManager mgr = ActivityManager.getService(); Application app; BroadcastReceiver receiver; ContextImpl context; try { //step 2: 创建Application对象 app = packageInfo.makeApplication(false, mInstrumentation); //step 3: 创建ContextImpl对象 context = (ContextImpl) app.getBaseContext(); if (data.info.splitName != null) { context = (ContextImpl) context.createContextForSplit(data.info.splitName); } java.lang.ClassLoader cl = context.getClassLoader(); data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); //step 4: 创建BroadcastReceiver对象 receiver = packageInfo.getAppFactory() .instantiateReceiver(cl, data.info.name, data.intent); } catch (Exception e) { ...... } try { ...... //step 5: 执行onReceive回调 receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); } catch (Exception e) { ...... } finally { sCurrentBroadcastIntent.set(null); } if (receiver.getPendingResult() != null) { data.finish(); } }
- 以上过程是静态广播接收者,即通过 AndroidManifest.xml 的标签来申明的 BroadcastReceiver
- 如果是动态广播接收者,则不需要再创建那么多对象,因为动态广播在注册时进程已创建,基本对象已创建完成。只需要回调 BroadcastReceiver 的 onReceive() 方法即可
2.4 installProvider
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { ...... Context c = null; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { //step 1 && 2: 创建LoadedApk和ContextImpl对象 c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { // Ignore } } if (c == null) { ...... return null; } ...... try { final java.lang.ClassLoader cl = c.getClassLoader(); LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); if (packageInfo == null) { // System startup case. packageInfo = getSystemContext().mPackageInfo; } //step 3: 创建ContentProvider对象 localProvider = packageInfo.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); ...... //step 4: ContextImpl都attach到ContentProvider对象 //step 5: 并执行回调onCreate localProvider.attachInfo(c, info); } catch (java.lang.Exception e) { ...... } } else { ...... } ...... return retHolder; }
2.5 handleBindApplication
private void handleBindApplication(AppBindData data) { ...... //step 1: 创建LoadedApk对象 data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ...... //step 2: 创建ContextImpl对象 final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); ...... try { final ClassLoader cl = instrContext.getClassLoader(); //step 3: 创建Instrumentation mInstrumentation = (Instrumentation) cl.loadClass(data.instrumentationName. getClassName()).newInstance(); } catch (Exception e) { ...... } final ComponentName component = new ComponentName(ii.packageName, ii.name); mInstrumentation.init(this, instrContext, appContext, component, data.instrumentationWatcher, data.instrumentationUiAutomationConnection); ...... } } else { mInstrumentation = new Instrumentation(); mInstrumentation.basicInit(this); } ...... Application app; ...... try { //step 4: 创建Application对象 app = data.info.makeApplication(data.restrictedBackupMode, null); ...... if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { //step 5: 安装providers installContentProviders(app, data.providers); } } try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { ...... } try { //step 6: 执行Application.Create回调 mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { ...... } } finally { ...... } ...... }
三 核心对象
上面介绍了4大组件以及 Application 的初始化过程,接下来再进一步说明其中 LoadedApk,ContextImpl,Application 的初始化过程。
3.1 创建LoadedApk
3.1.1 ActivityThread.getPackageInfo
public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid) : true); boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0; if ((flags&(Context.CONTEXT_INCLUDE_CODE |Context.CONTEXT_IGNORE_SECURITY)) == Context.CONTEXT_INCLUDE_CODE) { if (securityViolation) { //违反隐私问题, 抛出SecurityException String msg = "Requesting code from " + ai.packageName + " (with uid " + ai.uid + ")"; if (mBoundApplication != null) { msg = msg + " to be run in process " + mBoundApplication.processName + " (with uid " + mBoundApplication.appInfo.uid + ")"; } throw new SecurityException(msg); } } return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, registerPackage); }final ArrayMap> mPackages = new ArrayMap<>();private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, ......) { final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); synchronized (mResourcesManager) { WeakReference ref; if (differentUser) { // Caching not supported across users ref = null; } else if (includeCode) { ref = mPackages.get(aInfo.packageName); } else { ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref != null ? ref.get() : null; if (packageInfo != null) { if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) { packageInfo.updateApplicationInfo(aInfo, null); } return packageInfo; } ...... packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); if (mSystemThread && "android".equals(aInfo.packageName)) { packageInfo.installSystemApplicationInfo(aInfo, getSystemContext().mPackageInfo.getClassLoader()); } if (differentUser) { // Caching not supported across users } else if (includeCode) { mPackages.put(aInfo.packageName, new WeakReference (packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference (packageInfo)); } return packageInfo; } }
- mPackages 的数据类型为 ArrayMap<String, WeakReference>,记录着每一个包名所对应的 LoadedApk 对象的弱引用
- 当 mPackages 没有找到相应的 LoadedApk 对象,则创建该对象并加入到 mPackages
3.1.2 ActivityThread.getPackageInfoNoCheck
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true, false); }
除了 Activity 的初始化,其他组件的初始化都是采用该方法,有默认参数值,主要功能不变。
- securityViolation = false,则不进行是否违反隐私的监测
- registerPackage = false,则在获取类加载器 (getClassLoader) 时,不会将该 package 添加到当前所在进程的成员变量 pkgDeps
3.2 创建Application
有了 LoadedApk 对象,接下来可以创建 Application 对象,该对象一个 Apk 只会创建一次。
3.2.1 LoadedApk.makeApplication
frameworks/base/core/java/android/app/LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { //保证一个LoadedApk对象只创建一个对应的Application对象 if (mApplication != null) { return mApplication; } ...... 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); } //创建ContextImpl对象 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); //创建Application对象, 并将appContext attach到新创建的Application app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { ...... } mActivityThread.mAllApplications.add(app); mApplication = app;//将刚创建的app赋值给mApplication if (instrumentation != null) { try { //调用application的OnCreate方法 instrumentation.callApplicationOnCreate(app); } catch (Exception e) { ...... } } ...... return app; }
- 获取当前应用的 ClassLoader 对象,根据是否为 ”android” 来决定调用 initializeJavaContextClassLoader()
- 根据当前 ActivityThread 对象来创建相应的 ContextImpl 对象
- 创建 Application 对象, 初始化其成员变量:
- mBase 指向新创建 ContextImpl
- mLoadedApk 指向当前所在的 LoadedApk 对象
- 将新创建的 Application 对象保存到ContextImpl 的成员变量 mOuterContext
关于 initializeJavaContextClassLoader() 的过程,将会在Application创建中介绍。
关于应用类名采用的是 Apk 中声明的应用类名,即 Manifest.xml 中定义的类名。有两种特殊情况会强制设置应用类名为 ”android.app.Application”:
- 当 forceDefaultAppClass = true,目前只有 system_server 进程初始化包名为 ”android” 的 apk 过程会调用
- Apk 没有自定义应用类名的情况
3.2.2 Instrumentation.newApplication
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { Application app = getFactory(context.getPackageName()) .instantiateApplication(cl, className); app.attach(context); return app; }
3.3 创建ContextImpl
创建 ContextImpl 的方式有多种,不同的组件初始化调用不同的方法,如下:
- Activity:调用 createBaseContextForActivity 初始化
- Service/Application:调用createAppContext 初始化
- Provider:调用 createPackageContext 初始化
- BroadcastReceiver:直接从 Application.getBaseContext() 来获取 ContextImpl 对象
3.3.1 ActivityThread.createBaseContextForActivity
private ContextImpl createBaseContextForActivity( ActivityClientRecord r) { final int displayId; try { displayId = ActivityTaskManager.getService().getActivityDisplayId(r.token); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } //创建ContextImpl对象 ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); String pkgName = SystemProperties.get("debug.second-display.pkg"); if (pkgName != null && !pkgName.isEmpty() && r.packageInfo.mPackageName.contains(pkgName)) { for (int id : dm.getDisplayIds()) { if (id != Display.DEFAULT_DISPLAY) { Display display = dm.getCompatibleDisplay(id, appContext.getResources()); appContext = (ContextImpl) appContext.createDisplayContext(display); break; } } } return appContext; }
3.3.1.1 ContextImpl.createActivityContext
static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) { String[] splitDirs = packageInfo.getSplitResDirs(); ClassLoader classLoader = packageInfo.getClassLoader(); if (packageInfo.getApplicationInfo(). requestsIsolatedSplitLoading()) { try { classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); } catch (NameNotFoundException e) { ...... } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } } ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, activityToken, null, 0, classLoader, null); // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) ? packageInfo.getCompatibilityInfo() : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; final ResourcesManager resourcesManager = ResourcesManager.getInstance(); context.setResources(resourcesManager. createBaseActivityResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, classLoader)); context.mDisplay = resourcesManager.getAdjustedDisplay(displayId, context.getResources()); return context; }
Activity 采用该方法来初始化 ContextImpl 对象
3.3.2 ContextImpl.createAppContext
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { return createAppContext(mainThread, packageInfo, null); } static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo, String opPackageName) { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, null, opPackageName); context.setResources(packageInfo.getResources()); return context;}
Service/Application 采用该方法来初始化 ContextImpl 对象
3.3.3 ContextImpl.createPackageContext
public Context createPackageContext(String packageName, int flags) throws NameNotFoundException { return createPackageContextAsUser(packageName, flags, mUser); } @Overridepublic Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, // so we can safely copy the context without reloading Resources. return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user, flags, null, null); } //创建LoadedApk LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, flags, null, null); final int displayId = getDisplayId(); c.setResources(createResources(mActivityToken, pi, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } } // Should be a better exception. throw new PackageManager.NameNotFoundException( "Application package " + packageName + " not found");}
provider 采用该方法来初始化 ContextImpl 对象
3.3.4 ContextImpl初始化
class ContextImpl extends Context { final ActivityThread mMainThread; final LoadedApk mPackageInfo; private final IBinder mActivityToken; private final String mBasePackageName; private Context mOuterContext; //缓存Binder服务 final Object[] mServiceCache = SystemServiceRegistry.createServiceCache(); private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, @NonNull LoadedApk packageInfo, @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) { mOuterContext = this; //ContextImpl对象 mMainThread = mainThread; // ActivityThread赋值 mActivityToken = activityToken; mPackageInfo = packageInfo; // LoadedApk赋值 String opPackageName; if (container != null) { mBasePackageName = container.mBasePackageName; opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) { opPackageName = ActivityThread.currentPackageName(); } else { opPackageName = mBasePackageName; } } mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; mContentResolver = new ApplicationContentResolver(this, mainThread); }}
四 Context attach
4.1 Activity
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, ......) { attachBaseContext(context);//调用父类方法设置mBase. ......}
将新创建的 ContextImpl 赋值到父类 ContextWrapper.mBase 变量
4.2 Service
public final void attach( Context context, ActivityThread thread, String className, IBinder token, Application application, Object activityManager) { attachBaseContext(context);//调用父类方法设置mBase mThread = thread; // NOTE: unused - remove? mClassName = className; mToken = token; mApplication = application; mActivityManager = (IActivityManager)activityManager; mStartCompatibility = getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ECLAIR;}
4.3 BroadcastReceiver
final Context getReceiverRestrictedContext() { if (mReceiverRestrictedContext != null) { return mReceiverRestrictedContext; } return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext()); }
对于广播来说 Context 的传递过程,跟其他组件完全不同。广播是在 onCreate 过程通过参数将 ReceiverRestrictedContext 传递过去的。此处 getOuterContext() 返回的是 ContextImpl 对象
4.4 ContentProvider
framework/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) { attachInfo(context, info, false); }private void attachInfo(Context context, ProviderInfo info, boolean testing) { mNoPerms = testing; mCallingPackage = new ThreadLocal<>(); if (mContext == null) { //将新创建ContextImpl对象保存到ContentProvider对象的成员变量mContext mContext = context; if (context != null && mTransport != null) { mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); } mMyUid = Process.myUid(); if (info != null) { setReadPermission(info.readPermission); setWritePermission(info.writePermission); setPathPermissions(info.pathPermissions); mExported = info.exported; mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0; setAuthorities(info.authority); } // 执行onCreate回调 ContentProvider.this.onCreate(); }}
需要补充的是 ContentProvider 没有继承自 Context,但是其成员变量 mContext 是 Context 类型,那么这个变量是怎么赋值的呢?在构造 ContextImpl 时,会初始化 ContentResolver
mContentResolver = new ApplicationContentResolver(this, mainThread);
这个 this 即是 ContextImpl 自身,传进去赋值给了 ContentResolver 变量:
private final Context mContext;
当使用 ContentResolver 查询 ContentProvider 并且创建 ContentProvider 的时候,这个 mContext 就赋值给 ContentProvider 的 mContext。
4.5 Application
final void attach(Context context) { attachBaseContext(context); mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;}
4.6 Context核心方法
对象 | 方法 | 返回值类型 | 含义 |
---|---|---|---|
Activity | getApplication() | Application | 获取Activity所属的mApplication |
Service | getApplication() | Application | 获取Service所属的mApplication |
ContextWrapper | getBaseContext | ContextImpl | 获取mBase,即ContextImpl |
ContextWrapper | getApplicationContext | Application | 见小节4.6.1 |
ContextImpl | getApplicationContext | Application | 见小节4.6.1 |
ContextImpl | getOuterContext | ContextImpl | 获取mOuterContext |
ContextImpl | getApplicationInfo | ApplicationInfo | mPackageInfo.mApplicationInfo |
关于 Application
- Activity 的 mApplication 是通过 makeApplication() 过程创建
- Service 的 mApplication 同样是通过 makeApplication() 过程创建
- Receiver 是通过其方法 onReceive() 的第一个参数指向当前所在 Application
- provider 无法获取 application,因为其所在 application 不一定初始化
关于 mOuterContext: ContextImpl 的 mOuterContext,默认值是由 ContextImpl 初始化过程创建。但往往通过调用 setOuterContext() 使其指向外部的 Context
- makeApplication 过程,mOuterContext 指向 Application
- handleCreateService() 过程,mOuterContext 指向 Service
- performLaunchActivity 的 createBaseContextForActivity 过程,mOuterContext 指向 Activity
- BroadcastReceiver/Provider 则采用默认值 ContextImpl
4.6.1 ContextImpl.getApplicationContext
@Override public Context getApplicationContext() { return (mPackageInfo != null) ? mPackageInfo.getApplication() : mMainThread.getApplication(); }//上述mPackageInfo的数据类型为LoadedApkpublic final class LoadedApk { Application getApplication() { return mApplication; }}//上述mMainThread为ActivityThreadpublic final class ActivityThread { public Application getApplication() { return mInitialApplication; }}
- mPackageInfo.getApplication():返回的是 LoadedApk.mApplication
- Activity/Servie/BroadcastReceiver/Application 初始化, 调用 makeApplication() 完成;但对于同一个 apk 只会执行一次
- mMainThread.getApplication():返回的是 ActivityThread.mInitialApplication
- ActivityThread.handleBindApplication() 赋值
- system_server 进程的 ActivityThread.attach() 赋值
五 Context与Resources
之前列举了 Context 的用处,最常用的莫过于通过 Context 获取资源文件(Resources),具体是怎么实现的,接下来分析下。
Context 并没有 Resources 类型的成员变量,ContextWrapper 也没有,ContextImpl 有成员变量:private @NonNull Resources mResources;
而 Context 里有获取 Resources 的成员方法:
public abstract Resources getResources();
最终会调用 ContextImpl,返回 mResources。因此重点是研究 ContextImpl 的 mResources 是如何被赋值的。
上面提到过,Application,Activity,Service 等关联 ContextImpl 时,会新构造一个 ContextImpl 实例,在初始化的时候,会给 mResources 赋值。而 Resources 是通过 ResourcesManager 管理的,最终来看 ResourcesManager 如何管理 Resources 的。
5.1 ResourcesManager
Resources 和 ResourcesImpl
Resources 里有个成员变量:private ResourcesImpl mResourcesImpl;
顾名思义,Resources 具体加载资源是通过 ResourcesImpl 实现的。ResourcesImpl 里成员变量:
final AssetManager mAssets;
AssetManager 负责与底层通信(操作文件)。
ResourcesManager 获取 Resources 核心代码:Resources getOrCreateResources(@android.annotation.Nullable IBinder activityToken, @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) { //ResourcesKey 记录着资源文件的路径、Configuration等信息,最后用来生成AssetManager synchronized (this) { //activityToken 不为空,说明是Activity的Resource if (activityToken != null) { //从ResourcesImpl 的Map里寻找相应的缓存 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); if (resourcesImpl != null) { return getOrCreateResourcesForActivityLocked(activityToken, classLoader, resourcesImpl, key.mCompatInfo); } } else { //非Activity的Resource ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); if (resourcesImpl != null) { return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo); } } //没找到现成的,生成ResourcesImpl 实例 //同时创建AssetManager ResourcesImpl resourcesImpl = createResourcesImpl(key); if (resourcesImpl == null) { return null; } //记录到Map里 mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); final Resources resources; if (activityToken != null) { //Activity Resource resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, resourcesImpl, key.mCompatInfo); } else { //非Activity Resource resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo); } return resources; }}
总结:
- 通过 ResourcesKey 去检索之前是否创建过 ResourceImpls,如果没有则创建新的对象,并记录到 Map 里
- 通过 ArrayList 遍历寻找可以复用的 Resources 对象,判断的依据是传入的 ResourceImpls 与已有的相等(其中的条件)。如果没有可以复用的,则创建 Resources 对象,设置 ResourceImpls,并将 Resources 对象放入 List 等待下次复用
- 可以看出 ResourcesManager 通过设置缓存来管理 Resource。而 ResourcesManager 是单例,Context 通过 getResources(…) 获取Resource 本质是通过 ResourcesManager 获取的
接下来看看 Application/Service/Activity 获取的 Resource 是同一个 Resource 吗?
ActivityThread 为 Application/Service 创建 ContextImpl 时使用如下方法:#ContextImpl.java static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo, String opPackageName) { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, null, opPackageName); //从packageInfo 获取resources context.setResources(packageInfo.getResources()); return context; } #LoadedApk.java public Resources getResources() { //LoadedApk是全局的,因此它的mResources也只有一个 if (mResources == null) { mResources = ResourcesManager.getInstance().getResources(null, mResDir, splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), getClassLoader()); } return mResources; }
可以看出,通过 createAppContext 创建的 ContextImpl,最后都共用一个 ContextImpl,也就是共用同一个 Resources 对象。而 ActivityThread 为 Activity 创建 ContextImpl 时使用的是:
#ContextImpl.java static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, ......) { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, activityToken, null, 0, classLoader, null); final ResourcesManager resourcesManager = ResourcesManager.getInstance(); //通过resourcesManager 获取 context.setResources(resourcesManager.createBaseActivityResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, classLoader)); return context; }
可以看出,直接使用了 ResourcesManager 获取 Resource,最终不同的 Activity 获取的 Resource 对象不同,但是共用同一个 ResourcesImpl 对象。
六 总结
5.1 组件初始化
下面用图表来看看核心组件的初始化过程会创建哪些对象
类型 | LoadedApk | ContextImpl | Application | 创建相应对象 | 回调方法 |
---|---|---|---|---|---|
Activity | 是 | 是 | 是 | Activity | onCreate |
Service | 是 | 是 | 是 | Service | onCreate |
Receiver | 是 | 是 | 是 | BroadcastReceiver | onReceive |
Provider | 是 | 是 | 非 | ContentProvider | onCreate |
Application | 是 | 是 | 是 | Application | onCreate |
每个 Apk 都对应唯一的 application 对象和 LoadedApk 对象,当 Apk 中任意组件的创建过程中,当其所对应的的 LoadedApk 和 Application 没有初始化则会创建,且只会创建一次。
另外大家会注意到唯有 Provider 在初始化过程并不会去创建所相应的 Application 对象。也就意味着当有多个 Apk 运行在同一个进程的情况下,第二个 apk 通过 Provider 初始化过程再调用 getContext().getApplicationContext() 返回的并非 Application 对象,而是 NULL。这里要注意会抛出空指针异常。
5.2 Context attach过程
- Application 调用 attachBaseContext() 将新创建 ContextImpl 赋值到父类 ContextWrapper.mBase 变量 可通过 getBaseContext() 获取该 ContextImpl
- Activity/Service 调用 attachBaseContext() 将新创建 ContextImpl 赋值到父类 ContextWrapper.mBase 变量 可通过 getBaseContext() 获取该 ContextImpl 可通过 getApplication() 获取其所在的 Application 对象
- ContentProvider 调用 attachInfo() 将新创建 ContextImpl 保存到 ContentProvider.mContext 变量 可通过 getContext() 获取该 ContextImpl
- BroadcastReceiver 在 onCreate 过程通过参数将 ReceiverRestrictedContext 传递过去的
- ContextImpl 可通过 getApplicationContext() 获取 Application
5.3 Context使用场景
类型 | startActivity | startService | bindService | sendBroadcast | registerReceiver | getContentResolver |
---|---|---|---|---|---|---|
Activity | 是 | 是 | 是 | 是 | 是 | 是 |
Service | - | 是 | 是 | 是 | 是 | 是 |
Receiver | - | 是 | × | 是 | - | 是 |
Provider | - | 是 | 是 | 是 | 是 | 是 |
Application | - | 是 | 是 | 是 | 是 | 是 |
说明: (图中第一列代表不同的 Context,“是” 代表允许在该 Context 执行相应的操作;“×” 代表不允许; “-” 代表分情况讨论)
- 当 Context 为 Receiver 的情况下
- 不允许执行 bindService() 操作, 由于限制性上下文 (ReceiverRestrictedContext) 所决定的,会直接抛出异常.
- registerReceiver 是否允许取决于 receiver,当 receiver == null 用于获取 sticky 广播,允许使用;否则不允许使用 registerReceiver
- 纵向来看 startActivity 操作
- 当为 Activity Context 则可直接使用
- 当为其他 Context, 则必须带上 FLAG_ACTIVITY_NEW_TASK flags 才能使用
- 另外 UI 相关要 Activity 中使用
- 除了以上情况, 其他的操作都是被允许执行
5.4 getApplicationContext
绝大多数情况下,getApplication() 和 getApplicationContext() 这两个方法完全一致,返回值也相同;那么两者到底有什么区别呢?我们来讨论下这个问题:
getApplicationContext() 存在是 Android 历史原因。我们都知道 getApplication() 只存在于 Activity 和 Service 对象;那么对于 BroadcastReceiver 和 ContentProvider 却无法获取 Application,这时就需要一个能在 Context 上下文直接使用的方法,那便是 getApplicationContext()。
两者对比:
- 对于 Activity/Service 来说,getApplication() 和 getApplicationContext() 的返回值完全相同;除非厂商修改过接口
- BroadcastReceiver 在 onReceive 的过程, 能使用 getBaseContext().getApplicationContext 获取所在 Application, 而无法使用 getApplication
- ContentProvider 能使用 getContext().getApplicationContext() 获取所在Application。绝大多数情况下没有问题,但是有可能会出现空指针的问题,情况如下:
当同一个进程有多个 apk 的情况下, 对于第二个 apk 是由 provider 方式拉起的, 前面介绍过 provider 创建过程并不会初始化所在 application,此时执行 getContext().getApplicationContext() 返回的结果便是 NULL。所以对于这种情况要做好判空。
发表评论
最新留言
关于作者
