I have added volley for request to get Json object, If wifi is turned on then it takes data but does not get in case of offline mode even cache is enabled for the request.
I do the following code
public class VolleySingleton extends Application
{
public static final String TAG = VolleySingleton.class.getSimpleName();
private RequestQueue mRequestQueue;
private static VolleySingleton mInstance;
private ImageLoader mImageLoader;
private final String DEFAULT_CACHE_DIR = "sl_cache";
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
#Override
public void onCreate()
{
super.onCreate();
mInstance = this;
}
public static synchronized VolleySingleton getInstance()
{
return mInstance;
}
public ImageLoader getImageLoader()
{
getRequestQueue();
if (mImageLoader == null)
{
mImageLoader = new ImageLoader(this.mRequestQueue, new LruBitmapCache());
}
return this.mImageLoader;
}
public <T> void addToRequestQueue(Request<T> req, String tag)
{
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req)
{
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag)
{
if (mRequestQueue != null)
{
mRequestQueue.cancelAll(tag);
}
}
public RequestQueue getRequestQueue()
{
if (mRequestQueue == null)
{
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024 * 10); // 10MB cap
Network network = new BasicNetwork(new HurlStack());
mRequestQueue = new RequestQueue(cache, network);
mRequestQueue.start();
}
return mRequestQueue;
}
}
private void getData(String url, String tag)
{
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>()
{
#Override
public void onResponse(JSONObject response)
{
Log.wtf("HOME", response.toString());
String result = parseData(response.toString());
postProcessing(result);
//SocialLadder.getInstance().getRequestQueue().getCache().invalidate(url, true);
}
}, new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error)
{
VolleyLog.wtf("HOME", "Error: " + error.getMessage());
stopRefresher();
}
})
{
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response)
{
try
{
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return !jsonString.isEmpty() ? Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)) : Response.success(new JSONObject(), HttpHeaderParser.parseCacheHeaders(response));
}
catch (JSONException ex)
{
ex.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return null;
}
};
jsonObjReq.setShouldCache(true);
VolleySingleton.getInstance().addToRequestQueue(jsonObjReq, tag);
}
Please help, I want to cache my screen data.
Edit
Cache Data
private String getCache(String url)
{
String data = "";
Cache cache = VolleySingleton.getInstance().getRequestQueue().getCache();
Cache.Entry entry = cache.get(url);
if (entry != null)
{
try
{
data = new String(entry.data, "UTF-8");
// handle data, like converting it to xml, json, bitmap etc.,
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
/*else
{
// Cached response doesn't exists. Make network call here
}*/
return data;
}
Just add this line in **BasicNetwork* class or modify it as follow
#Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
if(!ConnectivityUtils.isNetworkEnabled(BBApplication.getContext())) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
}
// 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) {
Cache.Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
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 NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (NoHttpResponseException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (UnknownHostException e) {
attemptRetryOnException("socket", 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 {
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, SystemClock.elapsedRealtime() - requestStart);
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);
}
}
}
}
and for data request expiry you can change the Cached.Entry using using own HttpHeaderParser
Click for BasicNetwork
What is this code will do it will check for internet connection and Network call and revert if it has cached copy .
Note The API response should be cache-able because Volley only cache data if Response Header permits . See here for Cached Control Header
In order to cache anything with Volley you need to have two things:
1) server allows you to cache it. it usually appears in a cache control tag in the HTTP header.
2) you save it or in this scenario you tell the Volly to save.
so i think your problem is in number one. that means the server dose not allow you to cache those files, in order to confirm my answer you can do one of these things:
download this plug in (RESTClient) for mozilla and send your request and check the header file for cache control. if the server dose not allow you to cache you will see something like below image, notice cache control tag
set break point in headerValue = headers.get("Cache-Control"); at HttpHeaderParse class and see whats going on when Volley wants to parse the cache control tag.
Related
This question already has answers here:
Sending POST data in Android
(17 answers)
Closed 5 years ago.
I'm new to android and learning it now. I'm working on an application where I was able to get response form a get request successfully. Now I want to execute a POST request with a single parameter this is what I've came up with so far:
public class BGTask extends AsyncTask<String, String, String > {
#Override
protected String doInBackground(String... params) {
HttpURLConnection connection = null;
BufferedReader reader = null;
String color = "null";
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
//I want to pass the id parameter
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line ="";
while ((line = reader.readLine()) != null){
buffer.append(line);
}
String finalJson = buffer.toString();
JSONObject parentObject = new JSONObject(finalJson);
int status = parentObject.getInt("status");
if(status == 1) {
color = parentObject.getString("bgcolor");
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.disconnect();
}
try {
if(reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return color;
}
}
The variable color is in hex code i.e #FF0089
** Use volley library for GET , POST and network calls it's very simple and efficient **
First create AppController.java
public class AppController extends Application {
public static final String TAG = AppController.class.getSimpleName();
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static AppController mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized AppController getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
public ImageLoader getImageLoader() {
getRequestQueue();
if (mImageLoader == null) {
mImageLoader = new ImageLoader(this.mRequestQueue,
new LruBitmapCache());
}
return this.mImageLoader;
}
public void setConnectivityListener(ConnectivityReceiver.ConnectivityReceiverListener listener) {
ConnectivityReceiver.connectivityReceiverListener = listener;
}
}
Second make POST request
public void Send() {
final String First = "";
final String Second = "";
StringRequest stringRequest = new StringRequest(Request.Method.POST, CREATEORDER_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.e("RESPONSE", response);
JSONObject jObj = null;
try {
jObj = new JSONObject(response);
String success = jObj.getString("success");
String errorMessage = jObj.getString("message");
if (success.equals("")) {
Intent intent = new Intent(SendGiftActivity.this, PayScreenActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(SendGiftActivity.this, errorMessage, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
// Toast.makeText(SignUpActivity.this, response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(SendGiftActivity.this, error.toString(), Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put(KEY_FIRST_PARAMETERS, First);
params.put(KEY_SECOND_PARAMETER, Second);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
Include volley library
compile 'com.android.volley:volley:1.0.0'
Don't forget to add android:name=".AppController" in application tag of manifest file
In my application I implemented Retrofit to call WebServices and I'm using OkHttp to use Interceptor and Authenticator. Some requests need token, and I have implemented Authenticator interface to handle the refresh (following the official documentation). But I have the following issue : time to time in my app I have to call more than one request at once. Because of that, for one of them I will have the 401 error.
Here is my code for request calls :
public static <S> S createServiceAuthentication(Class<S> serviceClass, boolean hasPagination) {
final String jwt = JWT.getJWTValue(); //Get jwt value from Realm
if (hasPagination) {
Gson gson = new GsonBuilder().
registerTypeAdapter(Pagination.class, new PaginationTypeAdapter()).create();
builder =
new Retrofit.Builder()
.baseUrl(APIConstant.API_URL)
.addConverterFactory(GsonConverterFactory.create(gson));
}
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new AuthenticationInterceptor(jwt));
httpClient.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= 2) {
// If both the original call and the call with refreshed token failed,
// it will probably keep failing, so don't try again.
return null;
}
if (jwt.equals(response.request().header("Authorization"))) {
return null; // If we already failed with these credentials, don't retry.
}
APIRest apiRest = createService(APIRest.class, false);
Call<JWTResponse> call = apiRest.refreshToken(new JWTBody(jwt));
try {
retrofit2.Response<JWTResponse> refreshTokenResponse = call.execute();
if (refreshTokenResponse.isSuccessful()) {
JWT.storeJwt(refreshTokenResponse.body().getJwt());
return response.request().newBuilder()
.header(CONTENT_TYPE, APPLICATION_JSON)
.header(ACCEPT, APPLICATION)
.header(AUTHORIZATION, "Bearer " + refreshTokenResponse.body().getJwt())
.build();
} else {
return null;
}
} catch (IOException e) {
return null;
}
}
});
builder.client(httpClient.build());
retrofit = builder.build();
return retrofit.create(serviceClass);
}
private static int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
The issue is simple, the first request will refresh the token successfully but others will failed because they will try to refresh a token already refreshed. The WebService return an error 500. Is there any elegant solution to avoid this ?
Thank you !
If I understand your issue, some requests are sent while the token is being updated, this gives you an error.
You could try to prevent all the requests while a token is being updated (with a 'synchronized' object) but this will not cover the case of an already sent request.
Since the issue is difficult to avoid completely, maybe the right approach here is to have a good fallback behavior. Handling the error you get when you've made a request during a token update by re-running the request with the updated token for instance.
Write Service.
public class TokenService extends Service {
private static final String TAG = "HelloService";
private boolean isRunning = false;
OkHttpClient client;
JSONObject jsonObject;
public static String URL_LOGIN = "http://server.yoursite";
String phone_number, password;
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
jsonObject = new JSONObject();
client = new OkHttpClient();
SharedPreferences pref_phone = getSharedPreferences("Full_Phone", MODE_PRIVATE);
phone_number = pref_phone.getString("Phone", "");
SharedPreferences pref_password = getSharedPreferences("User_Password", MODE_PRIVATE);
password = pref_password.getString("Password", "");
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
try {
jsonObject.put("phone_number", phone_number);
jsonObject.put("password", password);
} catch (JSONException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
#Override
public void run() {
for (; ; ) {
try {
Thread.sleep(1000 * 60 * 2);
} catch (Exception e) {
}
if (isRunning) {
AsyncTaskRunner myTask = new AsyncTaskRunner();
myTask.execute();
} else {
Log.d("CHECK__", "Check internet connection");
}
}
}
}).start();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onDestroy() {
isRunning = false;
Log.i(TAG, "Service onDestroy");
}
String post(String url, JSONObject login) {
try {
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, login.toString());
okhttp3.Request request = new okhttp3.Request.Builder()
.url(url)
.post(body)
.build();
okhttp3.Response response = client.newCall(request).execute();
try {
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
String response;
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... params) {
try {
response = post(
URL_LOGIN, jsonObject);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.d("---OKHTTP---", response);
}
}
}
I have used the volley library to get the API response. Initially when am calling the API the error response is showing exactly from the server. but when i again call the API means i am getting error response is null.
Kindly help me to do this.
Here is my code
public void requestString(final String requestName,
final String webserviceUrl,
final Map<Object, Object> requestParams, final int webMethod,
final boolean getCache) {
LogUtils.i("Sending Request", webserviceUrl);
StringRequest stringRequest = new StringRequest(webMethod,
webserviceUrl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
LogUtils.i("Response", response);
mRequestCompletedListener.onRequestCompleted(
requestName, true, response, null);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
String errorResponse = null;
if (getCache) {
final Cache cache = AppController.getInstance()
.getRequestQueue().getCache();
final Entry entry = cache.get(webserviceUrl);
if (entry.data != null) {
try {
errorResponse = new String(entry.data, "UTF-8");
mRequestCompletedListener
.onRequestCompleted(requestName,
true, errorResponse, null);
return;
} catch (UnsupportedEncodingException e) {
LogUtils.e(TAG, e);
}
} else {
LogUtils.e(TAG, requestName
+ " Cache does not exist");
}
}
try {
VolleyError responseError = new VolleyError(
new String(error.networkResponse.data));
LogUtils.i("ErrorResponse", responseError.getMessage());
try {
if (responseError != null) {
final JSONObject responseJson = new JSONObject(responseError.getMessage());
// Show Alert Information
errorResponse = responseJson.getString(AppConstants.MESSAGE);
}
} catch (Exception e) {
errorResponse = "Unknown";
}
} catch (Exception e) {
LogUtils.e(TAG, e);
}
mRequestCompletedListener.onRequestCompleted(
requestName, false, null,
errorResponse);
}
})
stringRequest.setTag(requestName);
// Adding String request to request queue
AppController.getInstance().addToRequestQueue(stringRequest);
}
Here am passing the Params:
AppController.getInstance().requestApi(mVolleyRequest, REQUEST_NAME, PATH
+ API, Request.Method.POST, HTTP_POST, Request.Priority.HIGH
, false, out.toString().trim(), true);
I am using Volley library in Application.
in onresponse listener i need InputStream as response
how do i get it ?
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.
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.
I am using Google Volley on the Android platform.
I am having a problem in which the error parameter in onErrorResponse is returning a null networkResponse
For the RESTful API I am using, I need to determine the Http Status Code which is often arriving as 401 (SC_UNAUTHORIZED) or 500 (SC_INTERNAL_SERVER_ERROR), and I can occasionally check via:
final int httpStatusCode = error.networkResponse.statusCode;
if(networkResponse == HttpStatus.SC_UNAUTHORIZED) {
// Http status code 401: Unauthorized.
}
This throws a NullPointerException because networkResponse is null.
How can I determine the Http Status Code in the function onErrorResponse?
Or, how can I ensure error.networkResponse is non-null in onErrorResponse?
Or, how can I ensure error.networkResponse is non-null in
onErrorResponse?
My first thought would be to check if the object is null.
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null && networkResponse.statusCode == HttpStatus.SC_UNAUTHORIZED) {
// HTTP Status Code: 401 Unauthorized
}
}
Alternatively, you could also try grabbing the Status Code by extending the Request class and overriding parseNetworkResponse.
For example, if extending the abstract Request<T> class
public class GsonRequest<T> extends Request<T> {
...
private int mStatusCode;
public int getStatusCode() {
return mStatusCode;
}
...
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
mStatusCode = response.statusCode;
try {
Log.d(TAG, "[raw json]: " + (new String(response.data)));
Gson gson = new Gson();
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(gson.fromJson(json, mClazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
...
}
Or, if you are using one of the toolbox classes that already extend the abstract Request<T> class and you don't want to muddle up the implementation for parseNetworkResponse(NetworkResponse networkResponse), continue overriding the method but return the super's implementation via super.parseNetworkResponse(networkResponse)
e.g. StringResponse
public class MyStringRequest extends StringRequest {
private int mStatusCode;
public MyStringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
public int getStatusCode() {
return mStatusCode;
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
mStatusCode = response.statusCode;
return super.parseNetworkResponse(response);
}
}
usage:
public class myClazz extends FragmentActivity {
private Request mMyRequest;
...
public void makeNetworkCall() {
mMyRequest = new MyNetworkRequest(
Method.GET,
BASE_URL + Endpoint.USER,
new Listener<String>() {
#Override
public void onResponse(String response) {
// Success
}
},
new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (mMyRequest.getStatusCode() == 401) {
// HTTP Status Code: 401 Unauthorized
}
}
});
MyVolley.getRequestQueue().add(request);
}
Of course, the option to override the method inline is available too
public class MyClazz extends FragmentActivity {
private int mStatusCode;
...
public void makeNetworkCall() {
StringRequest request = new StringRequest(
Method.GET,
BASE_URL + Endpoint.USER,
new Listener<String>() {
#Override
public void onResponse(String response) {
// Success
}
},
new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (mStatusCode == 401) {
// HTTP Status Code: 401 Unauthorized
}
}
}) {
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
mStatusCode = response.statusCode;
return super.parseNetworkResponse(response);
}
};
MyVolley.getRequestQueue.add(request);
}
Update:
HttpStatus is Deprecated. Use HttpURLConnection instead. See Link.
401 Not Supported by Volley
It turns out that it is impossible to guarantee that error.networkResponse is non-null without modifying Google Volley code because of a bug in Volley that throws the Exception NoConnectionError for Http Status Code 401 (HttpStatus.SC_UNAUTHORIZED) in BasicNetwork.java (134) prior to setting the value of networkResponse.
Work-Around
Instead of fixing the Volley code, our solution in this case was to modify the Web Service API to send Http Error Code 403 (HttpStatus.SC_FORBIDDEN) for the particular case in question.
For this Http Status Code, the value of error.networkResponse is non-null in the Volley error handler: public void onErrorResponse(VolleyError error). And, error.networkResponse.httpStatusCode correctly returns HttpStatus.SC_FORBIDDEN.
Other-Suggestions
Rperryng's suggestion of extending the Request<T> class may have provided a solution, and is a creative and excellent idea. Thank you very much for the detailed example. I found the optimal solution for our case is to use the work-around because we are fortunate enough to have control of the web services API.
I might opt for fixing the Volley code in one location within BasicNetwork.java if I did not have access to making a simple change at the server.
Volley supports HTTP 401 Unauthorized response. But this response MUST include "WWW-Authenticate" header field.
Without this header, 401 response causes "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found" error.
For more detail : https://stackoverflow.com/a/25556453/860189
If you consume 3rd party API's and have no right to change response header, you may consider to implement your own HttpStack because of this exception thrown from HurlStack. Or better, use OkHttpStack as a HttpStack.
You may modify the volley library's performRequest me(toolbox/BasicNetwork.java) method to capture 401 Unauthorized response. (This modified code will also solve http-> https redirect problem of volley)
#Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
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) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Handle moved resources
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setUrl(newUrl);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
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 NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} 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 {
throw new NoConnectionError(e);
}
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
VolleyLog.e("Request at %s has been redirected to %s", request.getUrl(), request.getUrl());
} else {
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
if (statusCode==HttpStatus.SC_FORBIDDEN) {
throw new VolleyError("403");
}else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
attemptRetryOnException("auth",
request, new AuthFailureError(""));
}
}
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
attemptRetryOnException("redirect",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
then in volley error handler use this code
#Override
public void onErrorResponse(VolleyError error) {
if (error instanceof AuthFailureError) {
//handler error 401 unauthorized from here
}
}
})
Happy coding :D
Network response can be received in the following format
NetworkResponse response = error.networkResponse;
if(response != null && response.data != null){
switch(response.statusCode){
case 403:
json = new String(response.data);
json = trimMessage(json, "error");
if(json != null) displayMessage(json);
break;
}
}
The error.networkResponse will be null, if the device has no network connection (you can proof this by enabling the airplane mode). Look at the corresponding code fragment from the Volley library.
You have to check then, if the error is an instance of the NoConnectionError, before you look for the networkResponse. I cannot agree, that 401 error is not supported by Volley, I tested it and got a non-null networkResponse object back with 401 status code. Look at the corresponding code here.
This is how I check and grep error.
// TimeoutError => most likely server is down or network is down.
Log.e(TAG, "TimeoutError: " + (e instanceof TimeoutError));
Log.e(TAG, "NoConnectionError: " + (e instanceof NoConnectionError));
/*if(error.getCause() instanceof UnknownHostException ||
error.getCause() instanceof EOFException ) {
errorMsg = resources.getString(R.string.net_error_connect_network);
} else {
if(error.getCause().toString().contains("Network is unreachable")) {
errorMsg = resources.getString(R.string.net_error_no_network);
} else {
errorMsg = resources.getString(R.string.net_error_connect_network);
}
}*/
Log.e(TAG, "NetworkError: " + (e instanceof NetworkError));
Log.e(TAG, "AuthFailureError: " + (e instanceof AuthFailureError));
Log.e(TAG, "ServerError: " + (e instanceof ServerError));
//error.networkResponse.statusCode
// inform dev
Log.e(TAG, "ParseError: " + (e instanceof ParseError));
//error.getCause() instanceof JsonSyntaxException
Log.e(TAG, "NullPointerException: " + (e.getCause() instanceof NullPointerException));
if (e.networkResponse != null) {
// 401 => login again
Log.e(TAG, String.valueOf(e.networkResponse.statusCode));
if (e.networkResponse.data != null) {
// most likely JSONString
Log.e(TAG, new String(e.networkResponse.data, StandardCharsets.UTF_8));
Toast.makeText(getApplicationContext(),
new String(e.networkResponse.data, StandardCharsets.UTF_8),
Toast.LENGTH_LONG).show();
}
}
else if (e.getMessage() == null) {
Log.e(TAG, "e.getMessage");
Log.e(TAG, "" + e.getMessage());
if (e.getMessage() != null && e.getMessage() != "")
Toast.makeText(getApplicationContext(),
e.getMessage(), Toast.LENGTH_LONG).show();
else
Toast.makeText(getApplicationContext(),
"could not reach server", Toast.LENGTH_LONG).show();
}
else if (e.getCause() != null) {
Log.e(TAG, "e.getCause");
Log.e(TAG, "" + e.getCause().getMessage());
if (e.getCause().getMessage() != null && e.getCause().getMessage() != "")
Toast.makeText(getApplicationContext(),
e.getCause().getMessage(), Toast.LENGTH_LONG).show();
else
Toast.makeText(getApplicationContext(),
"could not reach server", Toast.LENGTH_LONG).show();
}
I handle this problem manually:
Download Volley library from github and add into AndroidStudio project
Go to com.android.volley.toolbox.HurlStack class
Find setConnectionParametersForRequest(connection, request); line inside of performRequest method
And finally add this codes belew of setConnectionParametersForRequest(connection, request); line :
// for avoiding this exception : No authentication challenges found
try {
connection.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}