Android中关于Context的三言两语

前言

今天咱们来分析一下 Context 的源码,在 APP 开发中,咱们会常常用到 Context ,那么什么是 Context 呢?它的常规语义是“上下文”那么这个“上下文”究竟是什么呢?经过源码分析,咱们能对这个Context有个基本的认识。android

类继承图

咱们来看下关于 Context 的类继承图,咱们经过查看源码得知,Context 是一个抽象类,因此它确定有其实现类,查阅得知它的实现类为 ContextWrapper 和 ContextImpl ,因此它的继承图以下:bash

在这里插入图片描述

以上的 Context 类继承关系清晰简洁,能够得知,Application 、 Service 、Activity 都是继承的 Context 类,因此从这里咱们能够得知:app

Context 数量 = Activity 数量 + Service 数量 + 1
复制代码

另外,咱们能够看到 Application 和 Service 都是直接继承 ContextWrapper 的而 Activity 倒是继承 ContextThemeWrapper 的,这是为什么?其实 ContextThemeWrapper 是关于主题类的,Activity 是有界面的,而 Application 和 Service 却没有。接下来咱们来详细看下它们的源码实现。ide

ContextWrapper

咱们进入到 ContextWrapper 源码中能够发现,它其实调用了 mBase 里面的方法,而 mBase 实际上是 ContextImpl ,因此最终仍是得调用它的实现类 ContextImpl 类里面的方法。oop

public class ContextWrapper extends Context {
    Context mBase;
    public ContextWrapper(Context base) {
        mBase = base;
    }
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    //其他的都是覆盖Context里面的方法
}
复制代码

咱们能够按照上面的类的继承图进行依次分析,由上面能够知道 ContextWrapper 实际上是调用 ContextImpl 里面的方法,因此 Application 和 Service 还有 Activity 它们应该都跟 ContextImpl 有关的。究竟是不是这样的呢?咱们追踪源码进行分析。源码分析

Application

相似于 Java 的 main 启动方法程序,Android 也有一个相似的方法,那就是在 ActivityThread 类中也有一个 main ,这是开始的地方,咱们从这里进行一点一点跟踪:学习

ActivityThread#mainui

//省略部分代码...
	  Looper.prepareMainLooper();
      ActivityThread thread = new ActivityThread();
      thread.attach(false);
      //省略部分代码...
      Looper.loop();      
      //省略部分代码...
复制代码

咱们找到 ActivityThread 的 main 方法,省略无关代码,这个 main 方法就是不断的从消息队列中获取消息,而后进行处理。咱们本次不分析 Looper 相关的东西,只分析跟 Context 有关的内容,继续进入 attach 方法,this

Android 分析源码,不能一头扎进去,咱们应该主要分析它的流程。idea

ActivityThread#attach

//省略部分代码...
				mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
				//Application的实例建立
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                
                //调用Application里面的生命周期方法onCreate
                mInitialApplication.onCreate();
//省略部分代码...
复制代码

这里面出现了 ContextImpl ,因此下面应该会跟 Application 扯上关系,因此进入到 makeApplication 方法中继续往下追踪,

LoadedApk#makeApplication

//省略部分代码...
  Application app = null;
 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
//省略部分代码...
复制代码

最终又进入到 Instrumentation#newApplication 方法里面

Instrumentation#newApplication

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }
复制代码

Application#attach

/**
    * @hide
    */
   /* package */ 
   final void attach(Context context) {
       attachBaseContext(context);
       mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
   }
复制代码

走到这里就很明清晰了,最终将会调用 ContextWrapper 的 attachBaseContext 方法。从上面到这里,如预料的同样,分析到这里,记住了多少?是否是只知道 Application 里面最终会调用 attachBaseContext 这个方法?这样的话就对了,不能一头扎进代码的海洋里,处处遨游,那样会迷失方向的,Android 源码那么大,那么多,一一细节分析根本是不大可能的,因此只能把握流程,而后再针对性的分析实现过程。接着分析 Service 里面相关的方法。

Service

对于 Service ,咱们在 ActivityThread 中能够发现有个方法叫 handleCreateService ,这里面有关于 Service 和 ContextImpl 之间的联系。

ActivityThread#handleCreateService

Service service = null;
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
           context.setOuterContext(service);
           Application app = packageInfo.makeApplication(false, mInstrumentation);
           service.attach(context, this, data.info.name, data.token, app,
                   ActivityManager.getService());
           service.onCreate();
复制代码

对于 Application 的那段代码咱们能够发现,这二者及其相似,咱们进入到 attach 方法中查看相关代码,发现

/**
    * @hide
    */
   public final void attach(
           Context context,
           ActivityThread thread, String className, IBinder token,
           Application application, Object activityManager) {
	//调用attachBaseContext方法
       attachBaseContext(context);
       mThread = thread;           // NOTE:  unused - remove?
       mClassName = className;
       mToken = token;
       mApplication = application;
       mActivityManager = (IActivityManager)activityManager;
       mStartCompatibility = getApplicationInfo().targetSdkVersion
               < Build.VERSION_CODES.ECLAIR;
   }
复制代码

代码很简单,就是这样跟 ContextImpl 扯上关系的。由于 Service 和 Application 都是继承的 ContextWrapper 类,接下来咱们来分析一下关于 Activity 的代码。

Activity

在这里说明一下为何 Service 和 Application 都是继承的 ContextWrapper 类而 Activity 倒是继承 ContextThemeWrapper 那是由于 Activity 是带有界面显示的,而 Service 和 Application 却没有,因此从名字咱们能够看到 ContextThemeWrapper 包含主题的信息,同时 ContextThemeWrapper 却又是继承自 ContextWrapper ,分析 ContextThemeWrapper 源码咱们能够看到,里面基本都是关于 theme 的方法,同时它也覆盖了 attachBaseContext 方法。

咱们进入 Activity 源码也发现它也有和 Service 相似的 attach 方法

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, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
		//省略部分代码...
        attachBaseContext(context);
复制代码

接下来咱们来分析一下 Activity 在哪里和这个扯上关系的。

ActivityThread#performLaunchActivity

performLaunchActivity 这个方法其实就是启动 Activity 的方法 ,咱们之后再来学习关于这个方法的内容,如今先分析 Context 的内容。咱们进入到这个方法查看:

//省略部分代码...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
//省略部分代码...
  activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
//省略部分代码...
复制代码

首先经过 createBaseContextForActivity 方法建立ContextImpl 而后直接有 Activity attach 进去。到此为止,关于 Application 、Service 和 Activity 关于Context 的源码基本就差很少了。接下来咱们来解决一些实际的内容。

实例理解

既然 Application、Service 和 Activity 都有 Context 那么它们之间到底有啥区别呢?同时 getApplicationContext 和 getApplication() 又有什么区别呢?接下来咱们经过代码进行验证。

咱们如今的项目通常都有自定义 Application 的类进行一些初始化操做,本例中也新建一个 MyApplication 的类继承自 Application,而后在Manifest.xml中进行注册,代码以下:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("androidos_analysis", "getApplicationContext()——> " + getApplicationContext());
        Log.d("androidos_analysis", "getBaseContext() ——> " + getBaseContext());
    }
}
复制代码

打印结果以下:

getApplicationContext()——> com.ihidea.androidosanalysis.MyApp@9831cf9
getBaseContext()       ——> android.app.ContextImpl@13d643e
复制代码

咱们发现当咱们经过 getApplicationContext 获取的是咱们申明的 Application 实例,而经过 getBaseContext 获取到的倒是 ContextImpl 这是为何呢?咱们查看它们的实现发现

ContextWrapper#getBaseContext

/**
   * @return the base context as set by the constructor or setBaseContext
   */
  public Context getBaseContext() {
      return mBase;
  }
复制代码

其实在上文咱们已经分析过了它们的源码,咱们知道其实这个mBase就是 ContextImpl 了。而 getApplicationContext

ContextWrapper#getApplicationContext

@Override
  public Context getApplicationContext() {
      return mBase.getApplicationContext();
  }
复制代码

经过上面分析咱们知道 其实 Application 它自己也是一个 Context 因此,这个们返回的就是它本身了。因此这里获取getApplicationContext()获得的结果就是MyApplication自己的实例。

有时候咱们代码里面也会有关于 getApplication 的用法,那么 这个跟 getApplicationContext 又有什么区别呢?咱们再来log一下就知道了。

咱们建立一个 MainActivity 而后在里面打印两行代码:

MainActivity#onCreate

Log.d("androidos_analysis", "getApplicationContext()——> " + getApplicationContext());
Log.d("androidos_analysis", "getApplication() ——> " + getApplication());
复制代码

在这里插入图片描述

咱们能够发现 这两个返回的结果都是 同样的,其实不难理解,

Activity#getApplication

/** Return the application that owns this activity. */
   public final Application getApplication() {
       return mApplication;
   }
复制代码

其实 getApplication 返回的就是 Application 因此这二者是同样的了。可是都是返回的 Application ,Android 为何要存在这两个方法呢?这就涉及到做用域的问题了,咱们能够发现使用 getApplication 的方法的做用范围是 Activity 和 Service ,可是咱们在其余地方却不能使用这个方法,这种状况下咱们就可使用 getApplicationContext 来获取 Application 了。什么状况下呢?譬如:BroadcastReceiver 咱们想在Receiver 中获取 Application 的实例咱们就能够经过这种方式来获取:

public class MyReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) { 
        MyApplication myApp = (MyApplication) context.getApplicationContext();  
        //...
    }  
}
复制代码

以上内容就是关于 Context 的部份内容。

关于做者

专一于 Android 开发多年,喜欢写 blog 记录总结学习经验,blog 同步更新于本人的公众号,欢迎你们关注,一块儿交流学习~

在这里插入图片描述
相关文章
相关标签/搜索