排球 - 直接下载到文件(没有内存字节数组)

时间:2014-10-23 07:29:52

标签: android memory-management android-volley fileoutputstream

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

我一直在研究凌空的实现,看来凌空的唯一方法就是将整个文件下载到一个潜在的大量字节数组中,然后将这个字节数组的处理推迟到一些回调处理程序。

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

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

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

1 个答案:

答案 0 :(得分:3)

好的,所以我想出了一个涉及编辑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的设计来说是一个严重的缺陷,如果你看到这个设计的缺陷,或者你可以想到更好的设计,我认为这对每个人都有好处。