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.
Related
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 phone contact numbers list stored in an array and called contactsString[]
and in an online database registered users numbers
I want to count how many registered users are there
and there is my code
for (i=0;i<contactsString.length-1;i++){
Phone phone=new Phone();
phone.phone=contactsString[i]
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
availableUsers++;
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
}
my problem is the web service response is delayed so it don't count and availableUsers is printed it's initial value which is 0
I would try better sending an array of Phone objects. In this way you would get the correct answer in 1 call.
I would never do this in the way you implemented: imagine you have 500 contacts: you will be doing 500 calls to your server. Now imagine you have 100000 users with 500 contacts each
Try to customize your api call in this format. Which uses async task class.
private void phoneContact() {
new AsyncTask<String,Void,String>() {
#Override
protected String doInBackground(String ... params) {
try {
Platform http = Url_Contacts;
JSONObject resp = http.search(what,where);
Log.d(TAG, "Response: " + resp.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return "";
}
}.execute();
}
Make sure that your service works well and the format of json with value status in there.
In onResponse, run on UIThread to update your View with the availableUsers.
The enqueue method is asynchronous. So your code should respect the multithreaded nature of it.
There are many approaches you can take:
Replace enqueue() method with execute(). But that makes all the calls synchronous. If you call it in UI Thread then whole app can stutter. Probably you will get NetworkOnMainThreadException. Not a good approach anyway.
Use RxAndroid or RxJava with Observer pattern.
Simple solution. Create a variable int callsFinished = 0;. In onResponse increment that variable. Then if that callsFinished == contactsString.length that means all calls have been done.
In your activity add a listener
void onAllCallsFinished(int availableUsers) {
//do what you want with availableUsers information
}
Call onAllCallsFinished(availableUsers) when callsFinished == contactsString.length.
There you can do what you want with that data. Update a view, call another service.
My app crashes because the images ArrayList is empty when I set the adapter, I figured that out by putting a toast message right after I parse my JSON request, and a Toast message after I initialize my adapter, "second" gets printed first on screen and the app crashes right after, does it have to do with my internet? Or am I missing something, here's my code, thanks!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_page);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mVolleySingleton = VolleySingleton.getInstance();
mRequestQueue = mVolleySingleton.getRequestQueue();
//First Toast message inside this method
sendAPIRequest();
//after you get the images
mCustomSwipeAdapter = new CustomSwipeAdapter(this, images);
//SECOND TOAST
Toast.makeText(getApplicationContext(), "Second", Toast.LENGTH_LONG).show();
mViewPager.setAdapter(mCustomSwipeAdapter);
mCustomSwipeAdapter.notifyDataSetChanged();
}
public void sendAPIRequest(){
String requestURL = "";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, requestURL, (String) null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
parseJSONResponse(response);
//FIRST TOAST : SHOULD BE CALLED FIRST
Toast.makeText(getApplicationContext(), "First", Toast.LENGTH_LONG).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
mRequestQueue.add(jsonObjectRequest);
}
public void parseJSONResponse(JSONObject response) {
if (response != null || response.length() != 0) {
try {
JSONObject GObject = response.getJSONObject("game");
String name = "N/A";
if (GObject.has("name") && !GObject.isNull("name")) { name = GObject.getString("name"); }
if (GObject.has("screenshots") && !GObject.isNull("screenshots")) {
JSONArray screenShotsArray = GObject.getJSONArray("screenshots");
for (int i = 0; i < screenShotsArray.length(); i++){
JSONObject screenshot = screenShotsArray.getJSONObject(i);
String screenshotURL = screenshot.getString("url");
images.add(screenshotURL);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Does it have to do with my internet? Or am I missing something ...
Both. It happens because you have a race condition.
From what I can make out, your images list is being populated asynchronously by the onResponse callback. Basically, that happens when your app gets the responses to the API requests that it is making. That is going to take at least milliseconds, and possibly seconds (or longer).
But your app is (so you say) crashing soon after the swipe adapter is registered, and the evidence is that the images list has not been populated.
There are three possibilities:
There is something wrong with the requests you are sending which is causing the API requests to not give you any response. (Hypothetically, you could have authentication wrong or something.)
The API requests are taking a long time because of internet connection speed, congestion, or the remote server being slow.
The API requests are taking a short time ... but the adapter registration is even quicker.
If (hypothetically) there is a problem with your requests you will need to fix that. But both of the other scenarios have to be fixed by:
modifying the code that uses the images to work properly if there are no images (yet), or
modifying the code to wait until the image loading has completed before registering the adapter.
Please use this code in your onResponse callback :
//after you get the images
mCustomSwipeAdapter = new CustomSwipeAdapter(this, images);
//SECOND TOAST
Toast.makeText(getApplicationContext(), "Second", Toast.LENGTH_LONG).show();
mViewPager.setAdapter(mCustomSwipeAdapter);
mCustomSwipeAdapter.notifyDataSetChanged();
Volley adds your requests in queue , so better do all the dependent tasks in Response or Error callback only.
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.