Related
"Cannot access database on the main thread since it may potentially lock the UI for a long period of time" but
when i allow room to execute queries on main thread then it doesn't give error
// MyIntentService.java:
public class MyIntentService extends IntentService {
String URL = "http://192.168.1.102/android/Document%20Sharing/getServerData.php";
String course;
AppDatabase appDatabase;
public MyIntentService() { super("MyIntentService"); }
#Override
public void onCreate() {
Log.i("tagg","onCreate");
super.onCreate();
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
appDatabase = AppDatabase.getAppDatabase(this);
course = intent.getStringExtra("course");
getServerData();
Log.i("tagg","onHandleIntentBefore");
}
private void getServerData() {
Log.i("tagg","here");
final RequestQueue requestQueue = Volley.newRequestQueue(this);
final StringRequest stringRequest = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Gson gson = new Gson();
IT_Subjects[] itSubjects = gson.fromJson(response, IT_Subjects[].class);
appDatabase.itSubjectsDao().insertAll(itSubjects);
//Log.i("tagg", response);
Log.i("tagg","here");
requestQueue.stop();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
Toast.makeText(MyIntentService.this, error.toString(), Toast.LENGTH_LONG).show();
requestQueue.stop();
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> prams = new HashMap<>();
prams.put("course", course);
return prams;
}
};
requestQueue.add(stringRequest);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
Log.i("tagg","onStartCommand");
return super.onStartCommand(intent,flags,startId);
}
#Override
public void onDestroy() {
Log.i("tagg","onDestroy");
AppDatabase.destroyInstance();
super.onDestroy();
}
}
//AppDatabase.java
#Database(entities = IT_Subjects.class, version = 1 )
public abstract class AppDatabase extends RoomDatabase {
public abstract IT_SubjectsDao itSubjectsDao();
private static AppDatabase INSTANCE;
public static AppDatabase getAppDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "IT_Students")
.build();
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
}
You enqueue a request - the request is processed asynchronously, so onHandleIntent returns immediately. (Then onDestroy is called and your cached static database instance lost!) Then onResponse is called on the main thread and data written to appDatabase.
requestQueue.add(stringRequest);
When you call add(), Volley runs one cache processing thread and a pool of network dispatch threads. When you add a request to the queue, it is picked up by the cache thread and triaged: if the request can be serviced from cache, the cached response is parsed on the cache thread and the parsed response is delivered on the main thread. If the request cannot be serviced from cache, it is placed on the network queue. The first available network thread takes the request from the queue, performs the HTTP transaction, parses the response on the worker thread, writes the response to cache, and posts the parsed response back to the main thread for delivery.
Source Send a simple request | Android Developers, emphasis mine.
A naïve workaround would be to execute the request synchronously (or enqueue it and await result). This has been explained in this answer (I didn't verify it so take it with a pinch of salt).
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(URL, new
JSONObject(), future, future);
requestQueue.add(request);
JSONObject response = future.get(); // this will block
A better approach might be to not use IntentService at all. Volley already does what you need - do the network stuff on worker thread, deliver the result to main thread, and even process the raw response on worker thread between those two.
You should create a custom Request class and override parseNetworkResponse as best described here: Implementing a custom request | Android Developers
Here's an example implementation of GsonRequest copied verbatim from the link above. It's not super efficient but should be good to start with.
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param clazz Relevant class object, for Gson's reflection
* #param headers Map of request headers
*/
public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}
I would like to send a new JsonObjectRequest request:
I want to receive JSON data (response from server): OK
I want to send JSON formatted data with this request to the server
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.POST, "myurl.com", null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//...
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//...
}
})
{
#Override
protected Map<String,String> getParams() {
// something to do here ??
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
// something to do here ??
return params;
}
};
P.S. I use GSON library in my project too.
JsonObjectRequest actually accepts JSONObject as body.
From this blog article,
final String url = "some/url";
final JSONObject jsonBody = new JSONObject("{\"type\":\"example\"}");
new JsonObjectRequest(url, jsonBody, new Response.Listener<JSONObject>() { ... });
Here is the source code and JavaDoc (#param jsonRequest):
/**
* Creates a new request.
* #param method the HTTP method to use
* #param url URL to fetch the JSON from
* #param jsonRequest A {#link JSONObject} to post with the request. Null is allowed and
* indicates no parameters will be posted along with request.
* #param listener Listener to receive the JSON response
* #param errorListener Error listener, or null to ignore errors.
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
I know that this thread is quite old, but I had this problem and I came up with a cool solution which can be very useful to many because it corrects/extended the Volley library on many aspects.
I spotted some not supported-out-of-box Volley features:
This JSONObjectRequest is not perfect: you have to expect a JSON at the end (see the Response.Listener<JSONObject>).
What about Empty Responses (just with a 200 status)?
What do I do if I want directly my POJO from the ResponseListener?
I more or less compiled a lot of solutions in a big generic class in order to have a solution for all the problem I quoted.
/**
* Created by laurentmeyer on 25/07/15.
*/
public class GenericRequest<T> extends JsonRequest<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
// Used for request which do not return anything from the server
private boolean muteRequest = false;
/**
* Basically, this is the constructor which is called by the others.
* It allows you to send an object of type A to the server and expect a JSON representing a object of type B.
* The problem with the #JsonObjectRequest is that you expect a JSON at the end.
* We can do better than that, we can directly receive our POJO.
* That's what this class does.
*
* #param method: HTTP Method
* #param classtype: Classtype to parse the JSON coming from the server
* #param url: url to be called
* #param requestBody: The body being sent
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param headers: Added headers
*/
private GenericRequest(int method, Class<T> classtype, String url, String requestBody,
Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
super(method, url, requestBody, listener,
errorListener);
clazz = classtype;
this.headers = headers;
configureRequest();
}
/**
* Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and not muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param toBeSent: Object which will be transformed in JSON via Gson and sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param headers: Added headers
*/
public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
this(method, classtype, url, new Gson().toJson(toBeSent), listener,
errorListener, headers);
}
/**
* Method to be called if you want to send some objects to your server via body in JSON of the request (without header and not muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param toBeSent: Object which will be transformed in JSON via Gson and sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
*/
public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(method, classtype, url, new Gson().toJson(toBeSent), listener,
errorListener, new HashMap<String, String>());
}
/**
* Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param requestBody: String to be sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
*/
public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(method, classtype, url, requestBody, listener,
errorListener, new HashMap<String, String>());
}
/**
* Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (Without header)
*
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
*/
public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener) {
this(Request.Method.GET, url, classtype, "", listener, errorListener);
}
/**
* Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (With headers)
*
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param headers: Added headers
*/
public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
this(Request.Method.GET, classtype, url, "", listener, errorListener, headers);
}
/**
* Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param toBeSent: Object which will be transformed in JSON via Gson and sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param headers: Added headers
* #param mute: Muted (put it to true, to make sense)
*/
public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers, boolean mute) {
this(method, classtype, url, new Gson().toJson(toBeSent), listener,
errorListener, headers);
this.muteRequest = mute;
}
/**
* Method to be called if you want to send some objects to your server via body in JSON of the request (without header and muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param toBeSent: Object which will be transformed in JSON via Gson and sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param mute: Muted (put it to true, to make sense)
*/
public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
this(method, classtype, url, new Gson().toJson(toBeSent), listener,
errorListener, new HashMap<String, String>());
this.muteRequest = mute;
}
/**
* Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
*
* #param method: HTTP Method
* #param url: URL to be called
* #param classtype: Classtype to parse the JSON returned from the server
* #param requestBody: String to be sent to the server
* #param listener: Listener of the request
* #param errorListener: Error handler of the request
* #param mute: Muted (put it to true, to make sense)
*/
public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
this(method, classtype, url, requestBody, listener,
errorListener, new HashMap<String, String>());
this.muteRequest = mute;
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
// The magic of the mute request happens here
if (muteRequest) {
if (response.statusCode >= 200 && response.statusCode <= 299) {
// If the status is correct, we return a success but with a null object, because the server didn't return anything
return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
}
} else {
try {
// If it's not muted; we just need to create our POJO from the returned JSON and handle correctly the errors
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
T parsedObject = gson.fromJson(json, clazz);
return Response.success(parsedObject, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
return null;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
private void configureRequest() {
// Set retry policy
// Add headers, for auth for example
// ...
}
}
It could seem a bit overkill but it's pretty cool to have all these constructors because you have all the cases:
(The main constructor wasn't meant to be used directly although it's, of course, possible).
Request with response parsed to POJO / Headers manually set / POJO to Send
Request with response parsed to POJO / POJO to Send
Request with response parsed to POJO / String to Send
Request with response parsed to POJO (GET)
Request with response parsed to POJO (GET) / Headers manually set
Request with no response (200 - Empty Body) / Headers manually set / POJO to Send
Request with no response (200 - Empty Body) / POJO to Send
Request with no response (200 - Empty Body) / String to Send
Of course, in order that it works, you have to have Google's GSON Lib; just add:
compile 'com.google.code.gson:gson:x.y.z'
to your dependencies (current version is 2.3.1).
final String URL = "/volley/resource/12";
// Post params to be sent to the server
HashMap<String, String> params = new HashMap<String, String>();
params.put("token", "AbCdEfGh123456");
JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
VolleyLog.v("Response:%n %s", response.toString(4));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
}
});
// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);
refer
final String url = "some/url";
instead of:
final JSONObject jsonBody = "{\"type\":\"example\"}";
you can use:
JSONObject jsonBody = new JSONObject();
try {
jsonBody.put("type", "my type");
} catch (JSONException e) {
e.printStackTrace();
}
new JsonObjectRequest(url, jsonBody, new Response.Listener<JSONObject>() { ... });
You can also send data by overriding getBody() method of JsonObjectRequest class. As shown below.
#Override
public byte[] getBody()
{
JSONObject jsonObject = new JSONObject();
String body = null;
try
{
jsonObject.put("username", "user123");
jsonObject.put("password", "Pass123");
body = jsonObject.toString();
} catch (JSONException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
try
{
return body.toString().getBytes("utf-8");
} catch (UnsupportedEncodingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
final Map<String,String> params = new HashMap<String,String>();
params.put("email", customer.getEmail());
params.put("password", customer.getPassword());
String url = Constants.BASE_URL+"login";
doWebRequestPost(url, params);
public void doWebRequestPost(String url, final Map<String,String> json){
getmDialogListener().showDialog();
StringRequest post = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
getmDialogListener().dismissDialog();
response....
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(App.TAG,error.toString());
getmDialogListener().dismissDialog();
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> map = json;
return map;
}
};
App.getInstance().getRequestQueue().add(post);
}
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
JSONObject JObj = new JSONObject();
try {
JObj.put("Id","1");
JObj.put("Name", "abc");
} catch (Exception e) {
e.printStackTrace();
}
params.put("params", JObj.toString());
// Map.Entry<String,String>
Log.d("Parameter", params.toString());
return params;
}
So, I have this Volley PUT request:
private boolean syncCall(JSONObject jsonObject, final VolleyCallback
callback) {
final ProgressDialog progDailog = new ProgressDialog(context);
final Boolean[] success = {false};
progDailog.setMessage("...");
progDailog.setIndeterminate(false);
progDailog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progDailog.setCancelable(false);
progDailog.show();
final SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
RequestQueue queue = Volley.newRequestQueue(context, new HurlStack());
final String token = prefs.getString("token", null);
String URL = Constants.getUrlSync();
String param1 = String.valueOf(prefs.getInt("pmp", 1));
String param2 = String.valueOf(prefs.getInt("ei", 1));
URL = URL.replace("[x]", param1);
URL = URL.replace("[y]", param2);
//pegar id pmp e IE corretas
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request
.Method.PUT, URL, jsonObject,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
callback.onSuccess(response + "");
success[0] = true;
progDailog.dismiss();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onFailure(error);
tokenFailure(error);
success[0] = false;
progDailog.dismiss();
}
}) {
#Override
public Map<String, String> getHeaders() throws
AuthFailureError {
HashMap<String, String> headers = new HashMap<>();
headers.put("Token", token);
return headers;
}
};
int socketTimeout = 30000;
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
queue.add(jsObjRequest);
return success[0];
}
My problem is that I send a very large JSON, so the default timeout of 5 seconds is not enough. So, I tried to increase the timeout to 30 seconds and messing with the DefaultRetryPolicy to increase the number of retries.
The thing is, it keeps timeouting in 5s and it doesn't even retry once!
Do I have to have a listener or a callback for the retries ? I'm doing something wrong with the DefaultRetryPolicy ? Please help, this issue is driving me nuts...
Do you need to use the DefaultRetryPolicy?
Because you could define your own.
Instead of this:
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
Try this:
jsObjRequest.setRetryPolicy(new RetryPolicy() {
#Override
public int getCurrentTimeout() {
// Here goes the new timeout
return mySeconds;
}
#Override
public int getCurrentRetryCount() {
// The max number of attempts
return myAttempts;
}
#Override
public void retry(VolleyError error) throws VolleyError {
// Here you could check if the retry count has gotten
// To the max number, and if so, send a VolleyError msg
// or something
}
});
I'm not sure exactly why the retry time doesn't work on your code, I did find a similar issue here though.
Instead, I can tell you some things that I don't believe to be ok in your code and suggest you to adopt my model of Volley use.
First of all, you're creating a new request queue for every request you're making. That's not cool, you should have a RequestManager singleton that holds one request queue and use that.
Second of all, I don't know if this is what affects the retry time, I have a base request class and set the retry time in the constructor. Then, I extend this class whenever I have to implement a new type of request. Then, I create an instance of the request, set the callbacks, and pass it to the request manager. The request manager adds it to the one request queue I was talking about.
More over, if you don't already, I suggest you use the Gson library to parse the JSON objects.
This is my base request class I'm using:
/**
* Created by Daniel on 2/6/2016.
*/
public class GsonRequest<T> extends Request<T> {
protected Context context;
protected final Gson gson = new Gson();
protected final Class<T> clazz;
protected final TypeToken typeToken;
protected Map<String, String> headers;
protected Map<String, String> params;
protected final Response.Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param clazz Relevant class object, for Gson's reflection
*/
public GsonRequest(final Context context, final int requestMethod, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(requestMethod, url, errorListener);
this.context = context;
this.clazz = clazz;
this.listener = listener;
this.headers = new HashMap<>();
typeToken = null;
setRetryPolicy();
}
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param typeToken Relevant typeToken object, for Gson's reflection
*/
public GsonRequest(final Context context, final int requestMethod, String url, TypeToken typeToken, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(requestMethod, url, errorListener);
this.context = context;
this.typeToken = typeToken;
this.listener = listener;
this.headers = new HashMap<>();
clazz = null;
setRetryPolicy();
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
//TODO add headers here
return headers;
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
JSONObject jsonObject = new JSONObject(json);
if (clazz != null) {
return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
} else {
return Response.success((T) gson.fromJson(json, typeToken.getType()), HttpHeaderParser.parseCacheHeaders(response));
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
} catch (JSONException e) {
return Response.error(new ParseError(e));
}
}
protected void setRetryPolicy() {
//TODO set your retry policy here
setRetryPolicy(new DefaultRetryPolicy(
30000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
}`enter code here`
This works like a charm for me. Hope it helps, if you need any further help, contact me
I am converting my application to use the Volley framework.
I have everything working as far as I can tell, when I make a webservice request using HttpClient, wireshark is showing my parameters as
devicePrint=Android&mobileSaveUserId=false&__ResponseType=XML&appVersion=Android-3.2&__ResponseTypeVersion=1.0&userId=mbltest6&retrievePostedTransaction=falseHTTP/1.1
200 OK
This works perfectly, but when I use volley, my packet looks like this.
devicePrint=Android&mobileSaveUserId=false&__ResponseType=XML&appVersion=Android-3.2&__ResponseTypeVersion=1.0&userid=mbltest6&retrievePostedTransaction=false&HTTP/1.1
200 OK
I moved them side by side and noticed the volley example (The bottom one) is appending an extra "&" which is causing my webservice to return "Username not found" even though it is clearly in there.
Any suggestions on how to fix this? Or why it's happening?
EDIT: Upon further investigation into the base classes, when you set the parameters it always adds an extra "&" to the end of the string. Is this normal behavior for volley? Could this be whats causing my request to fail?
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
Iterator uee = params.entrySet().iterator();
while(uee.hasNext()) {
java.util.Map.Entry entry = (java.util.Map.Entry)uee.next();
encodedParams.append(URLEncoder.encode((String)entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode((String)entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException var6) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, var6);
}
}
My code is as follows:
public abstract class WebServiceRequest extends StringRequest {
private static final int SOCKET_TIMEOUT = (1000 * 60);
private static final String RESPONSE_TYPE_KEY = "__ResponseType";
private static final String RESPONSE_TYPE_VERSION_KEY = "__ResponseTypeVersion";
private static final String APP_VERSION_KEY = "appVersion";
private static final String RETRIEVE_POSTED_KEY = "retrievePostedTransaction";
public boolean mRetrievePostedTransactions;
private Map<String, String> mParameters;
public WebServiceRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, listener, errorListener);
this.mParameters = new HashMap<>();
setRetryPolicy(new DefaultRetryPolicy(
SOCKET_TIMEOUT,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
));
}
public abstract void setParams(Map<String,String> params);
#Override
protected Map<String, String> getParams() throws AuthFailureError {
setCommonParameters(mParameters);
setParams(mParameters);
return mParameters;
}
private Map<String, String> setCommonParameters (Map<String,String> params) {
params.put( APP_VERSION_KEY, "Android-" + VirtualWalletApplication.getInstance().appVersion );
params.put( RESPONSE_TYPE_KEY, "XML" );
params.put( RESPONSE_TYPE_VERSION_KEY, "1.0" );
params.put( RETRIEVE_POSTED_KEY, Boolean.valueOf( mRetrievePostedTransactions ).toString() );
return params;
}
}
Here is where i create my request
WebServiceRequest request = new WebServiceRequest(
StringUtils.getFullServerUrlForResource("/alservlet/ValidateUserIdServlet"),
onSuccessListener,
onFailureListener) {
#Override
public void setParams(Map<String, String> params) {
params.put("userid","mbltest6");
params.put("mobileSaveUserId","false");
params.put("devicePrint","Android");
}
};
NetworkVolley.getInstance().sendRequest(request);
Turns out, i needed to capitalize the parameter "userId" not "userid". Keep a close eye on your parameters people!
How can custom headers be set for a Volley request? At the moment, there is way to set body content for a POST request. I've a simple GET request, but I need to pass the custom headers alongwith. I don't see how JsonRequest class supports it. Is it possible at all?
The accepted answer with getParams() is for setting POST body data, but the question in the title asked how to set HTTP headers like User-Agent. As CommonsWare said, you override getHeaders(). Here's some sample code which sets the User-Agent to 'Nintendo Gameboy' and Accept-Language to 'fr':
public void requestWithSomeHttpHeaders() {
RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://www.somewebsite.com";
StringRequest getRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response) {
// response
Log.d("Response", response);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
Log.d("ERROR","error => "+error.toString());
}
}
) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("User-Agent", "Nintendo Gameboy");
params.put("Accept-Language", "fr");
return params;
}
};
queue.add(getRequest);
}
It looks like you override public Map<String, String> getHeaders(), defined in Request, to return your desired HTTP headers.
If what you need is to post data instead of adding the info in the url.
public Request post(String url, String username, String password,
Listener listener, ErrorListener errorListener) {
JSONObject params = new JSONObject();
params.put("user", username);
params.put("pass", password);
Request req = new Request(
Method.POST,
url,
params.toString(),
listener,
errorListener
);
return req;
}
If what you want to do is edit the headers in the request this is what you want to do:
// could be any class that implements Map
Map<String, String> mHeaders = new ArrayMap<String, String>();
mHeaders.put("user", USER);
mHeaders.put("pass", PASSWORD);
Request req = new Request(url, postBody, listener, errorListener) {
public Map<String, String> getHeaders() {
return mHeaders;
}
}
You can see this solution. It shows how to get/set cookies, but cookies are just one of the headers in a request/response. You have to override one of the Volley's *Request classes and set the required headers in getHeaders()
Here is the linked source:
public class StringRequest extends com.android.volley.toolbox.StringRequest {
private final Map<String, String> _params;
/**
* #param method
* #param url
* #param params
* A {#link HashMap} to post with the request. Null is allowed
* and indicates no parameters will be posted along with request.
* #param listener
* #param errorListener
*/
public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, listener, errorListener);
_params = params;
}
#Override
protected Map<String, String> getParams() {
return _params;
}
/* (non-Javadoc)
* #see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse)
*/
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// since we don't know which of the two underlying network vehicles
// will Volley use, we have to handle and store session cookies manually
MyApp.get().checkSessionCookie(response.headers);
return super.parseNetworkResponse(response);
}
/* (non-Javadoc)
* #see com.android.volley.Request#getHeaders()
*/
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (headers == null
|| headers.equals(Collections.emptyMap())) {
headers = new HashMap<String, String>();
}
MyApp.get().addSessionCookie(headers);
return headers;
}
}
And MyApp class:
public class MyApp extends Application {
private static final String SET_COOKIE_KEY = "Set-Cookie";
private static final String COOKIE_KEY = "Cookie";
private static final String SESSION_COOKIE = "sessionid";
private static MyApp _instance;
private RequestQueue _requestQueue;
private SharedPreferences _preferences;
public static MyApp get() {
return _instance;
}
#Override
public void onCreate() {
super.onCreate();
_instance = this;
_preferences = PreferenceManager.getDefaultSharedPreferences(this);
_requestQueue = Volley.newRequestQueue(this);
}
public RequestQueue getRequestQueue() {
return _requestQueue;
}
/**
* Checks the response headers for session cookie and saves it
* if it finds it.
* #param headers Response Headers.
*/
public final void checkSessionCookie(Map<String, String> headers) {
if (headers.containsKey(SET_COOKIE_KEY)
&& headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
String cookie = headers.get(SET_COOKIE_KEY);
if (cookie.length() > 0) {
String[] splitCookie = cookie.split(";");
String[] splitSessionId = splitCookie[0].split("=");
cookie = splitSessionId[1];
Editor prefEditor = _preferences.edit();
prefEditor.putString(SESSION_COOKIE, cookie);
prefEditor.commit();
}
}
}
/**
* Adds session cookie to headers if exists.
* #param headers
*/
public final void addSessionCookie(Map<String, String> headers) {
String sessionId = _preferences.getString(SESSION_COOKIE, "");
if (sessionId.length() > 0) {
StringBuilder builder = new StringBuilder();
builder.append(SESSION_COOKIE);
builder.append("=");
builder.append(sessionId);
if (headers.containsKey(COOKIE_KEY)) {
builder.append("; ");
builder.append(headers.get(COOKIE_KEY));
}
headers.put(COOKIE_KEY, builder.toString());
}
}
}
In Kotlin,
You have to override getHeaders() method like :
val volleyEnrollRequest = object : JsonObjectRequest(GET_POST_PARAM, TARGET_URL, PAYLOAD_BODY_IF_YOU_WISH,
Response.Listener {
// Success Part
},
Response.ErrorListener {
// Failure Part
}
) {
// Providing Request Headers
override fun getHeaders(): Map<String, String> {
// Create HashMap of your Headers as the example provided below
val headers = HashMap<String, String>()
headers["Content-Type"] = "application/json"
headers["app_id"] = APP_ID
headers["app_key"] = API_KEY
return headers
}
}
Looking for solution to this problem as well.
see something here: http://developer.android.com/training/volley/request.html
is it a good idea to directly use ImageRequest instead of ImageLoader? Seems ImageLoader uses it internally anyway. Does it miss anything important other than ImageLoader's cache support?
ImageView mImageView;
String url = "http://i.imgur.com/7spzG.png";
mImageView = (ImageView) findViewById(R.id.myImage);
...
// Retrieves an image specified by the URL, displays it in the UI.
mRequestQueue = Volley.newRequestQueue(context);;
ImageRequest request = new ImageRequest(url,
new Response.Listener() {
#Override
public void onResponse(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}, 0, 0, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.drawable.image_load_error);
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new Map<String, String>();
params.put("User-Agent", "one");
params.put("header22", "two");
return params;
};
mRequestQueue.add(request);
try this
{
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
String bearer = "Bearer ".concat(token);
Map<String, String> headersSys = super.getHeaders();
Map<String, String> headers = new HashMap<String, String>();
headersSys.remove("Authorization");
headers.put("Authorization", bearer);
headers.putAll(headersSys);
return headers;
}
};
You can make a custom Request class that extends the StringRequest and override the getHeaders() method inside it like this:
public class CustomVolleyRequest extends StringRequest {
public CustomVolleyRequest(int method, String url,
Response.Listener<String> listener,
Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("key1","value1");
headers.put("key2","value2");
return headers;
}
}
public class CustomJsonObjectRequest extends JsonObjectRequest
{
public CustomJsonObjectRequest(int method, String url, JSONObject jsonRequest,Response.Listener listener, Response.ErrorListener errorListener)
{
super(method, url, jsonRequest, listener, errorListener);
}
#Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
headers.put("AppId", "xyz");
return headers;
}
}
As addition I'd like to share something I found regarding the Content-Type:
On top of
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
.
.
.
return params;
}
I had to add:
#Override
public String getBodyContentType() {
return /*(for exmaple)*/ "application/json";
}
Don't ask me why, I just thought it might help some others that can't get the Content-Type set right.
Here is setting headers from github sample:
StringRequest myReq = new StringRequest(Method.POST,
"http://ave.bolyartech.com/params.php",
createMyReqSuccessListener(),
createMyReqErrorListener()) {
protected Map<String, String> getParams() throws
com.android.volley.AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("param1", num1);
params.put("param2", num2);
return params;
};
};
queue.add(myReq);
try this
public void VolleyPostReqWithResponseListenerwithHeaders(String URL,final Map<String, String> params,final Map<String, String> headers,Response.Listener<String> responseListener) {
String url = URL;
Log.i("url:", ":" + url);
StringRequest mStringRequest = new StringRequest(Request.Method.POST,
url, responseListener, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// error
//Log.d("Error.Response", error.getLocalizedMessage());
}
}){
#Override
protected Map<String, String> getParams() {
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers;
}
};
mStringRequest.setRetryPolicy(new DefaultRetryPolicy(
60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
mStringRequest.setShouldCache(true);
// dialog.show();
SingletonRequestQueue.getInstance(context).addToRequestQueue(mStringRequest);
}
That is my code, dont forget = object: if don't put don't works
val queue = Volley.newRequestQueue(this)
val url = "http://35.237.133.137:8080/lamarrullaWS/rest/lamarrullaAPI"
// Request a string response from the provided URL.
val jsonObjectRequest = object: JsonObjectRequest(Request.Method.GET, url, null,
Response.Listener { response ->
txtPrueba.text = "Response: %s".format(response.toString())
},
Response.ErrorListener { txtPrueba.text = "That didn't work!" }
)
{
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers.put("Content-Type", "application/json")
return headers
}
}
queue.add(jsonObjectRequest)