单身人士与Android中的应用程序上下文?

msc*_*ker 360 java singleton android design-patterns

回顾这篇文章列举了使用单例的几个问题 并看过使用单例模式的Android应用程序的几个例子,我想知道使用Singletons而不是通过全局应用程序状态共享的单个实例是一个好主意(继承android.os.Application并获取它通过context.getApplication()).

两种机制都有哪些优点/缺点?

说实话,我希望在这篇文章中使用Web应用程序的单例模式得到相同的答案,不是一个好主意!但适用于Android.我对么?DalvikVM有什么不同呢?

编辑:我想对涉及的几个方面有意见:

  • 同步
  • 可重用性
  • 测试

Mat*_*ias 294

我非常不同意Dianne Hackborn的回应.我们一点一点地从项目中删除所有单例,转而使用轻量级的任务范围对象,这些对象在您实际需要时可以轻松地重新创建.

单身人士是一个测试的噩梦,如果懒洋洋地初始化,将引入"状态不确定性",带有微妙的副作用(当将呼叫getInstance()从一个范围转移到另一个范围时可能突然出现).可见性已被提及为另一个问题,并且由于单例意味着对共享状态的"全局"(=随机)访问,因此在并发应用程序中未正确同步时可能会出现细微的错误.

我认为它是一种反模式,它是一种糟糕的面向对象的风格,基本上相当于维持全球状态.

回到你的问题:

尽管应用程序上下文本身可以被视为单例,但它是框架管理的,并且具有明确定义的生命周期,范围和访问路径.因此,我相信如果你确实需要管理应用程序全局状态,它应该在这里,其他地方.对于其他任何事情,重新考虑如果你真的需要一个单例对象,或者如果还可以重写你的单例类而不是实例化执行手头任务的小的,短期的对象.

  • 如果您推荐应用程序,则建议您使用单身人士.老实说,没有办法绕过它.Application*是*单身,具有更疯狂的语义.我不会进入关于单身人士的宗教论点,你永远不应该使用这些论点.我更喜欢实用 - 有些地方它们是维持每个进程状态的好选择,并且可以通过这样做来简化事情,你也可以在错误的情况下使用它们并射击自己. (130认同)
  • 老实说,它不会阻止你在脚下射击自己比单身一样.这有点令人困惑,但是没有*应用程序的生命周期.它是在您的应用程序启动时(在其任何组件被实例化之前)和在此时调用的onCreate()创建的,并且......就是全部.它坐在那里永远活着,直到这个过程被杀死.就像一个单身人士.:) (93认同)
  • 哦,有一件事可能让人感到困惑--Android非常适合在流程中运行应用程序,以及管理这些流程的生命周期.所以在Android上,单例是一种非常自然的方式来利用流程管理 - 如果你想在你的流程中缓存一些东西,直到平台需要为其他东西回收进程的内存,那么将该状态放在单例中就可以做到这一点. (30认同)
  • 是的,我确实提到"应用程序上下文本身可以被认为是单身人士".不同之处在于,使用应用程序实例,将自己投入脚部会更加困难,因为它的生命周期由框架处理.像Guice,Hivemind或Spring这样的DI框架也使用单例,但这是开发人员不应该关心的实现细节.我认为依靠正确实现的框架语义而不是自己的代码通常更安全.是的,我承认我做到了!:-) (17认同)
  • 好的,还算公平.我只能说自从我们离开自我管理的单身人士之后我就没有回头.我们现在选择了一个轻量级DI风格的解决方案,我们保留一个工厂单件(RootFactory),然后由app实例管理(如果你愿意的话,它是一个委托).此单例管理所有应用程序组件所依赖的公共依赖项,但实例化在一个位置(应用程序类)中进行管理.虽然使用这种方法仍然存在一个单例,但它仅限于Application类,因此没有其他代码模块知道该"详细信息". (7认同)
  • 而且,顺便说一句,如果我同意黛安和马蒂亚斯,那还可以吗?:) (6认同)
  • 嘿戴夫,我所说的是懒惰的初始化(通常与单身人士一起使用,虽然肯定没有必要).当然懒惰初始化没有任何问题,但与Singletons一起暗示了"懒惰的全局状态",这是第一次访问Singleton.这可能导致细微的错误,其中语句的重新排序导致不同的执行行为.就个人而言,我发现如果一个问题的解决方案需要我处理这么多微妙的问题,那么它可能不是一个好的解决方案. (3认同)
  • 这些是对辛格尔顿的经典批评,我原则上同意这一点.在实践中,有时它们完全有意义,只要你记住了危险 - 实际上这些问题对所有对象都是正确的:减少可变状态,封装对你必须维护的状态的访问等等我有兴趣知道你所指的是什么样的非决定性,马蒂亚斯.您使用的是线程安全的单例模式还是仅使用经典的"if(instance == null)"等模式?不要质疑你的练习 - 我想知道我是否遗漏了什么.:) (2认同)
  • 来自iOS世界的一个词......在iOS世界中等效的"应用程序"确实......**恰好是单身**.嘿!它与任何其他单身人物完全一样**.如果我理解来自hackbod的评论,那就是**在**中的情况也是如此......除了应用程序带有"更疯狂的语义" - 但它仍然是一个单身人士.(我愚蠢的Apple设备上该死的拼写检查器改为'黑客'BTW,抱歉!呵呵.) (2认同)
  • @Matthias当进程被杀死并不是那么简单,因为"Android只是重启它,所以一切都将被重新初始化".像Philippe Breault这样的问题(http://goo.gl/oIbhbx) (2认同)

hac*_*bod 229

我非常推荐单身人士.如果您有需要上下文的单例,请:

MySingleton.getInstance(Context c) {
    //
    // ... needing to create ...
    sInstance = new MySingleton(c.getApplicationContext());
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢单身人士而不是应用程序,因为它有助于保持应用程序更有条理和更模块化 - 而不是让一个地方需要维护整个应用程序中的所有全局状态,每个单独的部分都可以自行处理.事实上,单身人士懒惰地初始化(在请求时)而不是引导你在Application.onCreate()中完成所有初始化的路径是好的.

使用单身人士并没有什么本质上的错误.只要有意义,请正确使用它们.Android框架实际上有很多,因为它可以维护已加载资源和其他此类事物的每个进程缓存.

同样对于简单的应用程序,多线程不会成为单例的问题,因为通过设计,应用程序的所有标准回调都会在进程的主线程上调度,因此除非您通过线程明确地引入多线程,否则不会发生多线程隐式地通过将内容提供者或服务IBinder发布到其他进程.

只是要考虑你在做什么.:)

  • **使用单身人士没有任何本质上的错误.只要有意义就正确使用它们.**..确切地说,确切地说.**Android框架实际上有很多,因为它可以维护加载资源和其他类似事物的每个进程缓存.**正如你所说.在iOS世界的朋友中,iOS中的"一切都是单身".在物理设备上没有什么比单身概念更自然了:gps,时钟,陀螺仪等等 - 概念上你如何设计这些比作为单身人士?是的. (8认同)
  • 不适用于外部事件 - 也在主线程上调用BroadcastReceiver.onReceive(). (2认同)
  • 好的.你能指点我一些阅读材料(我更喜欢代码),在那里我可以看到主线程调度机制吗?我认为这将立刻为我澄清几个概念.提前致谢. (2认同)
  • 这是主要的应用程序端调度代码:http://android.git.kernel.org/?p = platform/frameworks/base.git; a = blob; f = core/java /android/app/ActivityThread.java (2认同)

Som*_*tik 20

来自:开发人员>参考 - 应用程序

通常不需要子类Application.在大多数情况下,静态单例可以以更模块化的方式提供相同的功能.如果你的单例需要一个全局上下文(例如注册广播接收器),那么检索它的函数可以给一个Context,它在第一次构造单例时在内部使用Context.getApplicationContext().


Jos*_*éMi 11

我遇到了同样的问题:Singleton还是做了一个子类android.os.Application?

首先我尝试使用Singleton但我的应用程序在某个时候调用了浏览器

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
Run Code Online (Sandbox Code Playgroud)

问题是,如果手机没有足够的内存,大多数类(甚至单身人士)都会被清理以获得一些内存,因此,当从浏览器返回到我的应用程序时,它每次都会崩溃.

解决方案:将所需数据放入Application类的子类中.

  • 真?Dalvik卸载课程并失去计划状态?你确定它不是垃圾收集那种有限的生命周期活动相关的对象,你不应该首先放入单身人士?你必须给clrar例子这样一个非凡的主张! (15认同)

sun*_*ang 10

申请与Singleton不同.原因是:

  1. 在ui线程中调用应用程序的方法(例如onCreate);
  2. 可以在任何线程中调用singleton的方法;
  3. 在Application的"onCreate"方法中,您可以实例化Handler;
  4. 如果单例在非ui线程中执行,则无法实例化Handler;
  5. 应用程序具有管理app中活动生命周期的能力.它具有方法"registerActivityLifecycleCallbacks".但是单身人士没有这种能力.


adr*_*ale 5

同时考虑两者:

  • 将singleton对象作为类中的静态实例.
  • 有一个公共类(Context),它返回应用程序中所有singelton对象的单例实例,这样做的好处是Context中的方法名称有意义,例如:context.getLoggedinUser()而不是User.getInstance().

此外,我建议您扩展Context以不仅包括对单例对象的访问,还包括需要全局访问的一些功能,例如:context.logOffUser(),context.readSavedData()等.可能将Context重命名为门面就有意义了.