征服安卓面试官路漫漫:从源码深扒一下四大组件和Context( 三 )


注意 attach() 方法中的 setWindowManager() 方法中的 mToken 参数 , 这决定了 Application Context 无法创建和显示 Dialog。 后续会进行详细分析 。
再回头看看文章开头的问题 。
Log.e("context", "getApplication in Activity: " + getApplication().getClass().getName());Log.e("context", "getApplicationContext in Activity: " + getApplicationContext().getClass().getName());Log.e("context", "getBaseContext in Activity: " + getBaseContext().getClass().getName());第一个 getApplication(), 看下源码就知道了:
public final Application getApplication() {return mApplication;}getApplication() 返回的是当前的 Application 对象 。 开发者没有声明自己实现的 Application 的话 , 就是系统默认的 android.app.Application 。
第二个 getApplicationContext() , 它并不是 Activity 中的方法 , 而是 ContextWrapper 的 。 直接看源码:
@Overridepublic Context getApplicationContext() {return mBase.getApplicationContext();}调用的是 ContextImpl.getApplicationContext()。
@Overridepublic Context getApplicationContext() {return (mPackageInfo != null) ?mPackageInfo.getApplication() : mMainThread.getApplication();}所以返回的同样是 Application 对象 。
第三个 , getBaseContext(), 同样是 ContextWrapper 中的方法:
public Context getBaseContext() {return mBase;}所以这里返回的是 ContextImpl 对象 。
最后的打印语句是:
E/context: getApplication in Activity: luyao.android.AppE/context: getApplicationContext in Activity: luyao.android.AppE/context: getBaseContext in Activity: android.app.ContextImpl关于 Activity 就说这么多了 。 下面来看看 Service。
Service 和 ContextService 其实和 Activity 的整体流程基本一致 , 创建服务的主要逻辑在 ActivityThread.handleCreateService() 方法中 。 这里我就不贴源码了 , 简单叙述一下:

  1. 创建 LoadedApk 对象
  2. 反射创建 Service 对象
  3. 调用 ContextImpl.createAppCntext() 创建 ContextImpl 对象
  4. 创建 Application 对象
  5. 调用 service.attach() 进行绑定
  6. 回调 service 的 onCreate() 方法
直接看一下 Service.attach() 方法:
public final void attach(Context context,ActivityThread thread, String className, IBinder token,Application application, Object activityManager) {attachBaseContext(context);......}又看到了熟悉的 attachBaseContext() 方法 。
Activity 和 Service 都是继承自 ContextWrapper 的 , 最后都是通过 attachBaseContext() 对 ContextImpl 类型的 mBase 赋值 。 而 ContentProvider 和 BroadcastReceiver 都没有继承 Context , 所以它们获取 Context 的方式会有一点不一样 。
ContentProvider 和 Context先来看 ContentProvider , 创建 Provider 的逻辑在 Activity.installProvider() 方法中:
private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;// 创建 LoadedApk 和 ContextImplc = context.createPackageContext(ai.packageName,Context.CONTEXT_INCLUDE_CODE);try {......// 创建 ContentProviderlocalProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);provider = localProvider.getIContentProvider();......// 绑定 ContextlocalProvider.attachInfo(c, info);} catch (java.lang.Exception e) {......}......return retHolder;}最后在 ContentProvider.attachInfo() 方法中进行了 ContextImpl 的赋值操作 。
private void attachInfo(Context context, ProviderInfo info, boolean testing) {if (mContext == null) {// 给 mContext 赋值mContext = context;......// 回调 onCreate()ContentProvider.this.onCreate();}}这样 ContentProvider 也能拿到 Context 对象了 。
BroadcastReceiver 和 Context最后就是 BroadcastReceiver 了 , 对应 ActivityThread.handleReceiver()方法:
private void handleReceiver(ReceiverData data) {......// 创建 LoadedApk 对象LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Application app;BroadcastReceiver receiver;ContextImpl context;try {// 创建 Application 对象app = packageInfo.makeApplication(false, mInstrumentation);// 创建 ContextImpl 对象context = (ContextImpl) app.getBaseContext();......// 创建 BroadcastReceiver 对象receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);} catch (Exception e) {......}try {......// 回调 onReceive()receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);} catch (Exception e) {......} finally {sCurrentBroadcastIntent.set(null);}......}大多数步骤和 Activity 还是类似的 , 只是到最后回调 onReceive() 方法的时候 , 才会把 ContextImpl 对象传过去 。 注意 , 这里并不是直接返回原生的 ContextImpl 对象 , 而是调用 context.getReceiverRestrictedContext()返回一个 受限制 的 ReceiverRestrictedContext , 你无法使用这个 Context 对象启动 Service。