我有一个相当棘手的情况,我正在努力确定最好的设计。基础是:
我遇到的困难是支持旋转以及按下主页按钮等。下载当前是通过线程和处理程序设置完成的。
而不是这个,我希望流程如下:
显然,我需要一些能够继续下载的持久后台进程,并能够回调任意绑定的活动。似乎IntentService几乎符合我的需求,因为它在后台线程中工作并具有服务(非UI)生命周期。但是,是否可以满足我的其他需求?
我注意到我想要做的常见实现从调用者Activity获取Messenger,以便可以将Message对象发送回调用者线程中的Handler。这一切都很好,但在我的情况下,当调用者活动被停止或销毁并且当前活动的活动(附件查看器)显示时会发生什么? 是否有某种方法可以将新的Activity动态绑定到正在运行的IntentService,以便我可以将消息发送回新的Activity?
另一个问题是在Message对象上。我可以在这个包中发回任意大的数据吗?例如,我不需要发回“文件已下载”,而是发送回下载文件本身的字节数组,因为我从不想把它写入磁盘(是的,这是必须的)。
非常感谢任何关于实现我想要的行为的建议。我已经很久没有使用Android了,我经常对如何在Activity生命周期中最好地处理异步流程感到困惑,特别是在方向更改和主页按钮按下时......
答案 0 :(得分:2)
您使用IntentService关闭,执行工作,然后将其发布到某个持久存储中。要与Activity通信,您可以在Activity中使用广播接收器并从IntentService发送广播Intents。
旋转和主页按钮按下对IntentService没有影响,只要它仍在执行其工作,但旋转将执行重新绘制,主页按钮将暂停活动。出于这些原因,您应该避免在AsyncTask中进行异步工作,除非它不必完成并且不必持久化。
消息适用于处理程序,不适用于组件。通过系统传输信息的基本支持是Intents和广播Intents。
IntentService简单地处理将Intent转换为Message,创建一个新的HandlerThread,将Message放入HandlerThread的MessageQueue,然后触发Looper的工作。在后台线程上执行的run()是onHandleIntent()中的任何内容(这有点简化)。
答案 1 :(得分:2)
有很多方法可以解决这个问题。你是对的,我认为你显然需要一个服务来为这个场景分离下载过程与可能来来去去的活动。为了实现这一点,您需要在此服务中实现一些下载队列,并且IntentService提供了开箱即用的这种行为,因此您可以使用它。 IntentService唯一的缺点就是取消之前为其发布的工作并不容易。
我不会对下载的数据进行序列化,但是它听起来效率不高,尤其是在处理大量二进制数据时。您可以将下载的数据写入内存中的LRU缓存,并为其分配一个ID,通过该ID,Viewer Activity可以获取此下载的数据。
您还可以使用广播Intents从服务发出查看者活动信号,表示已完成具有特定ID的下载。这将确保查看活动和下载服务之间的松耦合。
所以它会是这样的:
修改强>
至于缓存,你必须做出一些选择。我认为这本身就是一个相当广泛的主题,所以我将根据我之前的项目在这里写一些想法。
上面,我只提到了内存中的LRU缓存。这将存在于自定义Application对象中,或者作为Singleton。哪个是使用邪恶的全球状态,但我认为在这种情况下这是合理的。优点是您无需在任何地方序列化下载的数据:下载完成后,您将内容存储在内存中,您可以立即从Viewer Activity访问它。缺点是,这些数据只有在没有从此缓存中删除时才可用,或者由于内存条件不足,系统不会终止应用程序的进程。
为避免在丢失此易失性内存缓存的内容时重新下载数据,您需要为其添加持久层。例如。将下载的数据写入application's cache directory。这会带来将下载的数据写入磁盘的开销,但至少您以后可以访问它,即使系统杀死了您的进程。您可以使用2级缓存方法将此持久性缓存与内存缓存相结合,也可以选择其中之一。我建议查看一些广泛使用的图像下载程序库,这些库可以实现此行为,例如: Universal Image Loader