你真的了解Context吗?

571 阅读8分钟

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.
 */

英语渣来翻译一下:

  1. 它是关于应用程序环境全局信息的接口。
  2. 该类是一个抽象(abstract class)类,Android 系统提供了该抽象类的具体实现类。
  3. 允许访问特定于应用程序的资源和类,以及向上调用应用程序级操作,如启动Activity,广播和接收Intent等。

本质上,Context只定义了一套基本的功能接口,它不负责具体的实现。那么又是由谁来负责实现它呢?

在回答这个问题前,我们先来认识一下Context家族。

Context家族

我们知道,Activity、Application、Service都是Context的子类。Context还有没有其他的子类呢,它们的继承关系是怎样的呢?

image.png

我们来看一下context的相关类的继承关系:

image.png

类图中出现了三个我们不常见到的类,先来认识一下它们:

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组件,然后再让它驱动应用程序。