How do i get Inpustream as response in Volley Library
Asked Answered
A

2

12

I am using Volley library in Application.

in onresponse listener i need InputStream as response

how do i get it ?

Annelid answered 12/8, 2014 at 7:17 Comment(7)
what are you getting now?Anisaanise
I have option to get String or JsonObject or Jsonarray but no InputStreamAnnelid
Not sure but I think You may have to create a new InputStreamRequest class.. performRequest() of HurlStack or HttpClientStack return HttpResponse you cane return connection.getInputStream().Gawlas
and why do you need an InputStream?Anisaanise
I am using messagepacks for my servicesAnnelid
in fact, there is a way to achieve that goal, but it's too complicated and inelegant because must copy many code from Volley source, would you like to know how?Chantress
I tried overriding volley but i am getting end of buffer exceptionAnnelid
C
7

well, as i said, it's too complicated, we need to copy great deal code of BasicNetwork, then change some behaviors for handling request, make it different if pass a special Request, use MyNetworkResponse to wrap that response finally fetch the InputStream in the customize NeededInsRequest.

import android.os.SystemClock;
import com.android.volley.*;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.ByteArrayPool;
import com.android.volley.toolbox.HttpStack;
import com.android.volley.toolbox.PoolingByteArrayOutputStream;
import org.apache.http.*;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class MyNetwork extends BasicNetwork {
    private static int SLOW_REQUEST_THRESHOLD_MS = 3000;

    private static int DEFAULT_POOL_SIZE = 4096;

    /**
     * @param httpStack HTTP stack to be used
     */
    public MyNetwork(HttpStack httpStack) {
        // If a pool isn't passed in, then build a small default pool that will give us a lot of
        // benefit and not use too much memory.
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    /**
     * @param httpStack HTTP stack to be used
     * @param pool a buffer pool that improves GC performance in copy operations
     */
    public MyNetwork(HttpStack httpStack, ByteArrayPool pool) {
        super(httpStack, pool);
    }

    @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            InputStream responseIns = null;
            Map<String, String> responseHeaders = new HashMap<String, String>();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,
                            responseHeaders, true);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                    // Note : only particular request needed InputStream.
                    if (request instanceof NeededInsRequest) {
                        responseIns = httpResponse.getEntity().getContent();
                    } else {
                        responseContents = entityToBytes(httpResponse.getEntity());
                    }
                } else {
                    // Add 0 byte response as a way of honestly representing a
                    // no-content request.
                    responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }

                return new MyNetworkResponse(statusCode,
                    responseContents, responseIns, responseHeaders, false);
            } catch (SocketTimeoutException e) {
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException e) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException e) {
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } catch (IOException e) {
                int statusCode;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    throw new NoConnectionError(e);
                }
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (responseContents != null || responseIns != null) {
                    networkResponse = new MyNetworkResponse(statusCode,
                        responseContents, responseIns, responseHeaders, false);

                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

    /**
     * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
     */
    private void logSlowRequests(
            long requestLifetime, Request<?> request, byte[] responseContents, StatusLine statusLine) {
        if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
            VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
                    "[rc=%d], [retryCount=%s]", request, requestLifetime,
                    responseContents != null ? responseContents.length : "null",
                    statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
        }
    }

    /**
     * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
     * request's retry policy, a timeout exception is thrown.
     * @param request The request to use.
     */
    private static void attemptRetryOnException(
            String logPrefix, Request<?> request, VolleyError exception) throws VolleyError {
        RetryPolicy retryPolicy = request.getRetryPolicy();
        int oldTimeout = request.getTimeoutMs();

        try {
            retryPolicy.retry(exception);
        } catch (VolleyError e) {
            request.addMarker(
                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
            throw e;
        }
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }

    private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
        // If there's no cache entry, we're done.
        if (entry == null) {
            return;
        }

        if (entry.etag != null) {
            headers.put("If-None-Match", entry.etag);
        }

        if (entry.serverDate > 0) {
            Date refTime = new Date(entry.serverDate);
            headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
        }
    }

    protected void logError(String what, String url, long start) {
        long now = SystemClock.elapsedRealtime();
        VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
    }

    /** Reads the contents of HttpEntity into a byte[]. */
    private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
        PoolingByteArrayOutputStream bytes =
                new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
        byte[] buffer = null;
        try {
            InputStream in = entity.getContent();
            if (in == null) {
                throw new ServerError();
            }
            buffer = mPool.getBuf(1024);
            int count;
            while ((count = in.read(buffer)) != -1) {
                bytes.write(buffer, 0, count);
            }
            return bytes.toByteArray();
        } finally {
            try {
                // Close the InputStream and release the resources by "consuming the content".
                entity.consumeContent();
            } 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");
            }
            mPool.returnBuf(buffer);
            bytes.close();
        }
    }

    /**
     * Converts Headers[] to Map<String, String>.
     */
    private static Map<String, String> convertHeaders(Header[] headers) {
        Map<String, String> result = new HashMap<String, String>();
        for (Header header : headers) {
            result.put(header.getName(), header.getValue());
        }
        return result;
    }
}

replace BasicNetwork as MyNetwork where you initialize it.

import com.android.volley.NetworkResponse;

import java.io.InputStream;
import java.util.Map;

public class MyNetworkResponse extends NetworkResponse {
    public MyNetworkResponse(int statusCode, byte[] data, InputStream ins,
            Map<String, String> headers, boolean notModified) {
        super(statusCode, data, headers, notModified);
        this.ins = ins;
    }

    public MyNetworkResponse(byte[] data, InputStream ins) {
        super(data);
        this.ins = ins;
    }

    public MyNetworkResponse(byte[] data, InputStream ins, Map<String, String> headers) {
        super(data, headers);
        this.ins = ins;
    }

    public final InputStream ins;
}

extending NetworkResponse, add an InputStream field.

import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;

import java.io.InputStream;

public class NeededInsRequest extends Request<byte[]> {
    private final Response.Listener<byte[]> mListener;

    public NeededInsRequest(int method, String url, Response.Listener<byte[]> listener,
                         Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        // this request would never use cache.
        setShouldCache(false);
        mListener = listener;
    }

    @Override
    protected void deliverResponse(byte[] response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
        if (response instanceof MyNetworkResponse) {
            // take the InputStream here.
            InputStream ins = ((MyNetworkResponse) response).ins;
        }
        return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
    }
}

perform a NeededInsRequest to take the InputStream, then do whatever you want.

I didn't test these code, but i think it can help, if have some mistakes, i'm gladly to know it.

Chantress answered 12/8, 2014 at 9:21 Comment(3)
Thank you . Some how i manage to work using message pack decoding in volley itself and sending string as response Thanks i will give a try and let you knowAnnelid
great stuff, code worked great. if anyone wants me to upload full Volley changes and example usage to GitHub, give me a shoutMarriott
This is amazing. Even the official docs say Volley is not suitable for downloads or large operations. I just wonder how could anyone consider choosing between this and OkHttp, let alone Retrofit.Pittman
M
1

I posted a comment to VinceStyling's answer offering his code changes and example to GitHub, so here it is: https://github.com/georgiecasey/android-volley-inputstream-as-response

The relevant commit with the Volley edits you need is here: https://github.com/georgiecasey/android-volley-inputstream-as-response/commit/62d9b9132c4f66b2268e9099d418b28884c26ce5

I needed InputStream in Volley to download an image from one URL, grab the response as an InputStream and do a multipart upload to another URL. Since then I had to use the competing loopj Android HTTP library and discovered that my usecase was way easier in that library. So I switched to that library for this app. Leave another comment if anybody wants that code.

Marriott answered 27/2, 2015 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.