Context
,我们在写android应用时,每天都在和它打交道,但是我们真的了解它吗?
我先问各位读者一个问题,看大家回答得怎么样:
什么是Context? 它有什么作用?
可能有读者会回答,它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等。
但这样有点模糊了,能更具体一点呢?它描述了哪些应用程序环境信息,又有提供了哪几类操作,是什么?
我们接下来就来细剖一下,看看Context还有它的家族。
什么是Context
我们来看看谷歌官方是怎么定义的:
/**
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
英语渣来翻译一下:
- 它是关于应用程序环境全局信息的接口。
- 该类是一个抽象(abstract class)类,Android 系统提供了该抽象类的具体实现类。
- 允许访问特定于应用程序的资源和类,以及向上调用应用程序级操作,如启动Activity,广播和接收Intent等。
本质上,Context只定义了一套基本的功能接口,它不负责具体的实现。那么又是由谁来负责实现它呢?
在回答这个问题前,我们先来认识一下Context家族。
Context家族
我们知道,Activity、Application、Service都是Context的子类。Context还有没有其他的子类呢,它们的继承关系是怎样的呢?
我们来看一下context的相关类的继承关系:
类图中出现了三个我们不常见到的类,先来认识一下它们:
ContextImpl : Context API的实现类,它为Activity和其他应用程序组件提供基本Context对象。
ContextWrapper : Context的代理实现,它只将其所有调用委托给另一个Context。可以子类化以修改行为而不更改原始Context。
ContextThremeWapper : 具有指定主题的新的ContextWrapper。
怎么理解Context、ContextImpl以及ContextWrapper的关系呢?
这得从设计模式开始讲起,我们先来认识一个设计模式————装饰者模式。
装饰者模式 :动态地给一个对象添加一些额外的职责。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
它通常由四部分组成:
- Component:抽象类或者接口,这是装饰者和被装饰者都需要实现的接口或者继承的基类。
- ConcreteComponent:被装饰者的具体构件
- Decorate:装饰者角色,一般是一个抽象类,定义一个private变量指向Component。
- ConcreteDecotator:装饰者具体实现类,一些在方法中实现一些功能的添加
怎么去理解装饰者模式在Context上的应用呢?
ContextImpl是Context定义的接口的基本实现,无论是Application、Service、Activity都具有这些共同的特性或者业务需求。ContextWrapper是装饰者角色,它定义了一个mBase变量指向了ContextImpl,ContextWrapper子类是装饰者的具体实现类,由它再去扩展额外的功能,这些功能的修改和添加不需要对ContextImpl类做改动,从而保证设计既具有扩展弹性,又能遵从开放关闭原则。
Context
我们来看看Context,它有哪些方法,在这里限制于篇幅的原因,只举例部分方法,一些常用的方法就不再列出了。
第一类 ——— 访问应用程序资源、属性以及类
int getNextAutofillId()
此方法用于自动生成ID,所有返回的ID都不会重复。
ApplicationInfo getApplicationInfo()
返回此上下文包的完整应用程序信息。ApplicationInfo的数据结构如下:
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
//应用程序中所有的Activity默认的task Affinity,具体请自行查阅taskAffinity属性
public String taskAffinity;
//可选项,访问当前应用所有组件需要声明的权限,从”android:permission“属性得到
public String permission;
//从”android:process“属性得到,注明应用运行的进程名。或不设置则默认为应用包名。
public String processName;
//继承Application类的对象的类名
public String className;
//application 的描述 资源id
public int descriptionRes;
//application 主题 资源id
public int theme;
// 从“android:manageSpaceActivity”属性得到,用于指定一个Activity来管理数据,
//它最终会出现在设置->应用程序管理中,默认为按钮为”清除数据”,指定此属性后,该按钮可点击跳转到
//该Activity, 让用户选择性清除哪些数据。若不设置则为null.
public String manageSpaceActivityName;
//实现应用程序的备份功能的类,配置文件中“backupAgent”的属性值。
//如果android:allowBackup设置为false,忽略这个属性
public String backupAgentName;
//一个可选属性,指示应用程序支持应用程序数据的自动备份
//默认值为0,表示将备份应用程序的整个数据文件夹+托管外部存储空间。
//任何负值表示该应用程序不支持完整数据备份,尽管它可能仍希望通过传统的key/value备份API参与;
//正数标识指定一个xml资源,这个xml文件定义了应用程序在备份时include/exclude策略
@UnsupportedAppUsage
public int fullBackupContent = 0;
//应用程序所有Activity的默认的UIOptions,具体请参考android:uiOptions属性
public int uiOptions = 0;
// 所需屏幕空间的最短尺寸,具体请参考android:requiresSmallestWidthDp属性
public int requiresSmallestWidthDp = 0;
//应用程序支持的最大纵横比
public float maxAspectRatio;
//应用程序apk的全路径
public String sourceDir;
// 公共资源路径
public String publicSourceDir;
//所有已安装的拆分APK的名称,按字典顺序排列
public String[] splitNames;
//零个或多个拆分APK的完整路径,按字典顺序排列
public String[] splitSourceDirs;
//指向此应用程序链接的所有共享库的路径。
public String[] sharedLibraryFiles;
//此应用程序链接的所有共享库的列表
public List<SharedLibraryInfo> sharedLibraryInfos;
//为包的持久数据指定的默认目录的完整路径。
public String dataDir;
//为包的永久性数据分配的设备保护目录的完整路径。
public String deviceProtectedDataDir;
//为包的持久数据分配的凭据保护目录的完整路径。
@SystemApi
public String credentialProtectedDataDir;
//存储本机JNI库的目录的完整路径。
public String nativeLibraryDir;
//此应用程序所需的主ABI是从应用程序捆绑包的本机JNI库的ABI推断出来的
//如果此应用程序不需要任何特定的ABI,其值将为null
@UnsupportedAppUsage
public String primaryCpuAbi;
//已分配给此应用程序的内核用户ID;这不是唯一的ID(多个应用程序可以有相同的uid)
public int uid;
// app 声明的版本号
public long longVersionCode;
//提供应用程序的网络安全配置的资源文件
public int networkSecurityConfigRes;
//应用程序要在其中运行的沙盒的版本。
@SystemApi
public int targetSandboxVersion;
//请自行查阅appComponentFactory条目
public String appComponentFactory;
//com.android.internal.R.styleable.AndroidManifestProvider_icon的资源id
public int iconRes;
}
UserHandle getUser()
获取与此上下文关联的用户。
设备上的用户用UserHandle类来表示,UserHandler中有三个概念需要认识一下:
userid: linux 系统用户的uid
appid: 跟app相关,即使是不同用户,包名相同的appid都一样。
uid: 这个uid不是指的Linux的uid,是android 特有的uid。由于Android4.2才开始支持多用户,但这个时候Linux的uid/gid多用户体系已经被用在App管理上,所以造成了概念上的混乱。这个uid是由userid 和 appid 生成的。算法为 userid / 100000 + appid % 100000
。
IBinder getActivityToken()
如方法名称描述一样,获取Activity Token。
什么是Activity Token?
直接说结论,具体分析的过程就不贴了。Activity Token是一个IBinder对象,它实际的实现子类是LocalActivityRecord
这个类,这个类的字段内容如下所示:
final String id; // Unique name of this record.
Intent intent; // Which activity to run here.
ActivityInfo activityInfo; // Package manager info about activity.
Activity activity; // Currently instantiated activity.
Window window; // Activity's top-level window.
Bundle instanceState; // Last retrieved freeze state.
int curState = RESTORED; // Current state the activity is in.
大家可以根据字段自行体会一下Activity Token到底有什么作用。
第二类 ——— 向上调用应用程序级操作
Object getSystemService(@ServiceName @NonNull String name)
按名称返回系统级服务的句柄。返回对象的类因请求的名称而异。
getSystemService
方法最终是从SystemServiceRegistry
取出一个缓存对象返回给应用。
SystemServiceRegistry
类在初始化时,就会注册好所有的系统服务,并用一个ArrayMap
来进行缓存。
void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options, UserHandle userId)
允许以指定的用户启动Activity。此方法对未预装在系统镜像上的应用程序无效。
void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent, @Intent.MutableFlags int flagsMask, @Intent.MutableFlags int flagsValues, int extraFlags, @Nullable Bundle options)
类似于startActivity(Intent, Bundle)
, 但是要使用IntentSender启动。如果IntentSender用于某个Activity,则该Activity将启动,就如同你调用了startActivity(Intent)
。否则,将执行其关联的操作(例如发送广播),就如同你调用了IntentSender.sendentent
。
IntentSender的官方解释如下:
对要执行的Intent和目标Action的描述。返回的对象可以交给其他应用程序,以便它们可以在以后代表您执行所描述的操作。
void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions)
发送带有一组权限的广播,只有声明了同样权限的接收者才能收到此广播。
startInstrumentation(@NonNull ComponentName className, @Nullable String profileFile, @Nullable Bundle arguments)
开始执行android.app.Instrumentation
类。给定的Instrumentation组件会先杀掉目标Application(如果正在运行的话),启动目标进程,实例化Instrumentation组件,然后再让它驱动应用程序。