【问题标题】:Android: Singleton which is used between Activity and ServiceAndroid:在 Activity 和 Service 之间使用的 Singleton
【发布时间】:2012-11-24 06:36:03
【问题描述】:

我想知道创建一个在某些 Android 活动和 Android 服务之间使用的单例是否是个坏主意。据我所知,只要整个流程还活着,就可以使用静态字段,在我的情况下是单例。

我的计划是使用单例而不是 Parcelable 在我的活动和后台服务之间共享数据。所以我的 Activity1 将通过调用 MySingleton.getInstance().addData(foo); 添加一些数据;然后我会发送一个 Intent 通知我的服务新数据已添加到单例中。接下来,我的 BackgroundService 将处理意图并调用 MySingleton.getInstance().getLatestData();然后它将处理数据(需要一些时间)。服务的结果接下来将通过使用单例“回传”并触发广播意图,由 Activity1(如果还活着)处理,而 Activity1 将从单例中检索结果。

你们觉得这是个坏主意吗?

编辑: 我想要实现的是一个从网络服务器下载数据并解析它并返回结果的软件。所以我的 Activity 会创建 DownloadJob 对象。 DownloadJob-Object 将被放入排队和管理所有 DownloadJobs 的 DownloadScheduler (Singleton)。 DownloadScheduler 将允许同时运行 5 个 DownloadJobs 并使用队列来存储等待。有效的下载将由 DownloadService (IntentService) 完成,它通过 Intent 获得通知,现在应该立即执行(下载)新的 DownloadJob。 DowanlodService 将从 DownloadSchedulers 队列 (PriorityBlockingQueue) 中检索下一个作业,并通过设置 DownloadJob.setResult(...) 返回 Result 并启动广播意图,即 Result 已准备好,这将由 DownloadScheduler 接收从队列中删除作业并通知 Activity 下载完成等。

因此,在我的场景中,我将使用单例从 DownloadService 访问 DownloadJobs,而不是使 DownloadJob Parcelable 并与 Intent 一起传递。所以我会避免这个问题,我在内存中有两个 DownloadJobs(一个在“活动站点”上,一个在“服务站点”上)。

有什么建议可以更好地解决这个问题吗?

静态实例,如 DownloadScheduler(Singleton),是否会被 android 系统在低内存上释放?那么子类化应用程序并保留引用(非静态)会避免这个问题吗?

【问题讨论】:

  • 是的,这是个坏主意,在 Android 中,您的进程可能随时终止,将您的数据保存在某个持久存储中,然后编写一个内容提供程序作为其接口。
  • 我不通过网络来持久存储数据。我只需要某种队列,Activitis 将一些对象放入其中,服务检索它们并做一些工作,然后返回结果......

标签: android singleton


【解决方案1】:

如果您使用单例只是作为后台服务之间的共享内存,我假设它在不同的线程上执行操作,您可能会遇到同步问题或读取不一致的数据。

如果单例中的数据不同步,则必须小心,因为您依赖“协议”来确保在后台线程写入时没有人在读取(这可能会导致错误)。

另一方面,如果它是同步的,您将面临面临错误的风险,因为读取数据的活动可能会被阻塞,等待服务完成将数据写入单例中。

正如另一位所说,您还必须记住,如果操作系统需要资源,您的单例可能会被释放,并且您的数据可能不再存在。

我宁愿使用 ottoeventbus 等事件总线

编辑:

使用单例作为后台(意图)服务的入口点是 2010 年 Virgil Dobjanschi talk 关于为 android 构建 rest 客户端应用程序所建议的方法。

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

前段时间我也尝试将其作为图书馆的起点,但仍未完成。你可以找到来源here

我当然不会将您的数据存储在单例中。我更喜欢的方法是将数据存储在一些持久性存储中(例如 sql/preferences/file/content provider),并通过广播消息让客户端知道更改(或者,如果您使用的是 content provider,则通过观察者)。

最后,在某种程度上,这是 robospice 库所遵循的方法,它看起来相当成熟,并提供了许多有趣的功能,例如缓存。

【讨论】:

  • 您好,谢谢您的回答。我在我的帖子中添加了一个具体的场景。 EventBus 似乎是一个很好的解决方案。你有使用过 EventBus 的经验吗?你知道什么会更好(EventBus 或 Intent)吗?
  • 我修改了答案。我从未尝试过evenbus,但鉴于您的澄清,我会选择robospice。
【解决方案2】:

一个更好的主意是继承 Application 并将任何长期存在的对象放在那里。通过继承 Application 您可以正确处理应用程序的启动和关闭,这是单例无法轻松完成的。此外,通过使用应用程序,活动和服务可以共享对程序中模型的访问,而无需借助 parcelables。并且您可以避免单例给您的程序带来的所有问题。

您也不必求助于将所有内容存储在数据库中,这需要大量样板代码才能将一堆数据推入其中。它对在应用程序的各个部分之间共享行为没有任何作用,也没有对促进活动的通信和集中化做任何事情。如果您确实需要在关闭之间保持状态,请使用它,但如果不需要,您可以为自己节省大量工作。

您还可以考虑使用 Roboguice 之类的东西,它可以将共享模型注入到您的活动和服务中。

您可能会发现这很有帮助:

what's design pattern principle in the Android development?

【讨论】:

  • 子类化应用程序很好。那么你认为我应该在子类应用程序中使用“普通”引用而不是单例吗?顺便提一句。我在帖子中添加了一个具体场景。
  • 如果您正在构建下载管理器,那么您不应该使用单例。你真的不应该使用单例,尤其是对于下载管理器。一方面,如果您遇到 Android 想要关闭您的应用程序的情况,您会知道它,因为应用程序会收到回调。如果发生这种情况,您可以正确关闭在后台运行的每个线程,以保存您需要恢复的任何数据。访问应用程序的代码很简单:MyApplication app = (MyApplication)activity.getApplication()。我不确定您所说的“正常”参考是什么意思。
【解决方案3】:

使用这样的单例不一定是个坏主意,但如果 Android 决定停止您的进程,您将失去它的状态。您可能需要考虑将您的状态存储在 SQLite 数据库或持久队列中(请查看 tape 以获得一个很好的示例)。

【讨论】:

  • 感谢您的提示,但我不需要保留任何对象。请参阅我对上一篇文章的最后编辑。
猜你喜欢
  • 2013-01-19
  • 1970-01-01
  • 2011-08-30
  • 2012-05-17
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多