I'm using volley library:
compile 'com.mcxiaoke.volley:library:1.0.18'
In http helper class i have the following method.
public static JsonRequest createRequest(String responseType, int requestMethod, String scheme,
String url, final String requestParams, final HttpResponseListener listener,
Request.Priority priority) throws UnsupportedEncodingException {
// Start to prepare request URL
Uri.Builder builder = new Uri.Builder();
builder.scheme(scheme).encodedAuthority(url);
// GET REQUESTS - append the params into URL
if (requestMethod == Request.Method.GET && requestParams != null) {
boolean append = appendParamsToUrl(requestParams, builder);
if(!append) return null;
}
url = URLDecoder.decode(builder.build().toString(), Constants.Request.DEFAULT_ENCODING);
// Get response as JSON object
JsonRequest request;
if (responseType.equals(Constants.Request.JSON_OBJECT)) {
// Prepare request and set the Callbacks
request = new CustomJsonObjectRequest(requestMethod, url, requestParams,
priority, responseListener(listener), errorListener(listener), listener);
}else { // Get response as JSON array of objects
// Prepare request and set the Callbacks
request = new CustomJsonArrayRequest(requestMethod, url, requestParams,
priority, responseArrayListener(listener), errorListener(listener), listener);
}
request.setTag(REQUEST_TAG);
request.setShouldCache(false);
return request;
}
When i using the option:
request.setShouldCache(false);
To force disabling cache.
But when i get the response from server from the POSTMAN (Chrome extension for API testing) i got different values in response than on the Android device.
I tried also use the:
queue.getCache().clear();
But with the same results.
How can i force disable the cache from response?
Many thanks for any advice.
request.setShouldCache(false);
does not seem to be enough for GET requests.
however, clearing the cache before adding to the queue seems to help
myRequestQueue.getCache().clear();
I put this in my getRequestQueue() method in my Volley singleton before returning requestQueue.
Call the following:
myRequestQueue.getCache().remove(url);
To use Volley without response caching, instead of using Volley.newRequestQueue(), you can create your own RequestQueue as follows:
HttpStack stack;
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
Network network = new BasicNetwork(stack);
queue = new RequestQueue(new NoCache(), network);
The key is the NoCache object which implements the Volley Cache interface but does nothing.
Bonus: If you want, you can also implement the HttpStack using OkHttp (shipped with the app). The good thing about that approach is that since you ship the OkHttp library with your app, you can rest assured that your HttpStack implementation will always work on all Android versions since you're not dependent on the platform's HttpStack implementation. Plus OkHttp has all sorts of goodies like the interceptor mechanism and a very simple API.
Related
In okhttp3, if my connection times out in CONNECT or READ, is there some way I can get the cache from okhttp? Instead of the connection failing, I want to serve the user from the offline cache in case the request is taking too long.
I did experience a similar issue. I wanted to fallback to cache whenever my request was timing out (I don't mind in which state) or when connection is disrupted, or when there is no connection available. To do this I made an interceptor that would first check for connectivity and after that also catch exceptions when making the request. If there is a timeout then it will throw an exception, after which we fallback to an aggressive caching.
So basically, you first need to set up your okhttp client to use cache and then use an interceptor to use that cache in a better way.
public OkHttpClient getOkHttpClient() {
File cacheFile = new File(context.getCacheDir(), "okHttpCache");
Cache cache = new Cache(cacheFile, CACHE_SIZE);
ConnectivityInterceptor connectivityInterceptor = new ConnectivityInterceptor(networkStateHelper);
OkHttpClient.Builder builder = new OkHttpClient.Builder().cache(cache).addInterceptor(connectivityInterceptor);
return builder.build();
}
After that you can use this simple interceptor to force the usage of the cache. Normally the cache is used when the server responds with 340 which means there are no changes so we can take responses that are cached, but this of course needs an active internet connection. We can however force the cache usage so it will directly take any respond from the cache if possible, which comes in handy when you are offline or when you have timeouts
public class ConnectivityInterceptor implements Interceptor {
// NetworkStateHelper is some class we have that checks if we are online or not.
private final NetworkStateHelper networkStateHelper;
public ConnectivityInterceptor(NetworkStateHelper networkStateHelper) {
this.networkStateHelper = networkStateHelper;
}
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
// You can omit this online check or use your own helper class
if (networkStateHelper.isNotOnline()) {
return getResponseFromCache(chain, request);
}
try {
Response response = chain.proceed(request);
return new Pair<>(request, response);
}
catch (Exception exception) {
Log.w(exception, "Network failure discovered, trying cache fallback");
return getResponseFromCache(chain, request);
}
}
private Response getResponseFromCache(Interceptor.Chain chain,
Request request) throws IOException {
// We just create a new request out of the old one and set cache headers to it with the cache control.
// The CacheControl.FORCE_CACHE is already provided by OkHttp3
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
// Now we proceed with the request and OkHttp should automatically fetch the response from cache or return
// a failure if it is not there, some 5xx status code
return chain.proceed(request);
}
}
My app uses dynamic URLs to make web-service calls (on Android). baseUrl is set as empty and we pass Retrofit2 #Url parameters in the service interface:
public interface UserService {
#GET
public Call<ResponseBody> profilePicture(#Url String url);
}
We don't know the host/domain in advance, so MockWebServer is not able to intercept the requests. The call to fetch the initial list of dynamic URLs is made in different screens. One idea is to create a new flavor providing a local data source for URLs to be used, which is my fallback plan.
I am curious if MockWebServer has any other methods to help test such cases and can be limited to test code.
You could use an OkHttp interceptor to rewrite the hostname and port?
I was also facing the same kind of issue. When I use MockWebserver in testing I have to change base URL to target mock web server localhost and port. I tried this it is working fine.
private static final Interceptor mRequestInterceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
final InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
HttpUrl httpUrl = request.url().newBuilder().scheme("http://").host(address.getHostName()).port(8080)
.build();
request = request.newBuilder()
.url(httpUrl)
.build();
return chain.proceed(request);
}
};
After this base url changes to "http://localhost:8080/"
I am working on an Android app in which a log in post request is made to a webservice. The request returns a cookie which expires in 20 minutes.
Using okhttp3 and this PersistentCookieStore library, I got the cookie to be stored and subsequently added it as request header to access authentication-required get requests (e.g. personal information that are non-public).
The code goes this way,
CookieJar myCookieJar = new PersistentCookieJar(new SetCookieCache(),
new SharedPrefsCookiePersistor(this));
OkHttpClient client = new OkHttpClient.Builder().cookieJar(HttpRequests.cookieJar).build();
I then call a method like this inside an (after I have gone through another log in Async task to get the cookie) Async task to perform a get request that requires authentication,
public static String PostReq(String url, String json) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.addHeader("Cookie", "key=value")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
catch(Exception e){
}
}
The .addHeader("Cookie", "key=value") adds the cookie to the header to tell the webservice that I am authenticated.
Here comes my difficulty. Since the cookie expires after 20 minutes, I would like to be able to access the cookie itself to check for the expiration time and possibly redirect the user to the log in activity by calling the method,
myCookie.expiresAt()
and comparing it to
System.currentTimeMillis()
I tried to look at the PersistentCookieStore codes and found that it uses a SharedPreference with the key "CookiePersistence". I looked inside this file while my emulator was running the app and found it to be empty however.
How would I be able to access this cookie that I have obtained? Much thanks for any advice to be given.
OK, this is old, but I was facing the same problem, and here is how I fixed it.
Hold a reference to your SetCookieCache used to instantiate your CookieJar:
SetCookieCache cookieCache = new SetCookieCache();
CookieJar myCookieJar = new PersistentCookieJar(
cookieCache,
new SharedPrefsCookiePersistor(this)
);
Then use this to find your cookie and check it:
for (Cookie cookie : cookieCache) {
if (cookie.name().equals("cookie_name") && cookie.persistent()) {
//cookie is still good
break;
}
}
Or use cookie.expiresAt() to do your thing.
I am attempting to call a put method on my server using OkHttp from an Android application.
This is the api method signature:
public void Put(int userId, string regId)
{
}
This is the Android code to call the above method:
private boolean SendGCMRegIdToServer(String registrationId, Integer userId) throws IOException {
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(serverApiHost)
.addPathSegment("AppDashboard")
.addPathSegment("api")
.addPathSegment("GCM/")
.build();
MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
String json = "{'userId':" + userId + ","
+ "'regId':'" + registrationId + "'"
+ "}";
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.put(requestBody)
.build();
//this should post the data to my server
Response response = client.newCall(request).execute();
if(response.code() == 400)
return false;
return true;
}
Now the problem is I am getting the error code 405 in the response saying Method not allowed, but I cannot see where the problem is because I can successfully call the method using Postman on the server itself as below:
http://localhost/AppDashboard/api/GCM?userId=5®Id=123
I'm thinking it may have something to do with an integer or string being passed incorrectly in the JSON string, but cannot see why this isn't working.
i had the same problem and server was returning 405 . after some search i realized that is a configuration problem on IIS that does not let put requests. so there is no problem in android code and you should config your server to let this kind of requests.
see this , this and this
Ok thanks for replies guys but seems I was getting a little confused between the two methods I was using to pass the params to my API.
Here's what I did:
changed the signature of the method to post with a param [FromBody] as a Model (only supports one paramater)...
public void Post([FromBody]UserGcmRegIdModel model)
{
}
I was then able to change my method call to the following using a nicer JSONBuilder and using .post in the request builder rather than .put
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("UserId", userId);
jsonObject.put("RegId", registrationId);
} catch (JSONException e) {
e.printStackTrace();
}
String json = jsonObject.toString();
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
I still don't know if there is a problem with put() methods on IIS but using a post in my case was absolutely fine so I'm going with that...
I see two different approaches in your REST api calls. In the one of OkHttp you send a PUT method with a JSON object serialized, and in POSTMAN you send a PUT (although I guess you do a GET) request with the parameters within the URL, I mean not in JSON body structure.
Anyway, HTTP 405 is telling you that your backend does not support the PUT method, and probably it's expecting a POST method with the "X-HTTP-Method-Override:PUT" HTTP header since POST is more standard method in REST than PUT.
What would I do is check your POSTMAN request carefully and adjust the one of Android to be the same method, parameters and headers, not more.
Answer Update (as question has been updated)
Of course there is a problem with that verb, as I said above IIS handles only the standard methods and PUT is not one of those. You have three choices:
Change your PUT to POST.
Use POST with X-HTTP-Method-Override to PUT. (reference)
Modify IIS config to support non standard REST methods. I
personally wouldn't suggest the 3rd one, since it's attached to the
backend config (e.g. imagine you change IIS to NancyFX).
I want to try Volley combining with OkHttp but Volley cache system and OkHttp both rely on the HTTP cache as defined in the HTTP specification. So how can be disabled the cache of OkHttp for keeping one copy of HTTP cache?
EDIT: what I have done
public class VolleyUtil {
// http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/
private volatile static RequestQueue sRequestQueue;
/** get the single instance of RequestQueue **/
public static RequestQueue getQueue(Context context) {
if (sRequestQueue == null) {
synchronized (VolleyUtil.class) {
if (sRequestQueue == null) {
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());
client.setCache(null);
sRequestQueue = Volley.newRequestQueue(context.getApplicationContext(), new OkHttpStack(client));
VolleyLog.DEBUG = true;
}
}
}
return sRequestQueue;
}
}
Which OkHttpClient is referenced from https://gist.github.com/bryanstern/4e8f1cb5a8e14c202750
OkHttp is a kind of HTTP client like HttpUrlConnection which implements HTTP cache, we can disable the cache of OkHttp like below:
OkHttpClient client = new OkHttpClient();
client.setCache(null);
Then, we can keep one copy of HTTP cache maintained by Volley.
IMPROVED:
I'd like to try to answer Sotti's questions.
1 I would like to know what is a good cache setup when using Volley and OkHttp.
In my project, i'm using one Volley requestQueue instance across all of restful APIs, and OkHttp worked as the transport layer for Volley like below.
public class VolleyUtil {
// http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/
private volatile static RequestQueue sRequestQueue;
/** get the single instance of RequestQueue **/
public static RequestQueue getQueue(Context context) {
if (sRequestQueue == null) {
synchronized (VolleyUtil.class) {
if (sRequestQueue == null) {
OkHttpClient client = new OkHttpClient();
client.setCache(null);
sRequestQueue = Volley.newRequestQueue(context.getApplicationContext(), new OkHttpStack(client));
VolleyLog.DEBUG = true;
}
}
}
return sRequestQueue;
}}
2 Should we rely on Volley or on the OkHttp cache?
Yes, i'm using Volley cache for my HTTP Cache instead of OkHttp Cache;
It works great for me.
3 What's the default behaviour out of the box?
For Volley:
it will create a "volley" default cache directory for you automatically.
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
……
}
For OkHttp:
i can't find the default cache in the source code, and we can set the response cache like this post
http://blog.denevell.org/android-okhttp-retrofit-using-cache.html
4. What's the recommended behaviour and how to achieve it?
As this post says:
Volley takes care of requesting, loading, caching, threading, synchronization and more. It’s ready to deal with JSON, images, caching, raw text and allow some customization.
I prefer to using Volley HTTP Cache because of the ease of customization.
For example, we can have much more control on the cache like this
Android Volley + JSONObjectRequest Caching.
The graceful way for OkHttp to ignore caches is:
request.setCacheControl(CacheControl.FORCE_NETWORK);