Android 开发中必须了解的 Context
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的创建过程十分清楚:
- 创建ContextImpl对象,方法最终走到静态方法
ContextImpl.createActivityContext()
创建。 - 创建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,所以反射创建后,调用任何的系统的方法都是无效的。 - Activity初始化,调用Activity.attch(),这个方法对Activity做各种所需的初始化,Context、Thread、parent、Window、Token等等,而Context的初始化就是调用ContextWrapper.attachBaseContext()把第一步创建的ContextImpl设置到baseContext。
- Theme设置,前面说到Activity实现的是ContextThemeWrapper,对ContextWrapper扩展并支持了Theme的替换,调用ContextThemeWrapper.setTheme()完成Theme的初始化。
4.一些思考
ContextThemeWrapper作为ContextWrapper一个扩展,它是重写了ContextImpl中的一些关于Theme的实现,也就是说ContextImpl本身也是有Theme的实现,它提供的Theme是整个APP的Theme,而这里扩展了之后,支持了Theme的替换之后,在不同的页面支持了不同的Theme设置。
Context作为应用程序环境和运行时状态的信息,设计初衷上它应该是固定的,在创建成功之后就禁止改变,所以在ContextWrapper.attachBaseContext()中设置了拦截,只允许设置一次baseContext,重新设置会抛出异常。但是在一些特殊的场景中,比如跨页面使用View,或者提前创建View的时候,其实会有场景涉及替换Context。另一个坑是ContextWrapper限制baseContext只允许系统调用。不过在SDK31中,官方提供了一个特殊版本的ContextWrapper,也就是MutableContextWrapper,支持了替换baseContext。
Context设计是很典型的装饰器模式,Context抽象定义了具体的接口;ContextImpl具体实现了Context定义的所有方法;ContextWrapper继承了Context接口,并包装了具体实现ContextImpl;ContextThemeWrapper继承了ContextWrapper并扩展了替换Theme的功能。
5. 附录
装饰器模式是一种结构型设计模式,它允许我们在运行时动态地为一个对象添加新的行为,而无需修改其源代码。装饰器模式通过将对象包装在一个装饰器对象中,来增加对象的功能。装饰器模式是一种非常灵活的模式,它可以在不改变原始对象的情况下,动态地添加新的行为和功能。
装饰器模式的核心思想是将对象包装在一个或多个装饰器对象中,这些装饰器对象具有与原始对象相同的接口,可以在不改变原始对象的情况下,为其添加新的行为。装饰器对象可以嵌套在一起,形成一个链式结构,从而实现更复杂的功能。
装饰器模式的结构由四个基本元素组成:
抽象组件(Component):定义了一个对象的基本接口,可以是一个抽象类或接口。
具体组件(ConcreteComponent):实现了抽象组件接口,是被装饰的对象。
抽象装饰器(Decorator):继承或实现了抽象组件接口,用于包装具体组件或其他装饰器。
具体装饰器(ConcreteDecorator):继承或实现了抽象装饰器接口,实现了具体的装饰逻辑。
装饰器模式的优点在于:
可以动态地为对象添加新的行为,无需修改其源代码。
可以嵌套多个装饰器对象,形成一个链式结构,从而实现更复杂的功能。
装饰器对象与原始对象具有相同的接口,可以完全替代原始对象。
装饰器模式的缺点在于:
可能会导致类的数量增加,增加代码的复杂度。
在装饰器链中,有些装饰器可能不被使用,但仍然需要创建和维护,浪费资源。
链接:https://juejin.cn/post/7224433996137431101
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。