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

时间:2010-09-30 00:35:37

标签: java android design-patterns singleton

回顾这个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有什么不同呢?

编辑:我想就所涉及的几个方面发表意见:

  • 同步
  • 复用性
  • 测试

10 个答案:

答案 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)

申请与单身人士不同。原因是:

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

答案 5 :(得分:5)

同时考虑两者:

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

此外,我建议您扩展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没有,这在我的理解中表明内存恢复机制在某些时候发生了变化。