我在09年Android相对较新的时候就写下了这个答案,并且在Android开发中有很多尚未完善的领域。我在本文的底部添加了很长的附录,以解决一些批评,并详细说明了我对使用Singleton而不是对Application进行子类化时的哲学分歧。阅读它需要您自担风险。
原始答案:
您遇到的更普遍的问题是如何在多个“活动”和应用程序的所有部分中保存状态。静态变量(例如,单例)是实现此目的的常见Java方法。但是,我发现,Android中一种更优雅的方法是将您的状态与Application上下文相关联。
如您所知,每个Activity也是一个Context,它是广义上有关其执行环境的信息。您的应用程序还具有上下文,Android保证它在您的应用程序中作为单个实例存在。
这样做的方法是创建自己的android.app.Application子类,然后在清单的application标记中指定该类。现在,Android将自动创建该类的实例,并将其用于整个应用程序。可以从任何访问它context
使用Context.getApplicationContext()
方法(Activity
还提供了一种方法getApplication()
,其具有完全相同的效果)。以下是一个极为简化的示例,但要注意以下几点:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
本质上与使用静态变量或单例具有相同的效果,但是可以很好地集成到现有的Android框架中。请注意,这无法跨进程使用(如果您的应用是具有多个进程的稀有应用之一)。
上面的示例中有一些注意事项;假设我们做了类似的事情:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
现在,每次实例化应用程序时,都会执行这种缓慢的初始化(例如,击中磁盘,击中网络,任何阻塞等)!您可能会认为,这只是该过程的一次,无论如何我都必须支付费用,对吗?例如,正如黛安·哈克伯恩(Dianne Hackborn)在下文中提到的那样,完全有可能实例化您的过程(仅)以处理后台广播事件。如果您的广播处理不需要这种状态,则可能只是无所事事地完成了一系列复杂而缓慢的操作。延迟实例化是这里的游戏名称。以下是使用应用程序的稍微复杂的方法,除了最简单的使用方法之外,其他方法都更有意义:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
虽然我更喜欢Application子类,而不是在这里使用单例作为更优雅的解决方案,但我宁愿开发人员在确实需要时使用Singleton,而不是完全不考虑将状态与Application子类相关联的性能和多线程含义。
注意1:同样,在反咖啡厅中,为了正确地将应用程序替代项与应用程序相关联,清单文件中必须有一个标记。同样,请参阅Android文档以获取更多信息。一个例子:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
注意2: user608578在下面询问如何与管理本机对象生命周期一起工作。我丝毫没有加快在Android上使用本机代码的速度,也没有资格回答如何与我的解决方案交互。如果有人对此有答案,我愿意将其归功于此,并将这些信息放在此文章中,以实现最大的可视性。
附录:
正如某些人指出的那样,这不是持久状态的解决方案,我也许应该在原始答案中更加强调这一点。即,这并不意味着它是一种用于保存用户或其他信息的解决方案,这些信息或信息将在应用程序的整个生命周期中保持不变。因此,我认为下面的大多数批评都与应用程序随时被杀死等无关,因为任何需要保留到磁盘的内容都不应通过Application子类进行存储。它旨在成为一种解决方案,用于存储临时的,易于重新创建的应用程序状态(例如,是否已登录用户)和本质上是单个实例的组件(例如,应用程序网络管理器)(不是单例!)。
Dayerman友好地指出了与Reto Meier和Dianne Hackborn的有趣对话,在该对话中,不鼓励使用Application子类来支持Singleton模式。Somatik还早些时候指出了这种性质的东西,尽管当时我没有看到。由于Reto和Dianne在维护Android平台方面所扮演的角色,因此我不能真诚地建议您忽略他们的建议。他们说的去。我确实不同意关于优先选择Singleton而不是Application子类的观点。在我的不同意中,我将利用在StackExchange对Singleton设计模式的解释中最好地解释的概念,这样我就不必在此答案中定义术语。我强烈建议先跳过链接,然后再继续。逐点:
Dianne指出:“没有理由从Application进行子类化。这与创建单例没有什么不同……”第一个主张是不正确的。这有两个主要原因。1)Application类为应用程序开发人员提供了更好的生命周期保证;它保证了应用程序的生命周期。单例并没有明确地依赖于应用程序的生命周期(尽管有效)。对于您的普通应用程序开发人员来说,这可能不是问题,但是我认为这正是Android API应该提供的合同类型,并且通过最大程度地减少关联的生命周期,它还为Android系统提供了更大的灵活性。数据。2)Application类为应用程序开发人员提供了状态的单个实例持有者,这与单身汉的州持有人有很大的不同。有关差异的列表,请参见上面的Singleton说明链接。
Dianne继续说道:“ ...将来很可能会后悔,因为您发现您的Application对象变成了本应独立的应用程序逻辑的大混乱。” 这当然不是不正确的,但这不是选择Singleton而不是Application子类的原因。Diane的论点都没有提供使用Singleton比Application子类更好的理由,她试图建立的唯一理由是使用Singleton并不比Application子类差,我认为这是错误的。
她继续说:“这更自然地导致您应该如何管理这些事情-根据需要初始化它们。” 这忽略了以下事实:您也没有理由也无法使用Application子类按需初始化。再次没有区别。
Dianne的结尾是:“框架本身为应用程序维护的所有少量共享数据都有成千上万的单例,例如已加载资源的缓存,对象池等。它运作良好。” 我不是在争论使用Singletons不能正常工作或不是合法的选择。我在争辩说,Singletons与Android系统之间的合同不如使用Application子类提供的合同强大,而且使用Singletons通常指向不灵活的设计,这种设计不容易修改,并且会导致很多问题。恕我直言,Android API为开发人员应用程序提供的强大合同是Android编程中最吸引人,最令人愉悦的方面之一,它有助于导致早期开发人员采用,这促使Android平台取得了今天的成功。
Dianne在下面也进行了评论,并提到了使用Application子类的其他缺点,它们可能鼓励或简化编写性能较低的代码的过程。这是真的,我已经编辑了此答案,以强调此处考虑性能的重要性,如果您正在使用应用程序子类化,则应采用正确的方法。正如Dianne所说,重要的是要记住,即使只为后台广播而加载进程,每次加载进程时也会实例化Application类(如果您的应用程序在多个进程中运行,则可能一次被实例化!)事件。因此,重要的是将Application类更多地用作指向应用程序共享组件的指针的存储库,而不是用作进行任何处理的地方!
我留下了以下从SingleStack链接窃取的Singletons缺点:
- 无法使用抽象或接口类;
- 无法分类;
- 整个应用程序之间的高度耦合(难以修改);
- 难以测试(在单元测试中不能伪造/模拟);
- 在可变状态下很难并行化(需要广泛的锁定);
并添加我自己的:
- 不清楚且难以管理的终身合同,不适合Android(或大多数其他)开发;