在Android上为云实施DocumentsProvider的问题

时间:2016-11-22 07:28:49

标签: android android-contentprovider storage-access-framework

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

我按照文档进行操作,因此提供程序大部分时间都在工作,但我遇到某些应用程序的问题,即:

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一起工作我得到错误管道。 如果我首先使用相同的方法下载文件(阻止),它就可以工作。

Original适用于大多数其他应用程序

2)Blackberry Hub - 我无论如何都无法工作,因为它似乎是在主线程上调用提供者方法

我会放弃,但我看到Dropbox提供商使用了所有应用程序,包括BB hub。

更糟糕的是,似乎dropbox可以在下载时显示自己的UI。

知道如何做到这一点?

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

1 个答案:

答案 0 :(得分:0)

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

ContentProvider实际上只是IPC Binder,非常类似于Service#onBind通常返回的内容。但是,不是绑定到您的应用程序,客户端每次发出请求时都会通过ContentResolver间接获取它。通常没有明确的解除绑定,Android系统会在每个IPC请求完成后短时间内缓存提供程序。

不幸的是,隐式ContentProvider绑定的隐藏性质意味着无法立即释放提供者。更糟糕的是,没有办法处理错误提供程序中的错误 - 如果你的ContentProvider在返回Cursor或ParcelFileDescriptor之前崩溃,那么调用应用程序会立即与你同行! Google明显知道这一点,所以他们创建了另一个用于与错误信任的第三方ContentProviders进行交互的API - 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获得了#34;破坏了管道#34;错误。

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

问题第二部分的答案也在于ContentProvider内部。当从调用应用程序的主线程调用您的提供程序时,您的代码不会在您的进程的主线程上执行 - 它会像往常一样在Binder线程池中执行。但是要让程序员和#39;生活更轻松,Android需要采取几个步骤来降低这一点:

  1. 您的线程的优先级设置为线程的优先级,进行调用(如果从UI线程调用,则包括提升到UI优先级。)
  2. Android passes当前严格模式设置(在主线程上进行网络连接时使应用程序崩溃的内容)从调用应用程序到您的线程。通话结束后,将收集所有严格模式违规并将written to Parcel发送回主叫应用。
  3. 即使ContentProvider操作在绑定池中执行,它们的行为几乎就像进程之间没有边界一样 - 包括当有人试图从UI线程下载文件时发生的坏 - 坏 - 坏事。

    你应该能够摆脱那些令人讨厌的"帮助"如果相关文件太大(在调用过程中仍可能发生ANR),则使用android.os.StrictMode,但不会这样做。而不是将文件下载到管道,而是从openDocument ParcelFileDescriptor for socket返回。

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