1

我制作了自定义文档提供程序实现,其中文件实际存储在服务器上。

我遵循了文档,因此提供程序大部分时间都在工作,但我对某些应用程序有问题,即:

1) Gmail 附加来自我的提供商的文件:最初我的公共 ParcelFileDescriptor openDocument 是这样的:

ParcelFileDescriptor[] pipe=null;
pipe=ParcelFileDescriptor.createPipe();
OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);
new TransferThread(file_id,out).start();
return pipe[0];

但这从未与 Gmail 一起使用,我得到了错误的管道损坏。如果我首先以相同的方法(阻塞)下载文件,它会起作用。

原始与大多数其他应用程序一起使用

2) Blackberry Hub - 我无法让它正常工作,因为它似乎在主线程上调用提供程序方法

我会放弃,但我看到 Dropbox 提供商可以与包括 BB 集线器在内的所有应用程序一起使用。

更糟糕的是 :) 例如,Dropbox 似乎可以在下载时显示自己的 UI。

知道如何做到这一点吗?

请注意,我多次检查了文档和几个示例,但仍然无法让提供商使用所有应用程序,我知道某些应用程序可能使用错误,但 Dropbox 证明可以满足所有应用程序

4

1 回答 1

1

您的 Gmail 问题很可能来自 ContentProvider 生命周期。

ContentProvider 实际上只是IPC Binder,与那些非常相似,通常从Service#onBind. 但是客户端每次发出请求时都通过 ContentResolver 间接获取它,而不是绑定到您的应用程序。通常没有显式解除绑定,Android 系统会在每个 IPC 请求完成后将提供程序缓存一小段时间。

不幸的是,隐式 ContentProvider 绑定的隐藏特性意味着无法立即释放提供者。更糟糕的是,没有办法处理有问题的提供程序中的错误——如果您的 ContentProvider 在返回 Cursor 或 ParcelFileDescriptor 之前崩溃,调用应用程序将立即与您一起崩溃!谷歌显然知道这一点,因此他们创建了另一个 API 用于与不信任的第三方 ContentProvider 交互 - ContentProviderClient。请注意, ContentProviderClient 包含处理远程异常和进程死亡的方法以及显式关闭 ContentProvider 的方法

现在想象一下假设的 Gmail ContentProvider 工作流程:

ParcelFileDescriptor fd = null;

try (ContentProviderClient c = resolver.acquireUnstableContentProviderClient(...)) {
    fd = c.openFile(...)
} catch (Exception ohThoseBuggyProviders) {
    ...
}

// here ContentProvider is already closed

if (fd != null) {
    // use the received descriptor to create email attachment
    ...
}

但是,如果您的 ContentProvider 想要在后台线程中从服务器读取其余文件的时间更长一点怎么办?好吧,无论如何,系统很可能会杀死您的进程,因为它不知道您想要那个。您的进程终止,Gmail 收到“管道损坏”错误。

这就是为什么您不应该创建新线程或使用ContentProvider#openPipeHelper(为什么该方法甚至存在?),只需在调用线程中完成所有工作。


您问题第二部分的答案也在于 ContentProvider 内部。当您的提供程序从调用应用程序的主线程调用时,您的代码不会在进程的主线程上执行——它像往常一样在 Binder 线程池中执行。但为了让程序员的生活更轻松,Android 采取了几个步骤来让这一点变得不那么明显:

  1. 您的线程的优先级设置为线程的优先级,这使得调用(包括提高到 UI 优先级,如果您从 UI 线程调用)。
  2. Android当前的严格模式设置(当它们在主线程上联网时使应用程序崩溃的东西)从调用应用程序传递到您的线程。呼叫完成后,将收集所有严格模式违规并将其写入 Parcel发送回呼叫应用程序。

即使 ContentProvider 操作在绑定池中执行,它们的行为也几乎就像进程之间没有界限一样——包括当有人试图从 UI 线程下载文件时发生的坏-坏-坏事情。

您应该能够通过使用android.os.StrictMode摆脱那个令人讨厌的“帮助” ,但是如果有问题的文件太大(ANR 可能仍然在调用过程中发生),那将不会这样做。而不是从 ParcelFileDescriptor 下载文件以通过管道openDocument返回socket

为什么 Dropbox 没有遇到这个问题?因为 Dropbox Core 是用 C++ 编写的,而 Android Strict Mode 目前是仅限 Java 的构造,所以它不会挂钩到本机代码。如果您使用 C 库调用在主线程中写入磁盘或从网络下载,您的应用将不会收到任何影响(除了 ANR,它在独立于严格模式的 UI 线程上触发)。

于 2016-11-23T07:30:14.200 回答