【问题标题】:Volley - download directly to file (no in memory byte array)Volley - 直接下载到文件(内存字节数组中没有)
【发布时间】:2014-12-18 19:58:02
【问题描述】:

我正在使用 Volley 作为我在 Android 中工作的项目中的网络堆栈。我的部分要求是下载可能非常大的文件并将它们保存在文件系统上。

我一直在研究 volley 的实现,似乎 volley 工作的唯一方法是将整个文件下载到一个可能很大的字节数组中,然后将该字节数组的处理推迟到某个回调处理程序。

由于这些文件可能非常大,我担心在下载过程中会出现内存不足错误。

有没有办法告诉 volley 将来自 http 输入流的所有字节直接处理到文件输出流中?或者这是否需要我实现自己的网络对象?

我在网上找不到任何有关这方面的资料,因此我们将不胜感激。

【问题讨论】:

    标签: android memory-management android-volley fileoutputstream


    【解决方案1】:

    好的,所以我想出了一个涉及编辑 Volley 本身的解决方案。这是一个演练:

    网络响应不能再保存字节数组了。它需要保存一个输入流。这样做会立即破坏所有请求实现,因为它们依赖于持有公共字节数组成员的 NetworkResponse。我发现解决此问题的侵入性最小的方法是在 NetworkResponse 中添加一个“toByteArray”方法,然后进行一些重构,使对字节数组的任何引用都使用此方法,而不是删除的字节数组成员。这意味着输入流到字节数组的转换发生在响应解析期间。我不完全确定这样做的长期影响是什么,因此一些单元测试/社区输入将在这里提供巨大帮助。代码如下:

    public class NetworkResponse {
        /**
         * Creates a new network response.
         * @param statusCode the HTTP status code
         * @param data Response body
         * @param headers Headers returned with this response, or null for none
         * @param notModified True if the server returned a 304 and the data was already in cache
         */
        public NetworkResponse(int statusCode, inputStream data, Map<String, String> headers,
                boolean notModified, ByteArrayPool byteArrayPool, int contentLength) {
            this.statusCode = statusCode;
            this.data = data;
            this.headers = headers;
            this.notModified = notModified;
            this.byteArrayPool = byteArrayPool;
            this.contentLength = contentLength;
        }
    
        public NetworkResponse(byte[] data) {
            this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
        }
    
        public NetworkResponse(byte[] data, Map<String, String> headers) {
            this(HttpStatus.SC_OK, data, headers, false);
        }
    
        /** The HTTP status code. */
        public final int statusCode;
    
        /** Raw data from this response. */
        public final InputStream inputStream;
    
        /** Response headers. */
        public final Map<String, String> headers;
    
        /** True if the server returned a 304 (Not Modified). */
        public final boolean notModified;
    
        public final ByteArrayPool byteArrayPool;
        public final int contentLength;
    
        // method taken from BasicNetwork with a few small alterations.
        public byte[] toByteArray() throws IOException, ServerError {
            PoolingByteArrayOutputStream bytes =
                    new PoolingByteArrayOutputStream(byteArrayPool, contentLength);
            byte[] buffer = null;
            try {
    
                if (inputStream == null) {
                    throw new ServerError();
                }
                buffer = byteArrayPool.getBuf(1024);
                int count;
                while ((count = inputStream.read(buffer)) != -1) {
                    bytes.write(buffer, 0, count);
                }
                return bytes.toByteArray();
            } finally {
                try {
                    // Close the InputStream and release the resources by "consuming the content".
                    // Not sure what to do about the entity "consumeContent()"... ideas?
                    inputStream.close();
                } catch (IOException e) {
                    // This can happen if there was an exception above that left the entity in
                    // an invalid state.
                    VolleyLog.v("Error occured when calling consumingContent");
                }
                byteArrayPool.returnBuf(buffer);
                bytes.close();
            }
        }
    
    }
    

    然后要准备 NetworkResponse,我们需要编辑 BasicNetwork 以正确创建 NetworkResponse(在 BasicNetwork.performRequest 内):

    int contentLength = 0;
    if (httpResponse.getEntity() != null)
    {
         responseContents = httpResponse.getEntity().getContent(); // responseContents is now an InputStream
         contentLength = httpResponse.getEntity().getContentLength();
    }
    
    ...
    
    return new NetworkResponse(statusCode, responseContents, responseHeaders, false, mPool, contentLength);
    

    就是这样。一旦网络响应中的数据成为输入流,我就可以构建自己的请求,将其直接解析为文件输出流,该输出流仅包含一个小的内存缓冲区。

    从一些初步测试来看,这似乎工作正常而不会损害其他组件,但是像这样的更改可能需要一些更密集的测试和同行评审,所以我会留下这个答案不标记为正确,直到更多人们参与进来,或者我认为它足够可靠。

    请随时对此答案发表评论和/或自己发布答案。这感觉像是 Volley 设计中的一个严重缺陷,如果你看到这个设计的缺陷,或者自己能想出更好的设计,我认为它会对每个人都有好处。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-24
      • 2022-12-15
      相关资源
      最近更新 更多