前言

Context也就是上下文对象,是Android较为常用的类,但是对于Context,很多人都停留在会用的阶段,这个系列会带大家从源码角度来分析Context,从而更加深入的理解它。

1.Context概述

Context意为上下文或者场景,是一个应用程序环境信息的接口。
在开发中我们经常会使用Context,它的使用场景总的来说分为两大类,它们分别是:

  • 使用Context调用方法,比如:启动Activity、访问资源、调用系统级服务等。
  • 调用方法时传入Context,比如:弹出Toast、创建Dialog等。

Activity、Service和Application都是间接的继承自Context的,因此,我们可以计算出一个应用程序进程中有多少个Context,这个数量等于Activity和Service的总个数加1,1指的是Application的数量。

Context是一个抽象类,它的内部定义了很多方法以及静态常量,它的具体实现类为ContextImpl。和Context相关联的类,除了ContextImpl还有ContextWrapper、ContextThemeWrapper和Activity等等,下面给出Context的关系图。

从图中我们可以看出,ContextImpl和ContextWrapper继承自Context,ContextWrapper内部包含有Context类型的mBase对象,mBase具体指向的是ContextImpl。ContextImpl提供了很多功能,但是外界需要使用并拓展ContextImpl的功能,因此设计上使用了装饰模式,ContextWrapper是装饰类,它对ContextImpl进行包装,ContextWrapper主要是起了方法传递作用,ContextWrapper中几乎所有的方法实现都是调用ContextImpl的相应方法来实现的。
ContextThemeWrapper、Service和Application都继承自ContextWrapper,这样他们都可以通过mBase来使用Context的方法,同时它们也是装饰类,在ContextWrapper的基础上又添加了不同的功能。
ContextThemeWrapper中包含和主题相关的方法(比如: getTheme方法),因此,需要主题的Activity继承ContextThemeWrapper,而不需要主题的Service则继承ContextWrapper。

2.Application Context的创建过程

我们通过调用getApplicationContext来获取应用程序的全局的Application Context,那么Application Context是如何创建的呢?
当一个应用程序启动完成后,应用程序就会有一个全局的Application Context。那么我们就从应用程序启动过程开始着手。

Android深入四大组件(一)应用程序启动过程(后篇)这篇文章的最后讲了ActivityThread启动Activity。ActivityThread作为应用程序进程的核心类,它会调用它的内部类ApplicationThread的scheduleLaunchActivity方法来启动Activity,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

private class ApplicationThread extends ApplicationThreadNative {
...
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
...
sendMessage(H.LAUNCH_ACTIVITY, r);
}
...
}

在ApplicationThread的scheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑会在主线程中执行。我们接着查看H类的handleMessage方法对LAUNCH_ACTIVITY类型的消息的处理。

frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {
public static final int LAUNCH_ACTIVITY = ;
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);//
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");//
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break; ...
}

H继承自Handler ,是ActivityThread的内部类。在注释1处通过getPackageInfoNoCheck方法获得LoadedApk类型的对象,并将该对象赋值给ActivityClientRecord 的成员变量packageInfo,其中LoadedApk用来描述已加载的APK文件。在注释2处调用handleLaunchActivity方法,如下所示。
frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
Activity a = performLaunchActivity(r, customIntent);
...
}

我们接着查看performLaunchActivity方法:
frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
}
...
return activity;
}

performLaunchActivity方法中有很多重要的逻辑,这里只保留了Application Context相关的逻辑,想要更多了解performLaunchActivity方法中的逻辑请查看Android深入四大组件(一)应用程序启动过程(后篇)这篇文章的第二小节。这里ActivityClientRecord 的成员变量packageInfo是LoadedApk类型的,我们接着来查看LoadedApk的makeApplication方法,如下所示。
frameworks/base/core/java/android/app/LoadedApk.java

public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {//
return mApplication;
}
...
try {
...
java.lang.ClassLoader cl = getClassLoader();
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);//
appContext.setOuterContext(app);//
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;//
...
return app;
}

注释1处如果mApplication不为null则返回mApplication,这里假设是第一次启动应用程序,因此mApplication为null。在注释2处通过ContextImpl的createAppContext方法来创建ContextImpl。注释3处的代码用来创建Application,在Instrumentation的newApplication方法中传入了ClassLoader类型的对象以及注释2处创建的ContextImpl 。在注释4处将Application赋值给ContextImpl的Context类型的成员变量mOuterContext。注释5处将Application赋值给LoadedApk的成员变量mApplication,在Application Context的获取过程中我们会再次用到mApplication。我们来查看注释3处的Application是如何创建的,Instrumentation的newApplication方法如下所示。
frameworks/base/core/java/android/app/Instrumentation.java

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

Instrumentation中有两个newApplication重载方法,最终会调用上面这个重载方法。注释1处通过反射来创建Application,并调用了Application的attach方法,并将ContextImpl传进去:
frameworks/base/core/java/android/app/Application.java

/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

attach方法中调用了attachBaseContext方法,它的实现在Application的父类ContextWrapper中,代码如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}

从上文我们得知,这个base指的是ContextImpl,将ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase。Application Context的创建过程就讲到这里,最后给出Application Context创建过程的时序图。
绘图1_副本.png

3.Application Context的获取过程

当我们熟知了Application Context的创建过程,那么它的获取过程会非常好理解。我们通过调用getApplicationContext方法来获得Application Context,getApplicationContext方法的实现在ContextWrapper中,如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}

从上文我们得知,mBase指的是ContextImpl,我们来查看 ContextImpl的getApplicationContext方法:
frameworks/base/core/java/android/app/ContextImpl.java

@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}

如果LoadedApk不为null,则调用LoadedApk的getApplication方法,否则调用AvtivityThread的getApplication方法。由于应用程序这时已经启动,因此LoadedApk不会为null,则会调用LoadedApk的getApplication方法:
frameworks/base/core/java/android/app/LoadedApk.java

Application getApplication() {
return mApplication;
}

这里的mApplication我们应该很熟悉,它在上文LoadedApk的makeApplication方法的注释5处被赋值。这样我们通过getApplicationContext方法就获取到了Application Context。

Android深入理解Context(一)Context关联类和Application Context创建过程的更多相关文章

  1. Android深入理解Context(二)Activity和Service的Context创建过程

    前言 上一篇文章我们学习了Context关联类和Application Context的创建过程,这一篇我们接着来学习Activity和Service的Context创建过程.需要注意的是,本篇的知识 ...

  2. Android深入理解Context(三)Context完全解析

      1· Context类型        我们知道,Android应用都是使用Java语言来编写的,那么大家可以思考一下,一个Android程序和一个Java程序,他们最大的区别在哪里?划分界限又是 ...

  3. Android学习系列(37)--App调试内存泄露之Context篇(下)

    接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...

  4. Android中Context详解 ---- 你所不知道的Context

    转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好,  ...

  5. 转:Android中Context详解 ---- 你所不知道的Context

    转:http://blog.csdn.net/qinjuning/article/details/7310620 转:http://blog.csdn.net/lmj623565791/article ...

  6. Android开发之Android Context,上下文(Activity Context, Application Context)

    转载:http://blog.csdn.net/lmj623565791/article/details/40481055 1.Context概念Context,相信不管是第一天开发Android,还 ...

  7. Android中Context具体解释 ---- 你所不知道的Context

                                                                                                         ...

  8. Android中Context详解 ---- 你所不知道的Context(转)

    Android中Context详解 ---- 你所不知道的Context(转)                                               本文出处 :http://b ...

  9. Android Context完全解析与各种获取Context方法

    Context类型 我们知道,Android应用都是使用Java语言来编写的,那么大家可以思考一下,一个Android程序和一个Java程序,他们最大的区别在哪里?划分界限又是什么呢?其实简单点分析, ...

随机推荐

  1. ORM 开发环境之利器:MVC 中间件 FreeSql.AdminLTE

    前言 这是一篇纯技术干货的分享文章,FreeSql 已经基本完成 .NETCore 最方便的 ORM 使命,我们正在筹备生态的建立,比如 ABP 中如何使用 FreeSql 的实现,需要各种各样的扩展 ...

  2. 改造断路器集群监控Hystrix Turbine实现自动注册消费者、实时监控多个服务

    在上一篇文章中,我们搭建了Hystrix Dashoard,对指定接口进行监控.但是只能对一个接口进行监听,功能比较局限: Turbine:汇总系统内多个服务的数据并显示到 Hystrix Dashb ...

  3. html的<h>标签

    <h>标签:标题标签. <h>标签只有六个:<h1>........<h6>

  4. Mysql B+Tree原理

    B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引.B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的.在讲B ...

  5. ReactiveSwift源码解析(六) SignalProtocol的take(first)与collect()延展实现

    上篇博客我们聊了observe().map().filter()延展函数的具体实现方式以及使用方式.我们在之前的博客中已经聊过,Signal的主要功能是位于SignalProtocol的协议延展中的, ...

  6. 事务,Oracle,MySQL及Spring事务隔离级别

    一.什么是事务: 事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败. 二.事务特性(4种): 原子性 (atomicity):强调事务的不可分割:一致性 (consiste ...

  7. 调研pwa和sw

    概述 处于好奇,最近我调研了一下pwa和service worker,有些新的,记录下来,供以后开发时参考,相信对其他人也有用.pwa主要是通过service worker实现的,它主要包括桌面图标, ...

  8. python脚本简化jar操作命令

    本篇和大家分享的是使用python简化对jar包操作命令,封装成简短关键字或词,达到操作简便的目的.最近在回顾和构思shell脚本工具,后面一些文章应该会分享shell内容,希望大家继续关注. 获取磁 ...

  9. [Abp vNext 源码分析] - 3. 依赖注入与拦截器

    一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...

  10. KnockoutJS-绑定元素

    工作变更,又走回了WPF,一个来月没有接触web开发了,之前的KnockoutJS却不想放弃,继续进行知识的巩固,下个月开始重新走回web开发之路,还是得用回一些习惯了的工具.本次开始接触各绑定元素功 ...