Android 开发中必须了解的 Context

706 阅读6分钟

1. 什么是 context?

作为安卓开发工程师,Context是我们经常使用的一个重要概念。

从代码的角度来看,Context是一个抽象类,它代表着应用程序环境和运行时状态的信息。Context具有许多子类,包括Activity和Service等,每个子类都代表着不同的应用程序环境和状态。

从设计的角度来看,Context是一个非常重要的概念,因为它允许我们在应用程序中访问系统资源,例如数据库,共享偏好设置和系统服务等。Context还允许我们在应用程序中创建新的组件,例如Activity和Service等。

实际上,Context在安卓开发中几乎无处不在。例如,我们可以使用Context来启动一个新的Activity,获取应用程序的资源,读取和写入文件,以及访问系统服务和传感器等。Context还可以帮助我们管理应用程序的生命周期,例如在应用程序销毁时释放资源。

总之,Context是安卓开发中不可或缺的概念,它允许我们访问系统资源,管理应用程序的生命周期,并与系统交互。理解Context的概念和使用方法对于成为一名优秀的安卓开发工程师至关重要。

2. context继承关系

Context
├── ContextImpl
├── ContextWrapper
│   ├── Application
│   ├── Service
│   ├── ContextThemeWrapper
│   │   ├── Activity
│   │   │   ├── FragmentActivity
│   │   │   └── ...
│   │   └── ...
│   └── ...
└── ...

Context是一个抽象类,它有多个直接或间接的子类。

ContextImpl是Context的一个实现类,真正实现了Context中的所有函数,所调用的各种Context类的方法,其实现均来自于该类。

ContextWrapper是一个包装类,它可以包装另一个Context对象,并在其基础上添加新的功能。内部包含一个真正的Context引用,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。

ContextThemeWrapper是一个特殊的包装类,它可以为应用程序的UI组件添加主题样式。主题就是指Activity元素指定的主题。只有Activity需要主题,所以Activity继承自ContextThemeWrapper,而Application和Service直接继承自ContextWrapper。

总之,Context的继承关系非常复杂,但是理解这些关系对于在安卓开发中正确地使用Context非常重要。通过继承关系,我们可以了解每个Context子类的作用和用途,并且可以选择合适的Context对象来访问应用程序的资源和系统服务。

3.Context如何创建

在安卓应用程序中,Activity是通过调用startActivity()方法来启动的。当我们启动一个Activity时,系统会通过调用Activity的生命周期方法来创建、启动和销毁Activity对象。 而其中创建Activity的方法最终是走到ActivityThread.performLaunchActivity()方法。将其中无关方法删除后: 、、、 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... //创建 ContextImpl对象 ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null;

    //创建Activity对象
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    ...
    //Activity初始化
    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,
            r.assistToken, r.shareableActivityToken);
    //Theme设置
    int theme = r.activityInfo.getThemeResource();
    if (theme != 0) {
        activity.setTheme(theme);
    }
    
    return activity;
}

、、、 可以看到Activity的创建过程十分清楚:

  1. 创建ContextImpl对象,方法最终走到静态方法ContextImpl.createActivityContext()创建。
  2. 创建Activity对象,最终instantiateActivity()通过调用Class的newInstance()方法,反射创建出来,方法注解到This method is only intended to provide a hook for instantiation. It does not provide earlier access to the Activity object. The returned object will not be initialized as a Context yet and should not be used to interact with other android APIs.,方法只创建了Activity的早期对象,并没有对它做Context的初始化,所以不能调用安卓相关api。简单来说,Activity本身继承自ContextWrapper,这个方法并没有具体实现任何Context的方法,只是将所有方法代理给了内部的baseContext,所以反射创建后,调用任何的系统的方法都是无效的。
  3. Activity初始化,调用Activity.attch(),这个方法对Activity做各种所需的初始化,Context、Thread、parent、Window、Token等等,而Context的初始化就是调用ContextWrapper.attachBaseContext()把第一步创建的ContextImpl设置到baseContext。
  4. Theme设置,前面说到Activity实现的是ContextThemeWrapper,对ContextWrapper扩展并支持了Theme的替换,调用ContextThemeWrapper.setTheme()完成Theme的初始化。

4.一些思考

  1. ContextThemeWrapper作为ContextWrapper一个扩展,它是重写了ContextImpl中的一些关于Theme的实现,也就是说ContextImpl本身也是有Theme的实现,它提供的Theme是整个APP的Theme,而这里扩展了之后,支持了Theme的替换之后,在不同的页面支持了不同的Theme设置。

  2. Context作为应用程序环境和运行时状态的信息,设计初衷上它应该是固定的,在创建成功之后就禁止改变,所以在ContextWrapper.attachBaseContext()中设置了拦截,只允许设置一次baseContext,重新设置会抛出异常。但是在一些特殊的场景中,比如跨页面使用View,或者提前创建View的时候,其实会有场景涉及替换Context。另一个坑是ContextWrapper限制baseContext只允许系统调用。不过在SDK31中,官方提供了一个特殊版本的ContextWrapper,也就是MutableContextWrapper,支持了替换baseContext。

  3. Context设计是很典型的装饰器模式,Context抽象定义了具体的接口;ContextImpl具体实现了Context定义的所有方法;ContextWrapper继承了Context接口,并包装了具体实现ContextImpl;ContextThemeWrapper继承了ContextWrapper并扩展了替换Theme的功能。

5. 附录

装饰器模式是一种结构型设计模式,它允许我们在运行时动态地为一个对象添加新的行为,而无需修改其源代码。装饰器模式通过将对象包装在一个装饰器对象中,来增加对象的功能。装饰器模式是一种非常灵活的模式,它可以在不改变原始对象的情况下,动态地添加新的行为和功能。

装饰器模式的核心思想是将对象包装在一个或多个装饰器对象中,这些装饰器对象具有与原始对象相同的接口,可以在不改变原始对象的情况下,为其添加新的行为。装饰器对象可以嵌套在一起,形成一个链式结构,从而实现更复杂的功能。

装饰器模式的结构由四个基本元素组成:

  1. 抽象组件(Component):定义了一个对象的基本接口,可以是一个抽象类或接口。

  2. 具体组件(ConcreteComponent):实现了抽象组件接口,是被装饰的对象。

  3. 抽象装饰器(Decorator):继承或实现了抽象组件接口,用于包装具体组件或其他装饰器。

  4. 具体装饰器(ConcreteDecorator):继承或实现了抽象装饰器接口,实现了具体的装饰逻辑。

装饰器模式的优点在于:

  1. 可以动态地为对象添加新的行为,无需修改其源代码。

  2. 可以嵌套多个装饰器对象,形成一个链式结构,从而实现更复杂的功能。

  3. 装饰器对象与原始对象具有相同的接口,可以完全替代原始对象。

装饰器模式的缺点在于:

  1. 可能会导致类的数量增加,增加代码的复杂度。

  2. 在装饰器链中,有些装饰器可能不被使用,但仍然需要创建和维护,浪费资源。