【问题标题】:IntentService android download and return file to ActivityIntentService android下载并返回文件给Activity
【发布时间】:2012-11-19 22:29:04
【问题描述】:

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

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

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

我希望流程如下:

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

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

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

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

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

【问题讨论】:

    标签: android service download


    【解决方案1】:

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

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

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

    所以它会是这样的:

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

    编辑:

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

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

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

    【讨论】:

    • 听起来不错,但是对于全局可访问的缓存,您有什么想法?我能想到的唯一能达到这个目的的是 Application 对象,但我确信这不是存储这些东西的好地方。也许你的意思是我不知道的较低级别的东西?
    • 很棒的文章。非常感谢。
    【解决方案2】:

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

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

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

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

    【讨论】:

    • 感谢您的回复。不幸的是,我根本无法存储到磁盘,但鉴于 Zsombor 的帖子可能存在漏洞。我所说的 Messanger 涉及从调用方 Intent 获取 EXTRA_MESSENGER 属性并构造一个 Message 以发送回其 Handler。这可能不是正确的方法 - 正是我所看到的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多