一种 Android 应用内全局获取 Context 实例的装置

一种 Android 应用内全局获取 Context 实例的装置

App 运行的时候,肯定是存在至少一个 Application 实例的。同时,Context 我们再熟悉不过了,写代码的时候经常需要使用到 Context 实例,它一般是通过构造方法传递进来,通过方法的形式参数传递进来,或者是通过 attach 方法传递进我们需要用到的类。Context 实在是太重要了,以至于我经常恨不得着藏着掖着,随身带着,这样需要用到的时候就能立刻掏出来用用。但是换个角度想想,既然 App 运行的时候,Application 实例总是存在的,那么为何不设置一个全局可以访问的静态方法用于获取 Context 实例,这样以来就不需要上面那些繁琐的传递方式。
说到这里,有的人可能说想这不是我们经常干的好事吗,有必要说的这么玄乎?少侠莫急,请听吾辈徐徐道来。

获取Context实例的一般方式

这再简单不过了。

一种 Android 应用内全局获取 Context 实例的装置

这种方式应该是最常见的获取 Context 实例的方式了,优点就是严格按照代码规范来,不用担心兼容性问题;缺点就是 API 设计严重依赖于 Context 这个 API,如果早期接口设计不严谨,后期代码重构的时候可能很要命。此外还有一个比较有趣的问题,我们经常使用 Activity 或者 Application 类的实例作为 Context 的实例使用,而前者本身又实现了别的接口,比如以下代码。

一种 Android 应用内全局获取 Context 实例的装置

这段代码是我许久前看过的代码,本身不是什么厉害的东西,不过这段代码段我至今印象深刻。设想,如果 Foo 的接口设计可以不用依赖 Context,那么这里至少可以少一个this不是吗。

获取Context实例的二般方式

现在许多开发者喜欢设计一个全局可以访问的静态方法,这样以来在设计 API 的时候,就不需要依赖 Context 了,代码看起来像是这样的。

一种 Android 应用内全局获取 Context 实例的装置

这样在整个项目中,都可以通过Foo#getContext()获取 Context 实例了。不过目前看起来好像还有点小缺陷,就是使用前需要调用Foo#setContext(Context)方法进行注册(这里暂不讨论静态 Context 实例带来的问题,这不是本篇幅的关注点)。好吧,以我的聪明才智,很快就想到了优化方案。

一种 Android 应用内全局获取 Context 实例的装置

不过这样又有带来了另一个问题,一般情况下,我们是把应用的入口程序类FooApplication放在 App 模块下的,这样一来,Library 模块里面代码就访问不到FooApplication#getContext()了。当然把FooApplication下移到基础库里面也是一种办法,不过以我的聪明才智又立刻想到了个好点子。

一种 Android 应用内全局获取 Context 实例的装置

这样以来,就不用把FooApplication下移到基础库里面,Library 模块里面的代码也可以通过BaseApplication#getContext()访问到 Context 实例了。嗯,这看起来似乎是一种神奇的膜法,因吹斯听。然而,代码写完还没来得及提交,包工头打了个电话来和我说,由于项目接入了第三发 SDK,需要把FooApplication继承SdkApplication。

有没有什么办法能让FooApplication同时继承BaseApplication和SdkApplication啊?(场面一度很尴尬,这里省略一万字。)

以上谈到的,都是以前我们在获取 Context 实例的时候遇到的一些麻烦:

  • 类 API 设计需要依赖 Context(这是一种好习惯,我可没说这不好);
  • 持有静态的 Context 实例容易引发的内存泄露问题;
  • 需要提注册 Context 实例(或者释放);
  • 污染程序的 Application 类;
    那么,有没有一种方式,能够让我们在整个项目中可以全局访问到 Context 实例,不要提前注册,不会污染 Application 类,更加不会引发静态 Context 实例带来的内存泄露呢?

一种全局获取 Context 实例的方式

回到最开始的话,App 运行的时候,肯定存在至少一个 Application 实例。如果我们能够在系统创建这个实例的时候,获取这个实例的应用,是不是就可以全局获取 Context 实例了(因为这个实例是运行时一直存在的,所以也就不用担心静态 Context 实例带来的问题)。那么问题来了,Application 实例是什么时候创建的呢?首先先来看看我们经常用来获取 Base Context 实例的Application#attachBaseContext(Context)方法,它是继承自ContextWrapper#attachBaseContext(Context)的。

一种 Android 应用内全局获取 Context 实例的装置

是谁调用了这个方法呢?可以很快定位到Application#attach(Context)。

一种 Android 应用内全局获取 Context 实例的装置

又是谁调用了Application#attach(Context)方法呢?一路下来可以直接定位到Instrumentation#newApplication(Class

关注微信公众号获取更多相关资源

一种 Android 应用内全局获取 Context 实例的装置