【问题标题】:Custom ContentProvider - openInputStream(), openOutputStream()自定义 ContentProvider - openInputStream()、openOutputStream()
【发布时间】:2010-01-27 15:51:37
【问题描述】:

内容提供者/解析器 API 提供了一种复杂但稳健的方式,使用 URI 和 openInputStream()openOutputStream() 方法在进程之间传输数据。自定义内容提供者能够使用自定义代码覆盖openFile() 方法,以有效地将URI 解析为Stream;但是,openFile() 的方法签名具有 ParcelFileDescriptor 返回类型,并且不清楚如何为动态生成的内容生成正确的表示以从该方法返回。

Returning a memory mapped InputStream from a content provider?

现有代码库中是否有针对动态内容实现ContentProvider.openFile() 方法的示例?如果没有,您能建议这样做的源代码或流程吗?

【问题讨论】:

    标签: android file-io android-contentprovider


    【解决方案1】:

    从总是很有帮助的 CommonsWare 中查看这个很棒的示例项目。它允许您创建一个 ParcelFileDescriptor 管道,其中一侧带有您想要的任何 InputStream,另一侧带有接收应用程序:

    https://github.com/commonsguy/cw-omnibus/tree/master/ContentProvider/Pipe

    关键部分是在openFile中创建管道:

    public ParcelFileDescriptor openFile(Uri uri, String mode)
                                                            throws FileNotFoundException {
        ParcelFileDescriptor[] pipe=null;
    
        try {
          pipe=ParcelFileDescriptor.createPipe();
          AssetManager assets=getContext().getResources().getAssets();
    
          new TransferThread(assets.open(uri.getLastPathSegment()),
                           new AutoCloseOutputStream(pipe[1])).start();
        }
        catch (IOException e) {
          Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
          throw new FileNotFoundException("Could not open pipe for: "
              + uri.toString());
        }
    
        return(pipe[0]);
      }
    

    然后创建一个保持管道满的线程:

    static class TransferThread extends Thread {
        InputStream in;
        OutputStream out;
    
        TransferThread(InputStream in, OutputStream out) {
            this.in = in;
            this.out = out;
        }
    
        @Override
        public void run() {
            byte[] buf = new byte[8192];
            int len;
    
            try {
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
    
                in.close();
                out.flush();
                out.close();
            } catch (IOException e) {
                Log.e(getClass().getSimpleName(),
                        "Exception transferring file", e);
            }
        }
    }
    

    【讨论】:

    • 我使用第三方库,它通过内容提供者请求文件,并且由于某种原因,在以这种方式返回 ParcelFileDescriptor 或当使用像这样的真实文件时: ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY)
    • 知道为什么在使用此提供程序处理大量请求时有时会出错:java.io.IOException: write failed: EPIPE (Broken pipe) at libcore.io.IoBridge.write(IoBridge .java:502) 在 java.io.FileOutputStream.write(FileOutputStream.java:186)
    • 我遇到了和 Malachiasz 一样的问题。有人在这方面有什么进展吗?
    • 也看到这个错误,3年后你找到解决方法了吗?
    • 确实有效,但比在文件系统上缓存文件要慢
    【解决方案2】:

    MemoryFile 支持这一点,但公共 API 尚未最终确定。

    【讨论】:

    • 是否有计划在未来包含内存文件和包裹文件描述符之间的转换?沿着这些思路的东西会比使用具有未知生命周期的临时文件弄乱/污染文件系统更好。也许有一些方法可以检测内容提供者中流的关闭,这可以提供一种更安全的方式来清理自己?我担心将附件发送到(gmail/standaed)电子邮件客户端,但我确信在其他地方可能会出现这些问题。
    • 是的,MemoryFile.java 目前有一个public ParcelFileDescriptor getParcelFileDescriptor() 方法。这是作为 Donut 的一部分提交的,但正如 Jeff 所说,尚未最终确定。我已经确认“概念”至少有效,并且目前可以使用反射来完成。不过它很脏,不推荐使用:) 不幸的是,即使ParcelFileDescriptor.fromSocket() 也无法使用,因为Memory.isMemoryFile() 会抛出异常,因为套接字既不是PFD 也不是内存文件。
    • 小心 MemoryFile。如果我理解正确,它将文件的全部内容存储在内存中,因此您不能使用大于可用内存的文件。
    猜你喜欢
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多