Created
July 8, 2013 09:52
-
-
Save passsy/5947572 to your computer and use it in GitHub Desktop.
BasicNetwork fix for Volley with fallback to Cache if the device is offline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2011 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.android.volley.toolbox; | |
import android.os.SystemClock; | |
import com.android.volley.AuthFailureError; | |
import com.android.volley.Cache; | |
import com.android.volley.Network; | |
import com.android.volley.NetworkError; | |
import com.android.volley.NetworkResponse; | |
import com.android.volley.NoConnectionError; | |
import com.android.volley.Request; | |
import com.android.volley.RetryPolicy; | |
import com.android.volley.ServerError; | |
import com.android.volley.TimeoutError; | |
import com.android.volley.VolleyError; | |
import com.android.volley.VolleyLog; | |
import org.apache.http.Header; | |
import org.apache.http.HttpEntity; | |
import org.apache.http.HttpResponse; | |
import org.apache.http.HttpStatus; | |
import org.apache.http.StatusLine; | |
import org.apache.http.conn.ConnectTimeoutException; | |
import org.apache.http.impl.cookie.DateUtils; | |
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; | |
/** | |
* A network performing Volley requests over an {@link HttpStack}. | |
*/ | |
public class BasicNetwork implements Network { | |
protected static final boolean DEBUG = VolleyLog.DEBUG; | |
private static int SLOW_REQUEST_THRESHOLD_MS = 3000; | |
private static int DEFAULT_POOL_SIZE = 4096; | |
protected final HttpStack mHttpStack; | |
protected final ByteArrayPool mPool; | |
/** | |
* @param httpStack HTTP stack to be used | |
*/ | |
public BasicNetwork(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 BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { | |
mHttpStack = httpStack; | |
mPool = pool; | |
} | |
@Override | |
public NetworkResponse performRequest(Request<?> request) throws VolleyError { | |
long requestStart = SystemClock.elapsedRealtime(); | |
while (true) { | |
HttpResponse httpResponse = null; | |
byte[] responseContents = 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().data, responseHeaders, true); | |
} | |
responseContents = entityToBytes(httpResponse.getEntity()); | |
// if the request is slow, log it. | |
long requestLifetime = SystemClock.elapsedRealtime() - requestStart; | |
logSlowRequests(requestLifetime, request, responseContents, statusLine); | |
if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) { | |
throw new IOException(); | |
} | |
return new NetworkResponse(statusCode, responseContents, 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 = 0; | |
NetworkResponse networkResponse = null; | |
if (httpResponse != null) { | |
statusCode = httpResponse.getStatusLine().getStatusCode(); | |
} else { | |
if(request.getCacheEntry() != null && request.getCacheEntry().data != null){ | |
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, | |
request.getCacheEntry().data, responseHeaders, true); | |
}else{ | |
throw new NoConnectionError(e); | |
} | |
} | |
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); | |
if (responseContents != null) { | |
networkResponse = new NetworkResponse(statusCode, responseContents, | |
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 (int i = 0; i < headers.length; i++) { | |
result.put(headers[i].getName(), headers[i].getValue()); | |
} | |
return result; | |
} | |
} |
Hasn't Volley been deprecated like 6 years ago? Switch to okhttp 👍
Thank you! Would I need to modify somecode or start over afresh? I
apologise for the naive question,I am new to android app development.
…On Thu, Dec 17, 2020 at 5:00 AM Pascal Welsch ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
Hasn't Volley been deprecated like 6 years ago? Switch to okhttp 👍
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<https://gist.github.com/5947572#gistcomment-3564226>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AOZRF5ZFWO7TBQA6LMGFJBTSVF65NANCNFSM4U66WNJA>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Where does this go? I am using Volley to upload an image and it is slow for files that are just a few kilobytes. Thanks!