2

我有一个相当棘手的情况,我正在尝试确定最佳设计。基本情况是这样的:

  • 我正在设计一个与电子邮件界面相似的消息传递系统。
  • 当用户单击带有附件的消息时,将生成一个活动,该活动显示该消息的文本以及一个表明还有附加附件的回形针。
  • 此时,我开始预加载附件,以便当用户单击它时 - 它加载得更快。
  • 目前,当用户单击附件时,它会提示加载对话框,直到下载完成,此时它会加载单独的附件查看器活动,并传入 bmp 字节数组。
  • 我不想将附件保存到持久存储中。

我遇到的困难是支持旋转以及按下主页按钮等。下载当前是通过线程和处理程序设置完成的。

而不是这个,我希望流程如下:

  • 用户像以前一样加载消息,像以前一样预加载附件(用户不可见)。
  • 当用户单击附件链接时,附件查看器活动会立即生成。
  • 如果下载完成,将显示图像。如果没有,则在此活动中显示一个对话框,直到它完成并可以显示。请注意,理想情况下,下载永远不会重新启动,否则我在预加载上浪费了周期。

显然,我需要一些能够持续下载并能够回调任意绑定活动的持久后台进程。看起来 IntentService 几乎符合我的需求,因为它在后台线程中工作并且具有服务(非 UI)生命周期。但是,它可以满足我的其他需求吗?

我注意到我想要做的事情的常见实现从调用者 Activity 获取 Messenger,以便可以将 Message 对象发送回调用者线程中的 Handler。这一切都很好,但是在我的情况下,当调用者 Activity 被停止或销毁并且当前活动的 Activity(附件查看器)正在显示时会发生什么? 是否有某种方法可以将新 Activity 动态绑定到正在运行的 IntentService,以便我可以将消息发送回新 Activity?

另一个问题是关于 Message 对象。我可以在这个包中发回任意大的数据吗?例如,我不需要发回“文件已下载”,而是需要发回下载文件本身的字节数组,因为我不想将其写入磁盘(是的,必须如此)。

非常感谢任何有关实现我想要的行为的建议。我使用 Android 的时间不长,而且我经常对如何在 Activity 生命周期中最好地处理异步进程感到困惑,尤其是在方向更改和主页按钮按下时……

4

2 回答 2

2

您使用 IntentService 启动、工作,然后将其发布到某个持久存储中。要与 Activity 通信,您可以在 Activity 中使用广播接收器,并从 IntentService 发送广播 Intent。

旋转和按下主页按钮对 IntentService 没有影响,只要它仍在工作,但旋转会执行重绘,主页按钮会暂停 Activity。由于这些原因,您应该避免在 AsyncTask 中执行异步工作,除非它不必完成并且不必持久化。

消息是针对处理程序的,而不是针对组件的。通过系统传输信息的基本功能是意图和广播意图。

IntentService 简单地处理将 Intent 转换为 Message、创建新的 HandlerThread、将 Message 放入 HandlerThread 的 MessageQueue 以及触发 Looper 的工作。在后台线程上执行的 run() 是 onHandleIntent() 中的任何内容(这有点简化)。

于 2012-11-19T22:55:39.623 回答
2

有很多方法可以解决这个问题。你是对的,我认为你显然需要一个服务来将下载过程与可能来来去去的活动分离。为此,您需要在此服务中实现一些下载队列,并且 IntentService 提供了开箱即用的此行为,因此您可以使用它。IntentService 唯一想到的缺点是取消您之前发布的工作并不容易。

但是,我不会序列化下载的数据,它听起来效率不高,尤其是在处理大量二进制数据时。您可以将下载的数据写入内存中的 LRU 缓存并为其分配一个 ID,查看器活动可以通过该 ID 获取下载的数据。

您还可以使用广播 Intent 向服务中的查看器活动发出信号,表明具有特定 ID 的下载已完成。这将确保查看活动和下载服务之间的松散耦合。

所以它会是这样的:

  1. 用户打开带有附件的消息。
  2. 您将此附件的下载作业发布到下载器 IntentService,并为此附件分配一个唯一 ID。
  3. 当用户单击附件时,您将启动查看器活动并将附件的唯一 ID 作为此活动的参数传递。
  4. 查看器活动打开,它立即检查缓存以显示要显示的内容。如果还没有完成,它会显示一些加载图形并等待下载器 IntentService 用广播 Intent 喊出“xy ID 完成”。如果 xy ID == 传递给此查看器活动的 ID,您将从缓存中获取现已完全下载的内容并将其呈现给用户。

编辑:

至于缓存,你必须做出一些选择。我认为这本身就是一个相当广泛的话题,所以我将根据我之前的项目在这里写一些想法。

上面,我只提到了内存中的 LRU 缓存。这将存在于自定义应用程序对象中,或作为单例。这是使用邪恶的全局状态,但我认为在这种情况下它是合理的。优点是您不需要在任何地方序列化下载的数据:下载完成,内存中有内容,您可以立即从 Viewer Activity 访问它。缺点是,这些数据只有在没有从缓存中删除或者系统不会因为内存不足而终止应用程序的进程时才可用。

为了避免在丢失此易失性内存缓存的内容时重新下载数据,您需要为其添加一个持久层。例如将下载的数据写入应用程序的缓存目录。这带来了将下载的数据写入磁盘的开销,但至少您以后可以访问它,即使系统杀死了您的进程。您可以使用 2 级缓存方法将此持久性缓存与内存中缓存相结合,或者您可以选择其中任何一种。我建议查看一些广泛使用的实现此行为的图像下载器库以获取灵感,例如Universal Image Loader

于 2012-11-19T22:56:04.840 回答