如何在同一设备上运行的Android应用程序之间传输文件?

25
我正在编写一个与RESTful服务交互的Android应用程序。该Web服务基本上是文件系统的前端,并提供元数据以及对文件的CRUD访问。我的应用程序检索元数据,并通过ContentProvider将其公开给第三方应用程序。
我需要添加功能,使运行在与我的应用程序相同的设备上的第三方应用程序能够通过与我的应用程序进行请求/响应(而不是直接与服务器)来CRUD实际文件。这意味着他们需要通过我的应用程序发送或接收文件的内容(通常为XML或图像)。
我考虑了两种实现方法:
选项1-使用ContentProvider.openFile 这似乎是为第三方应用程序提供从我的ContentProvider读取文件的明显选择。但当这些应用程序需要通过我的ContentProvider创建或更新文件时,情况开始变得棘手。我需要一个回调来知道何时将新文件/更改的文件发送回服务器。我认为我可以使用FileObserver来实现这个目的。
选项2-通过服务使用Messenger 使用这种方法,我可以通过Messenger在我的应用程序和客户端应用程序之间发送文件。文件必须通过一个Bundle传递,所以我不确定传输它们的最佳格式是什么(FileFileDescriptor,字节数组,还是其他?)。如果文件变得很大,我不确定是否会出现问题。
选项3-混合方法
  1. 使用外部存储上的文件夹作为投放箱
  2. 通过Messenger/Service通信CRUD请求和投放箱内容
  3. 使用ContentProvider存储请求状态
  4. 第三方应用程序通过ContentObserver接收状态更新
总结
我认为使用ContentProvider将是理想的解决方案,但似乎API并不完全支持我的用例。我担心尝试走这条路可能会导致笨拙的实现。如果我选择MessengerService方法,我不确定通过Bundle传输文件的最强大的方式是什么。
混合方法似乎非常强大,但实现起来最复杂。文件实际上并没有被传递,因此性能应该很好。然而,我担心这是过度设计解决方案。

如何在同一台Android设备上运行的应用程序之间传输文件是最佳方法? 当然,我也可以接受我没有在问题中概述的其他选项。


4
我会跟 Android 一样做,将文件存储在sd卡上,并通过ContentProvider提供对文件URI而不是文件内容的访问。 - mibollma
@mibollma - 我喜欢那个想法,但我不确定它能解决整个问题 - Eric Levine
1
我建议您先仔细查看文档和源代码,以确定该概念是否符合您的需求。至少听起来有些相似。这是源代码:http://android.git.kernel.org/?p=platform/packages/providers/MediaProvider.git;a=blob;f=src/com/android/providers/media/MediaProvider.java;hb=HEAD - mibollma
1
@elevine 嵌入一个小型的HTTP服务器,供第三方应用程序进行通信,所有请求都通过本地主机进行。虽然我以前没有尝试过这种方法,但它似乎是一种简单的解决方案。 - dbryson
@RocketSurgeon,你完全错了。Android平台提供了多种机制来在应用程序之间共享文件,无需进行任何黑客攻击! - Eric Levine
显示剩余4条评论
3个回答

16

内容提供者绝对是最佳选择。如果您考虑到谷歌几乎在所有方面都使用此方法,那么这就变得明显了,因为这是旨在设计的方法。

我并不是在赞扬它们的优点,但在盲人国度中,有一只有眼的内容提供者就是国王。

更新

CommonsWare书中有一个如何实现内容提供者的例子,请参见提供的链接。

Content Provider / Files的来源

对于内容提供者,请使用同步框架。只需维护请求列表,然后安排同步以下载这些文件。您还可以在网络tickles等情况下执行此操作。您可以使用广播意图或内容观察器通知客户端已下载该文件。

本质上,这可能类似于您的第三个选项,但重要的是它使用Android提供的工具而不是自己编写的工具。

补充说明

最好从android-sdk\samples\android-8\SampleSyncAdapter的android SDK示例开始,但请注意,其中有大量与联系人相关的内容,掩盖了精华部分。我花了一些时间才弄清楚我几乎可以删除所有内容,除了同步适配器。


我认为ContenProvider API在我的使用情况下缺少一些关键功能。我需要允许第三方应用程序请求下载文件,然后在准备好时得到通知。我发现使用ContentProvider和ContentObserver实现这一点很笨拙。此外,我的ContentObservers并没有可靠地触发它们的onChange事件。转换到基于Service/Messenger的解决方案表现得更好。我还没有实现第三方应用程序请求上传文件的情况,但这似乎不适合ContentObserver。 - Eric Levine
请按照我的更新中所述使用同步。Android SDK 中有同步的示例。 - Moog
我认为你是对的,同步适配器方法似乎是一种改进。你有其他好的资源指导学习吗?我一直在基于Google I/O 2010 Android REST演讲的前两个模式尝试。克服同步适配器的学习曲线并实现第三个模式可能是值得的:http://www.google.com/events/io/2010/sessions/developing-RESTful-android-apps.html - Eric Levine
我不得不经历同样的学习曲线,感到有些困难。 我已经更新了SDK示例。我使用的唯一其他资源是did you win yet教程。 - Moog

3

http://developer.android.com/reference/android/os/ParcelFileDescriptor.html可以在进程之间传递。我认为这里有一个微妙之处,它们明确被列入黑名单,不能允许放置在意图中。但是它们可以通过AIDL发送。

此外,不要使用SD卡。这只是在招惹麻烦。一张SD卡是全球可读的,所以任何人都可以看到。而且,并不总是可以访问SD卡(它被移除或放在UMS中)。


2
使用SD卡肯定是在安卓上分享文件的 推荐 方式。
然而,我会使用修改后的混合解决方案,它利用客户端的 startActivityForResult()onActivityResult()(文档 这里),与服务交换CRUD请求(如果需要,则获取SD卡上文件的Uri), 只要您不介意创建一个虚拟活动作为前端。 客户端完成文件操作后,可以再次调用startActivityForResult()来通知您的应用程序进行更改。
当然,也可以使用startService()/bindService()实现此目的,但是如果需要IPC,它无法提供客户端轻松获得状态结果的方法。
尽管内容提供程序/解析器似乎是解决问题的正确方式,但我认为它更适用于特定于提供/消耗内容的单向请求。

我认为startActivityForResult和onActivityResult并没有完全达到预期效果。当一个应用程序想要与我的应用程序共享文件时,它不会启动我的应用程序的任何活动。此外,我发现使用Messenger,可以通过IPC相对容易地进行双向通信。 - Eric Levine
我再次研究IPC与服务,感觉还不错。这取决于客户端是否愿意实现服务绑定回调并使用消息,但我可以理解为什么startActvityForResult和onActivityResult对您无效。如果有一个startServiceForResult就好了。 - Jasoneer

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