Android: 在Activity和Service之间使用的单例模式

8
我想知道创建一个单例并在Android活动和Android服务之间使用是否是个坏主意。据我所知,静态字段,在我的情况下是单例,只要整个进程存活,就一直可用。
我的计划是使用单例而不是Parcelable来在活动和后台服务之间共享数据。因此,我的Activity1会通过调用MySingleton.getInstance().addData(foo)添加一些数据;然后,我将发送一个Intent通知我的服务新数据已添加到单例中。接下来,我的后台服务将处理该意图并调用MySingleton.getInstance().getLatestData(),然后它将处理数据(需要一些时间)。服务的结果将使用单例“post”回来并触发广播意图,由Activity1(如果存在)处理,Activity1将从单例中检索结果。
你们认为这是个坏主意吗?
编辑: 我想要实现的是一个软件,从Web服务器下载数据解析并返回结果。因此,我的Activity将创建DownloadJob对象。 DownloadJob对象将被放入DownloadScheduler(Singleton)中,该对象可以对所有DownloadJobs进行排队和管理。 DownloadScheduler将允许同时运行5个DownloadJobs,并使用队列存储等待时间。实际下载将由DownloadService(IntentService)执行,该服务通过意图得到通知,告知其现在应立即执行(下载)新的DownloadJob。 DowanlodService将从DownloadSchedulers队列(PriorityBlockingQueue)中检索下一个作业,并通过设置DownloadJob.setResult(...)返回结果,并触发广播意图,表示结果已准备好,该意图将由DownloadScheduler接收,该队列将从队列中删除作业并通知Activity下载已完成等。
因此,在我的情况下,我将使用单例来从DownloadService访问DownloadJobs,而不是使用Intent传递DownloadJob Parcelable。因此,我避免了一个问题,即在内存中有两个DownloadJobs(一个在“Activity Site”上,一个在“Service site”上)。
有什么更好的解决方法吗?
静态实例是否会在Android系统内存不足时被释放?那么,子类化应用程序并在那里保存引用(非静态)是否可以避免这个问题?

是的,在Android中这是一个不好的想法,因为你的进程随时可能会死亡,最好将数据存储在某个持久化存储中,然后编写内容提供程序作为其接口。 - Kristopher Micinski
我不需要将数据持久存储。我只需要一种队列,Activitis将某些对象放入其中,Service检索它们并进行一些工作,然后返回结果... - sockeqwe
3个回答

3
如果您仅将单例作为后台服务之间的共享内存(我假设在不同线程上执行操作),则可能会遇到同步问题或读取不一致的数据。
如果单例中的数据未同步,则必须小心,因为您依赖于自己的“协议”来确保在后台线程写入时没有人在读取(这可能导致错误)。
另一方面,如果它是同步的,则面临anr错误的风险,因为读取数据的活动可能会被阻塞,等待服务完成将数据写入单例。
正如其他人所说,您还必须记住,如果操作系统需要资源,可能会释放您的单例,并且您的数据可能不再存在。
我更喜欢使用事件总线,例如ottoeventbus 编辑:

使用单例作为后台(意图)服务的入口点是2010年Virgil Dobjanschi关于构建Android REST客户端应用程序的演讲中提出的方法。

建议的方法是使用单例作为正在进行的请求的控制器。还要考虑到,对意图服务的请求已经由操作系统排队,因此您可以抛出多个意图,这些意图将按顺序由意图服务处理。

有一段时间,我也尝试以此作为库的起点,但该库仍未完成。您可以在这里找到源代码。

我肯定不会将数据存储在单例中。我更喜欢的方法是将数据存储在某些持久存储中(例如SQL/首选项/文件/内容提供程序),并通过广播消息(或如果您使用内容提供程序,则通过观察者)告知客户端更改。

最后,某种程度上这是robospice库所采用的方法,看起来非常成熟,并且提供了许多有趣的功能,例如缓存。

嗨,感谢您的答复。我已将一个具体的场景添加到我的帖子中。EventBus似乎是一个很好的解决方案。您有使用EventBus的经验吗?您知道哪个表现更好(EventBus还是Intent)吗? - sockeqwe
我修改了答案。虽然我从未尝试过evenbus,但考虑到您的澄清,我会选择robospice。 - fedepaol

1
一个更好的想法是子类化Application并将任何长期存在的对象放在其中。通过子类化Application,您可以正确处理应用程序的启动和关闭,这是使用单例无法轻松完成的。此外,通过使用Application,活动和服务可以共享对程序中模型的访问,而无需使用可包裹对象。这样,您就可以避免所有单例为您的程序带来的问题。
您也不必诉诸于将所有内容存储在数据库中,因为这需要大量的样板代码才能将一堆数据塞进去。它对于在应用程序的各个部分之间共享行为没有任何作用,并且对于促进活动的通信和集中化也没有任何作用。如果您确实需要在关闭之间保持状态,那么请使用它,但如果不需要,您可以节省很多工作。
您还可以尝试使用Roboguice等工具,将共享模型注入到您的活动和服务中。
您可能会发现这个链接有帮助:Android开发中的设计模式原则是什么?

子类化应用程序是一个好的选择。那么你认为我在子类化的应用程序中应该使用“普通”的引用而不是单例模式吗?顺便说一下,我已经在我的帖子中添加了一个具体的场景。 - sockeqwe
如果您正在构建下载管理器,则不应使用单例模式。您真的永远不应该使用单例模式,尤其是对于下载管理器而言更是如此。首先,如果您遇到Android想要关闭您的应用程序的情况,您将会知道它,因为应用程序会收到回调。如果发生这种情况,您可以正确地关闭在后台运行的每个线程,并保存需要恢复的任何数据。访问应用程序的代码很简单:MyApplication app =(MyApplication)activity.getApplication()。我不确定您所说的“正常”引用是什么意思。 - chubbsondubs

0

像这样使用单例并不一定是个坏主意,但如果Android决定停止了您的进程,则会失去其状态。您可以考虑将状态存储在SQLite数据库或持久队列中(可以参考 tape )。


谢谢你的提示,但我不需要持久化任何对象。请看我在上一篇帖子中的最后一次编辑。 - sockeqwe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接