I have a problem with the waiting requests functionality in the volley library. The debugging led me to the AbstractQueue class in java.util where an element is being added (according to some values in the method that indicate a successful addition to the queue) and simultaneously - not being added(according to the 0 elements in the queue - that don't change their value). The adding method is synchronized. Bellow you can find a detailed description of the situation and my research so far. I will be really thankful if you have a look at them and share if you have any idea what is happening.
I try to automatically retry requests upon any kind of error ( for example - when there is no connection, or the server name is not correct ).
The error handler of a request adds the request back to the static singleton RequestQueue of my app.
RetriableRequestWraper.java
m_request = new StringRequest(
method,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
handleResponse(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
handleError(volleyError);
}
});
public void handleError(VolleyError volleyError)
{
Log.d("Request: ", m_request.toString());
Log.d("handleError: ", volleyError.toString());
if(retriesCount<3)
{
executeRequest();
++retriesCount;
}
else
{
retriesCount = 0;
}
}
public void executeRequest()
{
RequestsManager.getInstance().executeRequest(m_request);
}
public void executeRequest(Request request)
{
Log.d("executeRequest ","m_requestQueue.add(request)");
m_requestQueue.add(request);
}
RequestManager.java
public void executeRequest(Request request)
{
Log.d("executeRequest ","m_requestQueue.add(request)");
m_requestQueue.add(request);
}
This approach doesn't work and when debugging inside the volley library I come to the point where the request could not be added to the mCacheQueue of the RequestQueue class, because the cacheKey of the reuqest is present in the mWaitingRequests Map. So the request is added in the queue in mWaitingRequests map, corresponding to its key. When the previous request is finished - the new one is not added to the queue although these lines are being executed in the RequestQueue class:
synchronized(this.mWaitingRequests) {
String cacheKey1 = request.getCacheKey();
Queue waitingRequests1 = (Queue)this.mWaitingRequests.remove(cacheKey1);
if(waitingRequests1 != null) {
if(VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", new Object[]{Integer.valueOf(waitingRequests1.size()), cacheKey1});
}
this.mCacheQueue.addAll(waitingRequests1);
}
}
When debugging further this line
this.mCacheQueue.addAll(waitingRequests1);
In the AbstractQueue.java (class in java.util ) the element is being added to the queue, the "modified" value is true, but throughout the hole time the "this" parameter continues to contain 0 elements.
public boolean addAll(Collection<? extends E> c) {
if (c == null)
throw new NullPointerException("c == null");
if (c == this)
throw new IllegalArgumentException("c == this");
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
Inside the offer(E e) method of PriorityBlockingQueue.java the execution of the program stops at line 453.
l452 siftUpUsingComparator(n, e, array, cmp);
l453 size = n+1;
Obviously the returned value is true, but the element is not added. My debugger could not get into the method that adds the element - siftUpUsingComparator(n, e, array, cmp);
I am going to add a timer before retrying my request, and will construct a new one. So I am not really interested in a workaround, I want to understand what and how is happening in this situation. Do you have any idea as to what could be the reason behind this?
The issue is that you try to add the same Request instance once again to the queue it has been added to. This messes up with the queue and the Request itself as it has states. For example if you simply enable markers you'll have a crash. The solution is to either just use the default retry policy or clone the requests.
Related
So I'm completely lost on this one, it might be obvious solution or I'm just trying somethin that's not possible but here it is.
I have two classes one is being used as e listener class and second one is the one that handles queue(i will only include relevant code).
Handler class:
public void check() {
for (Queueable queueable : queue) {
if (!doesReceiverHavePlayers(queueable)) continue;
}
}
private boolean doesReceiverHavePlayers(Queueable queueable) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("PlayerCount");
out.writeUTF(queueable.getReceiver());
Player player = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
return /*response*/ > 0;
}
Listener class:
#Override
public void onPluginMessageReceived(String channel, #NotNull Player player, byte[] message) {
if (!channel.equals("BungeeCord")) return;
ByteArrayDataInput in = ByteStreams.newDataInput(message);
String subChannel = in.readUTF();
switch (subChannel) {
case "PlayerCount":
int response = in.readInt();
break;
}
}
The check method is called every 5 seconds and doesReceiverHavePlayers requests player count from a certain server to see if there are any players on it, but the 'response' arrives in the listener class onPluginMessageReceived method. But as you can see I'm trying to use response in the doesReceiverHavePlayers method and return boolean value. Is there any way I can achieve this and how should I do it?
In onPluginMessageReceived store the result in a ConcurrentHashMap and then lookup the value in doesReceiverHavePlayers instead of making a blocking call.
Something like this:
ConcurrentHashMap<String, Integer> playerCounts = new ConcurrentHashMap<>();
void onPluginMessageReceived() {
playerCounts.put(subChannel, response);
}
boolean doesReceiverHavePlayers() {
return playerCounts.get(queueable.getReceiver()) > 0;
}
I am making a chat app, checking if there are any new messages using a REST call. On a one second timer I am checking if the id of the last message in list is the same as the last id of newly downloaded list. If it isn't the same id (there are new messages) then update the recycerview. The problem is that it keeps on updating without any new messages and I am not sure why. Most likely it's a simple problem though i can't seem to find it.
Timer:
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
readMessages(myId, chatId);
}
}, 0, 1000);
REST call:
private void readMessages(String myId, String chatId) {
apiInterface = ApiClient.getClient().create(userApi.class);
Call<LinkedList<Messages>> call = apiInterface.getMessages(myId, chatId);
call.enqueue(new Callback<LinkedList<Messages>>() {
#Override
public void onResponse(Call<LinkedList<Messages>> call, Response<LinkedList<Messages>> response) {
mList.clear();
mList = response.body();
if (mList2.isEmpty() || mList2.getLast().getId().equals(mList.getLast().getId())) {
messageAdapter = new MessageAdapter(ChatActivity.this, mList, Integer.parseInt(myId));
recyclerView.setAdapter(messageAdapter);
mList2.clear();
mList2 = (LinkedList) mList.clone();
Toast.makeText(ChatActivity.this, mList2.getLast().getId(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<LinkedList<Messages>> call, Throwable t) {
}
});
}
The first part of your if statement is mList2.isEmpty() (I assume mList and mList2 are actually the same thing). A wild guess for a reason why each call to onResponse passes if the test would be that the list is actually empty. Try step-by-step debug and placing a breakpoint on the if line to check, and if so, take a look at your REST service in order to understand why it is responding with an empty list.
So in your code what exactly is supposed to happen if the condition is not met?
If see that there is an if statement. And let us assume we are not going into it because conditions are not met. So where is the else statement? What is the code supposed to do if the conditions don't match? As there is nothing else to be done in function, the control will return back from the function to the timer.
You can probably try putting timer inside the if statement, so it will only run when your conditions are met.
Do you think this was the problem?
I have an issue with android studio and the retrofit library and the way in which it processes the data.
I have a simple flow of operation I would like:
Request single item from database on server(fetch request)
Wait for callback to confirm it has been received by the app
Add another request(Loop)
Stop adding requests when all data is sent
The issue I have is my onResponse callback for my fetch result does not run until all my requests are sent. Then all the responses are errors. ( If I call a single item(1 from the database)) the call back runs fine.
How do I force it to send one request and wait until that response before sending another?
Loop code
private void Pull_data_loop(int total_entries){
//int current_data_point = 0;
boolean datum_processing = false;
for (int i = 1; i <= total_entries; i++) {
Add_single_datam(i);//Call until all entries are fetched from the server
}
}
Fetch code- Not running callback need to wait for this callback before sending next request
private void Add_single_datam(int id)
{
HashMap<String, String> map = new HashMap<>();
map.put("Id_request", Integer.toString(id));//The ID value
Call<Fetch_result> call = retrofitInterface.executeGet_data(map);//Run the post
call.enqueue(new Callback<Fetch_result>() {
#Override
public void onResponse(Call<Fetch_result> call, Response<Fetch_result> response) {
if (response.code() == 200)//Successful login
{
D1= response.body().getD1_String();
D2= response.body().getD2_String();
boolean result = BLE_DB.addData_Downloaded(D1, D2);//Add data
if (result == true) {
Log.d(TAG, "data_changes: Added data correctly");
}
if (result == false) {
Log.d(TAG, "data_changes: did not add data correctly");
}//false
} else if (response.code() == 404) {
Utils.toast(getApplicationContext(), "Get data fail");//Pass information to the display
}
}
#Override
public void onFailure(Call<Fetch_result> call, Throwable t) {
Utils.toast(getApplicationContext(), "Get data error");
}
});
}
Note:
I am using a node js server for my requests. I send the Id and it returns that Id in the database.
You could send a callBack instance to your Add_single_datam then in your retrofit response, send to that callback success.
Then in that callBack you would have iteravel i and you could see if you reached the end of total_entries added +1 in i and make request again, or just stop.
use some threading solutions like RxJava or Coroutines or AsyncTask. The reason it's not following the rule is because of there are two threads on which work is getting distributed so in order to get it make it work in sync, we have to use some threading solutions mentioned above and execute this for loop on the background thread and make it like a synchronous call and get all the results and finally switch back to main thread with the results.
If you are familiar with the AsynTask.
private class FetchDataTask extends AsyncTask<Int, Integer, List<Fetch_result>> {
protected Long doInBackground(Int... total_entries) {
List<Fetch_result> allResults = new ArrayList<Fetch_result>();
for (int i = 1; i <= total_entries[0]; i++) {
HashMap<String, String> map = new HashMap<>();
map.put("Id_request", Integer.toString(total_entries[0]));
Fetch_result response = retrofitInterface.executeGet_data(map).execute().body();
allResults.add(response);
}
return allResults;
}
protected void onProgressUpdate(Integer... progress) {
//show progress
}
protected void onPostExecute(List<Fetch_result> result) {
//do something on main thread, in loop on result
D1= result[0].getD1_String();
D2= result[0].getD2_String();
boolean result = BLE_DB.addData_Downloaded(D1, D2);//Add data
if (result == true) {
Log.d(TAG, "data_changes: Added data correctly");
}
if (result == false) {
Log.d(TAG, "data_changes: did not add data correctly");
}//false
}
}
now call like this.
new FetchDataTask().execute(total_entries);
I have problem with loop. In my opinion this should work, but goes one through the loop. I have to do check availability server and I don't have other ideas.
Server give answer:
{"exist":true}
do {
generateNumber = generate();
getExsistResponse = "http://skidd.herokuapp.com/exist/" + generateNumber;
final StringRequest request = new StringRequest(Request.Method.GET, getExsistResponse, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
existObject exist = new Gson().fromJson(response, existObject.class);
exist.getExist();
String abcd = exist.getExist();
Boolean boolean1 = Boolean.valueOf(abcd);
if (boolean1) {
Log.i("Info", "Pokój: " + abcd);
textView.setText("" + boolean1);
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_SHORT).show();
} else {
textView.setText("" + boolean1);
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//When an error
}
});
ConnectionManager.getInstance(this).add(request);
//Add the request to the RequestQueue.
}while (boolean1);
and here is my existObject class:
public class existObject {
#SerializedName("exist")
private String checkexist;
public String getExist() {
return checkexist;
}
public void getExist(String name) {
this.checkexist = name;
}
}
The logic of this approach is not sound. You have made the assumption that the do-while loop checks the value of boolean1 after each web-service call completes. In reality, what happens is that even before the first web-service call has completed, the do-while loop has already moved on to the next iteration of the loop, and boolean1 is probably still false, because the first web-service call has not yet been completed. So the do-while loop breaks.
Realize this: a web-service call is asynchronous. You simply cannot predict how much time it will take to complete, and the time required for each web-service call to complete will always vary. Your do-while loop, on the other hand, is synchronous: it keeps on creating StringRequest objects and adding them to the RequestQueue, and these requests are not executed immediately, so the value of boolean1 may or may not have been set correctly for the next iteration of the do-while loop.
You need to find another way of making successive network calls such that they are not dependent on each other in this way. If you still want to do it like this and depend on boolean1, try this:
Create an AsyncTask and perform an HttpURLConnection request (and
not a Volley Request) in its doInBackground(), and set the value
of boolean1 there.
If the value of boolean1 is true, then create a new instance of
the same AsyncTask in onPostExecute() and call execute() to
start the next web-service call.
(That title alone should cause people to come out of the woodwork to bash me with clubs, but hear me out).
I have a use case where I need to return a value from a asynchronous call. (I'm using GWT-Platform, but the concepts are the same.) I declared a final JavaScriptObject array, then assigned the value within the AsyncCallback. However, I need to return the value, and the method returns before the AsyncCallback completes. Therefore, I need to block somehow until the AsyncCallback completes. I need the returned value in another method, or I'd just do what I need to in onSuccess().
I've tried loops, Timers, and a few other methods with no luck. Can anyone help?
#Override
public JavaScriptObject doGetWhereAmIMarker(final double lng, final double lat) {
final JavaScriptObject[] markerArray = new JavaScriptObject[1]; // ugly hack, I know
dispatch.execute(new GetLocationDescriptionsAction(lng, lat), new AsyncCallback<GetLocationDescriptionsResult>() {
#Override
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
#Override
public void onSuccess(GetLocationDescriptionsResult result) {
Map<String, Location> resultMap = result.getResult();
StringBuffer message = new StringBuffer();
for (String key : resultMap.keySet()) {
message.append(key).append(": ").append(resultMap.get(key)).append("\n");
}
Map tempMap = new HashMap();
tempMap.put("TITLE","Location Information");
tempMap.put("LAT", lat);
tempMap.put("LNG", lng);
tempMap.put("CONTENTS", message.toString());
JavaScriptObject marker = GoogleMapUtil.createMarker(tempMap);
markerArray[0] = marker;
if (markerArray[0] != null) {
GWT.log("Marker Array Updated");
}
}
});
return markerArray[0];
}
UPDATE: As requested, here is the code that calls doGetWhereIAmMarker(). I've tried having a separate native method with the Google Map object (as a JavaScriptObject) as a parameter, but it appears that passing that object between native methods kills the ability to update said object.
public native void initMap(JavaScriptObject mapOptions, JavaScriptObject bounds, JavaScriptObject border, JsArray markerArray, Element e) /*-{
// create the map and fit it within the given bounds
map = new $wnd.google.maps.Map(e, mapOptions);
if (bounds != null) {
map.fitBounds(bounds);
}
// set the polygon for the borders
if (border != null) {
border.setMap(map);
}
// set up the info windows
if (markerArray != null && markerArray.length > 0) {
var infoWindow = new $wnd.google.maps.InfoWindow({
content:"InfoWindow Content Goes Here"
});
for (var i = 0; i < markerArray.length; i++) {
var marker = markerArray[i];
marker.setMap(map);
$wnd.google.maps.event.addListener(marker, 'click', function() {
infoWindow.setContent(marker.content);
infoWindow.open(map, this);
});
}
}
// need to reference the calling class inside the function(), so set a reference to "this"
var that = this;
$wnd.whereAmI=function(lng, lat) {
that.#org.jason.mapmaker.client.view.MapmakerMapViewImpl::whereAmI(DD)(lng,lat);
}
$wnd.google.maps.event.addListener(map, 'click', function(event) {
var lat = event.latLng.lat();
var lng = event.latLng.lng();
$wnd.whereAmI(lng, lat);
});
}-*/;
At some point I had to do something similar but eventually I eliminated that code in favor of asynchronous stuff. Therefore, I can't give exact code that you need to use but only few pointers on how to approach it.
Firstly, this blog describes how to do synchronous AJAX using javascript.
Second, you must provide support for sync calls. The problem is that GWT does not support the parameter that provides synchronous AJAX calls. Most likely is that they don't want to encourage its use. Therefore you would need to use JSNI to add appropriate method to XMLHttpRequest (which you probably would extend) and then to RequestBuilder (also should extend it).
Finally, amend your service using extended RequestBuilder. Something like
((ServiceDefTarget)service).setRpcRequestBuilder(requestBuilder);
And in conclusion - from the same blog post (slightly out of context):
Because of the danger of a request getting lost and hanging the browser,
synchronous javascript isn't recommended for anything outside of
(onbefore)unload event handlers.
I Think its all fate....
We cannot do in Gwt to catch the Response and Send it, because immediately after the request is send the next method starts executing, neither bothering the response
Still however they satisfy us to use the Timers, is what i think...
Timer t = new Timer() {
#Override
public void run() {
Window.alert("Nifty, eh?");
}
};
t.schedule(5000);