回顾这个post enumerating several problems of using singletons 并且看过使用单例模式的Android应用程序的几个例子,我想知道使用Singletons而不是通过全局应用程序状态共享的单个实例(子类化android.os.Application并通过context.getApplication()获取它)是一个好主意。 / p>
两种机制都有哪些优点/缺点?
说实话,我希望在这篇文章Singleton pattern with Web application, Not a good idea!中得到相同的答案,但适用于Android。我对么? DalvikVM有什么不同呢?
编辑:我想就所涉及的几个方面发表意见:答案 0 :(得分:291)
我非常不同意Dianne Hackborn的回应。我们一点一点地删除项目中的所有单例,转而使用轻量级的任务范围对象,这些对象很容易在您真正需要时重新创建。
单身人士是一个测试的噩梦,如果懒洋洋地初始化,会引入“状态不确定性”带有微妙的副作用(当从一个范围转移到getInstance()
时,这可能会突然出现另一个)。可见性已被提及为另一个问题,并且由于单例意味着“全局”(=随机)访问共享状态,因此在并发应用程序中未正确同步时可能会出现细微错误。
我认为它是一种反模式,它是一种糟糕的面向对象的风格,基本上相当于维持全球状态。
回到你的问题:
尽管应用程序上下文本身可以被视为单例,但它是由框架管理的,并且具有明确定义的生命周期,范围和访问路径。因此,我相信如果你确实需要管理应用程序全局状态,它应该在这里,其他地方。对于其他任何事情,重新考虑如果你真的需要一个单独的对象,或者是否也可以重写你的单例类而不是实例化执行手头任务的小的,短暂的对象。
答案 1 :(得分:225)
我非常推荐单身人士。如果你有一个需要上下文的单例,请:
MySingleton.getInstance(Context c) {
//
// ... needing to create ...
sInstance = new MySingleton(c.getApplicationContext());
}
我更喜欢单身人士而不是应用程序,因为它有助于保持应用程序更有条理和模块化 - 而不是让一个地方需要维护整个应用程序中的所有全局状态,每个单独的部分都可以自行处理。事实上,单身人士懒惰地初始化(根据要求)而不是引导你在Application.onCreate()中预先做好所有初始化的路径。
使用单身人士并没有什么本质上的错误。只要有意义,请正确使用它们。 Android框架实际上有很多,因为它可以维护已加载资源和其他此类事物的每个进程缓存。
对于简单的应用程序,多线程不会成为单例的问题,因为通过设计,应用程序的所有标准回调都会在进程的主线程上调度,因此除非您明确地引入多线程,否则不会发生多线程通过线程或隐式地通过将内容提供者或服务IBinder发布到其他进程。
只要考虑一下你在做什么。 :)
答案 2 :(得分:20)
来自:Developer > reference - Application
通常不需要子类Application。在大多数情况下, 静态单例可以在更模块化的情况下提供相同的功能 办法。如果您的单身人士需要全局上下文(例如注册 广播接收器),可以给出一个检索它的功能 内部使用Context.getApplicationContext()的Context 首先构建单身人士。
答案 3 :(得分:11)
我遇到了同样的问题:Singleton或者创建一个子类android.os.Application?
首先我尝试使用Singleton但我的应用程序在某个时候调用了浏览器
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
问题是,如果手机没有足够的内存,你的大部分课程(甚至单身人士)都会被清理以获得一些内存,因此,当从浏览器返回我的应用程序时,它每次都会崩溃。
解决方案:将所需数据放入Application类的子类中。
答案 4 :(得分:10)
申请与单身人士不同。原因是:
答案 5 :(得分:5)
同时考虑两者:
此外,我建议您扩展Context以不仅包括对单例对象的访问,还包括需要全局访问的一些功能,例如:context.logOffUser(),context.readSavedData()等。可能重命名面向上的语境就有意义了。
答案 6 :(得分:4)
他们实际上是一样的。 我能看到一个区别。使用Application类,您可以在Application.onCreate()中初始化变量,并在Application.onTerminate()中销毁它们。使用单例,您必须依赖VM初始化和销毁静态。
答案 7 :(得分:3)
我的2美分:
我注意到当我的活动被破坏时,一些单例/静态字段被重置。我在一些低端2.3设备上注意到了这一点。
我的情况很简单:我只有一个私有字段“init_done”和一个静态方法“init”,我从activity.onCreate()调用。我注意到init方法在重新创建活动时重新执行。
虽然我不能证明我的肯定,但它可能与首先创建/使用单身人士/班级时有关。当活动被销毁/回收时,似乎只有这个活动引用的所有类也被回收。
我将我的单例实例移动到了Application的子类。我从应用程序实例中访问它们。从那以后,再没有注意到这个问题。
我希望这可以帮助别人。
答案 8 :(得分:3)
从众所周知的马的嘴里......
在开发应用时,您可能会发现有必要在整个应用中共享数据,上下文或服务。例如,如果您的应用具有会话数据,例如当前登录的用户,您可能希望公开此信息。在Android中,解决此问题的模式是让您的android.app.Application实例拥有所有全局数据,然后将您的Application实例视为具有各种数据和服务的静态访问器的单例。
在编写Android应用时,您保证只有一个android.app.Application类的实例,因此它安全(并且由Google Android团队推荐)将其视为单身。也就是说,您可以安全地向Application实现添加静态getInstance()方法。像这样:
public class AndroidApplication extends Application {
private static AndroidApplication sInstance;
public static AndroidApplication getInstance(){
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
}
答案 9 :(得分:2)
我的活动调用finish()(不会立即完成,但最终会完成)并调用Google Street Viewer。当我在Eclipse上调试它时,我在调用Street Viewer时与应用程序的连接中断,我理解为(整个)应用程序被关闭,据说可以释放内存(因为单个活动完成不应该导致此行为) 。不过,我可以通过onSaveInstanceState()在Bundle中保存状态,并在堆栈中下一个活动的onCreate()方法中恢复它。通过使用静态单例或子类化应用程序,我面临应用程序关闭和丢失状态(除非我将其保存在Bundle中)。因此,根据我的经验,他们在国家保护方面也是如此。我注意到Android 4.1.2和4.2.2中的连接丢失了,但4.0.7或3.2.4没有,这在我的理解中表明内存恢复机制在某些时候发生了变化。