I've created my own request and in the constructor I set setShouldCache(Boolean.TRUE); .
When the request is called for the first time (no cache is used) then all is good and I can see the content being cached. Second time I call the same URL a bizarre thing happens:
else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request); <-- being called
Extracted from RequestQueue.add.
I can see in the debugger that mCacheQueuefills up, but nothing is being called from it, .e.g
// Take a request from the queue.
request = mCacheQueue.take();
Extracted from CacheDispatcher.run.
is stuck.
This is the piece of code that creates the request:
RequestFuture<String> future = _requestFutureProvider.get();
FetchArticleImageRequest request = new FetchArticleImageRequest(pageId,
300,
future, future);
_requestQueue.add(request);
try {
// this will never block
imgUrl = future.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("Unable to fetch img url", e);
}
One more piece of information, I'm running the request inside parseQueryResponse of another request, so it does not run in the main thread.
BTW when not using cache all is working well (e.g. requests are being made against the server).
Any idea what is going here?
Ok, Tx to our buddies in google groups (Ficus Kirkpatrick) there is an answer.
It happened because I ran a Future request inside another request which ran as part of the cache dispatcher. My solution was to remove caching from the request that dispatched the other requests.
Related
I am making a network call using rxJava2, and based on the response (either success or error), I have to move my work forward on UI thread.
I have written the code below. It seems working fine.
WApi wApi = ServiceGenerator.createService(WApi.class, sURL);
dataManager = InventoryModule.getDataManager();
rx.Observable<GetFeature> getFeatureObservable =
dataManager.executeGetFeature(caseId, wrapperApi);
if (getCV2FeatureObservable != null) {
try {
getFeatureObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> {
Log.e(TAG, "Err::" + throwable.getMessage());
// init default values because of error response
initDefaultValues();
// No data to use from WS call. Move forward with
//cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
})
.onErrorResumeNext(rx.Observable.empty())
.subscribe(getFeature -> {
// use the data from WS response
processAndUpdateFeature(getFeature);
// move forward with cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
});
} catch (Exception e) {
Log.e(TAG, e.getLocalizedMessage());
}
}
Still I need opinions, Am I doing it right? Am I missing something? or can I use other operators and make it better? the way I am placing my UI work into corresponding operators, will it work properly in both error or success response?
The only questionable choice is all the complications you're doing on errors.
instead of using doOnError + onErrorResumeNext I suggest you move your logic to the Subscriber:
getFeatureObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getFeature -> {
// use the data from WS response
processAndUpdateFeature(getFeature);
// move forward with cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
}, { throwable -> {
Log.e(TAG, "Err::" + throwable.getMessage());
// init default values because of error response
initDefaultValues();
// No data to use from WS call. Move forward with
//cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
});
Your thread switching (subscribeOn and observeOn) is fine.
EDIT:
One more thing: Unless the processAndUpdateFeature or initDefaultValues or cookieReceived can throw an error, the try-catch block seems unnecessary.
In my experience I have had best luck using AsyncTask for network operations.
There are many advantages. 1) Use publish progress to show progress bar advancement. 2) You can have a single place to handle errors in a consistent way while also making the 'success' flow do different things. 3) AsyncTask is an Android construct so you have a good chance of it working consistently between versions.
I have the following code,
#Singleton
class LoginHandler {
private Observable<Response<User>> loginObservable;
private void attemptLogin(LoginRequest loginRequest) {
LoginSubscriber loginSubscriber = new LoginSubscriber();
loginObservable = api.login(loginRequest);
loginObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(loginSubscriber);
}
}
with
#POST(PATH_AUTH)
Observable<Response<User>> login(#Body LoginRequest loginRequest);
This gets executed each time the user wants to login from a deep link.
I can log in and log out two times, but on the third time, the request just does not get executed. I also checked with the Charles Proxy tool, if the request is actually sent, but there was nothing to see.
Logging the loginObservable object shows that each api.login() call returns a different Observable object, which is correct for the subscription to be executed. Logging at doOnSubscribe(() -> ...) is also executed, just not the request itself.
I've browsed through SO and the only solution I found was to add a timeout(...) so that the user is able to get back to the login mask, once the timeout strikes. However when retrying to log in, the problem is still present.
So my question is, how could I debug this and find out, what is happening under the hood of retrofit and why is the request not executed after the third attempt?
Thank you very much in advance
I have tried to use retrofit 2.1.0 for my image upload call in multipart. But the issue that i m facing is call is requested so server side the update is done but in the way of response if internet connection gets off it jumps to onFailure() method even though call was successful.Hope you understand my issue here.Please help me to solve this.Your help will be appreciated.
In case that happens make the user try again..... in the application side send a tag with the file like presentFileTag and in the server side ``
if(previousFileTag.equals(presentFileTag)
{
if(fileUploaded)
{
//send response file already present or posetive response
}
else
{
//Store the file
}
}
else
{
//Store the file
}
I am using mockwebserver to mock request and response for my android app. I am testing a login feature which goes through a series of 4 service calls.
Get access token
Re-direct
Get user info (different base url)
Get some other stuff (original base url)
I am trying to mock the response of the redirected call. Here is my code:
#Test
public void testSuccessfulLogin() throws Exception {
// Post
server.enqueue(new MockResponse()
.setResponseCode(HTTP_OK)
.setBody(getStringFromFile(getInstrumentation().getContext(), "access_token.json")));
// Redirect
server.enqueue(new MockResponse().setResponseCode(HTTP_MOVED_TEMP));
// GET user info
server.enqueue(new MockResponse().setResponseCode(HTTP_OK).setBody(getStringFromFile(getInstrumentation().getContext(), "userinfo.json")));
// GET some other stuff
server.enqueue(new MockResponse().setResponseCode(HTTP_OK)
.setBody(getStringFromFile(getInstrumentation().getContext(), "sts.json")));
// Init call
loginWithoutWaiting(Data.serviceLoginUsername, Data.serviceLoginPassword);
// Debug (need to loop 4 times to get all 4 call paths)
RecordedRequest request = server.takeRequest();
request.getPath();
}
My test fails at the Redirect code. I cannot login. I have found some hints here but I do not fully understand what is going on, thus can't make it work at the moment.
It turned out to be quite easy. In the call that makes redirect, create a new mocked response with response code 302 and header with location url. The next call will use that location url.
case "/userinfo":
return new MockResponse().setResponseCode(HTTP_MOVED_TEMP).setHeader("Location", "/api-test.com/users");
case "/api-test.com/users":
return new MockResponse().setBody("{}")).setResponseCode(HTTP_OK);
I wan't to achieve next.
Depends on json content deside put or not to put data to Robospice Cache.
Sometimes data returned from the server is not valid. For example our authorization token goes off time. So we shouldn't cache this response.
But i can't find API which can help me to solve this trouble.
Here is how i am using requests now:
getSpiceManager().execute(getRequestCreator().getAllCategories(), getRequestCreator().getLastCacheKey(),
DurationInMillis.ONE_MINUTE * 120, new JSONCategoryListener(mCategories));
So the actual response is normal (status 200), but json content is telling me about exception.
So, what you want to get? It is right behavior for server. Server returns 200, means that request is successful. But it not guarantee, that wasn't some internal error of 'business logic' on server, such as not valid data or anything else.
EDITED
May be you can use your custom error handler:
class MyErrorHandler implements ErrorHandler {
#Override public Throwable handleError(RetrofitError cause) {
//check response on errors
}
}
And in createRestAdapterBuilder():
new RestAdapter.Builder()..setErrorHandler(new MyErrorHandler());
EDITED 2
You can implement in your robospice service method putDataInCache(Object cacheKey, T data), and in your listener check errors in content, and if no error then add it to cache, or remove it from cache