标签:android application context 全局变量
在上一篇博客中,我讲了初次开发安卓必须知道的 6件事(6 THINGS I WISH I KNEW BEFORE I WROTE MY FIRST ANDROID APP)。其中一条就是:不要有一个Context的静态引用。我这么警告的原因是一个Context的静态引用可能引发内存泄露。但是一位读者指出:一个Application Context的静态引用不会造成内存泄露,因为只要程序还在运行,Application Context的生命周期就不会结束。我则反驳到:技术上来说,你可以拥有一个Application Context的静态引用而不造成内存泄露,但是我推荐你这样做。
在这篇博客中,我想解释一下为什么拥有和使用一个Application Context的静态引用不是一个理想的选择。之所以强调“理想的选择”,因为我并不是说使用Application Context的静态引用每次都会造成程序崩溃。相对的,我这篇博客所说的是一些使用Context静态引用的缺陷,以至于说这并不是开发安卓应用最简洁的方式。
静态的获取全局变量并没有将它们(全局变量)构造函数和方法的依赖关系告诉给阅读代码的人。全局变量和单例通过API掩盖了它们真实的依赖关系。如果想要真正理解依赖关系,开发人员必须逐行阅读代码。(Accessing global state statically doesn’t clarify those shared dependencies to readers of the constructors and methods that use the Global State. Global State and Singletons make APIs lie about their true dependencies. To really understand the dependencies, developers must read every line of code.)
一个Application Context的全局静态引用也正如这点所说:读这个对象的人无法知道,这个对象依赖Context只是为了使用它的API。当一个对象拥有一个清晰真实的API来表达它的依赖,就能够更容易的理解类或者方法的功能以及它将如何实现这个功能。
下面用一个简单的例子进行阐述。假设你当你阅读代码时,遇到了一个这样的方法名称:
public void displayString(String stringToDisplay)
public void displayString(Context context, String stringToDisplay)
(译者:可能翻译的有些不通顺了,这里总的解释一下。就是说使用ApplicationContext的静态引用去使用一些方法的话,你是无法判断这个方法是不是需要用到Context的。而如果不使用ApplicationContext的静态引用的话,当一个方法需要用到Context对象时(如Toast),就必须多一个Context参数。而不需要时,则不会有Context参数。阅读代码的时候就可以根据这一点对方法的实现上有一定的估计)。
(封装)保障了一个对象的行为只能被它的API所影响。它保障了无关对象之间不会产生未知的引用,从而使得我们可以控制一个对象的修改对系统其它部分的影响。([It] Ensures that the behavior of an object can only be affected through its API. It lets us control how much a change to one object will impact other parts of the system by ensuring that there are no unexpected dependencies between unrelated components. -Pg. 92)
因为使用ApplicationContext静态引用的对象关联的是一个全局依赖,这些对象的行为可能会被全局共享的Application Context所影响。因为Application Context并不是这些对象API的一部分,这就意味着对象行为的改变可能不是被该对象的API所影响的。换一句话说,这就意味着使用Application Context静态引用会破坏封装。
在大多数情况下,以这样一种形式破坏封装不会产生太大影响。事实上,我仅可以想象的几个可能产生问题的例子也看起来像是故意编造的。但是,我仍然认为,在其他条件相等的情况下,我们应当选择能100%在全部情况下适用的结构,而不是99%都适用的结构。再一次的,使用ApplicationContext的静态引用和对封装的破坏并不会使你的程序崩溃,但是这并不是最稳定的结构。
public class ServiceLauncherTests { @Mock Context mContext; @Test public void launchesSessionCalendarService() { ServiceLauncher serviceLauncher = new ServiceLauncher(mContext); serviceLauncher.launchSessionCalendarService(); verify(mContext).startService(any(Intent.class)); } }如果这个ServiceLaucher使用了Application Context静态引用,这个对象就很难进行单元测试了。在这个例子当中,你可以使用测试支持库的UI测试来验证Intent被发送了,但是UI测试比单元测试要慢。并且,你也可能需要验证Context中也一些不使用Intent的方法。所以,注入一个Context到目标对象中相比全局静态变量更加灵活,即使你可以使用测试支持库来帮助你验证Intent的发送。
public class SmellySessionColorResolver { public SmellySessionColorResolver() { } public int resolveSessionColor(int sessionColor) { if (sessionColor == 0) { // no color -- use default sessionColor = IOApplication.getContext().getResources().getColor(R.color.default_session_color); } else { // make sure it's opaque sessionColor = UIUtils.setColorAlpha(sessionColor, 255); } return sessionColor; } }这就违背了迪米特法则。我实际上也抱怨过违背迪米特法则使得一个程序难以进行单元测试。但是即使你不关心单元测试,违背迪米特法则通常被视为一种恶心的代码风格。
使用ApplicationContext作为全局变量引用的缺陷
标签:android application context 全局变量
原文地址:http://blog.csdn.net/hwz2311245/article/details/47731477