3

I am trying to call functions in a chronological order but functions parseJSONTwo() seem to be executing before function parseJSON() (both functions just fetch JSON data from URL)

package com.example.rechev;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class pratim extends AppCompatActivity {

    RequestQueue queue;
    TextView degem;
    String model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pratim);
        degem = findViewById(R.id.pratim);
        queue = Volley.newRequestQueue(this);
        parseJSON();
        parseJSONTwo();
    }


    public void parseJSON() {
        Intent intent = getIntent();
        int number = intent.getIntExtra("inumber", 1234567);
        String url = "http://apiurl.com" + number}";
        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject result = response.getJSONObject("res");
                    JSONArray jsonArray = result.getJSONArray("rec");
                    String test = result.getString("total");

                    if (Integer.parseInt(test) == 1) {

                        for (int i = 0; i < jsonArray.length(); i++) {

                            JSONObject number = jsonArray.getJSONObject(i);

                            String carMaker = number.getString("toze");

                            degem.append(String.valueOf("\n maj: " + carMaker));

                            model = number.getString("degem_nm");

                            if (carGimur.length() >= 1) {
                                degem.append(String.valueOf("\n type: " + carGimur));
                            }
                        }
                    } else {
                        degem.append("invalid");
                    }
                } catch (JSONException e) {
                    degem.append(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                degem.append("Error");
            }
        });

        queue.add(request);
    }


    public void parseJSONTwo() {
        String url = "http://apiurl.com"+model}";
        JsonObjectRequest requestt = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject result = response.getJSONObject("res");
                    JSONArray jsonArray = result.getJSONArray("rec");
                    String test = result.getString("total");

                    if (Integer.parseInt(test) == 1) {

                        for (int i = 0; i < jsonArray.length(); i++) {

                            JSONObject number = jsonArray.getJSONObject(i);

                            String carNefah = number.getString("nefah_manoa");

                            degem.append(String.valueOf("num: " + carNefah));
                         }
                    }
                } catch (JSONException e) {
                    degem.append(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                degem.append("Error");
            }
        });

        queue.add(requestt);
    }
}


the function parseJSON() fetches JSON data from url and puts it into a TextView, And it also sets a global variable (model) a value, which parseJSONTWO() uses in its URL so it can fetch data based on the first parseJSON() function.

The problem is parseJSONTwo() is being called first for some reason, so it can't use the information parseJSON() retrieves.

Updated code:

package com.example.rechev;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class pratim extends AppCompatActivity {

    RequestQueue queue;
    TextView degem;
    String model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pratim);
        degem = findViewById(R.id.pratim);
        queue = Volley.newRequestQueue(this);
        parseJSON();

    }


    public void parseJSON() {
        Intent intent = getIntent();
        int number = intent.getIntExtra("inumber", 1234567);
        String url = "http://apiurl.com" + number}";
        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject result = response.getJSONObject("res");
                    JSONArray jsonArray = result.getJSONArray("rec");
                    String test = result.getString("total");

                    if (Integer.parseInt(test) == 1) {

                        for (int i = 0; i < jsonArray.length(); i++) {

                            JSONObject number = jsonArray.getJSONObject(i);

                            String carMaker = number.getString("toze");

                            degem.append(String.valueOf("\n maj: " + carMaker));

                            model = number.getString("degem_nm");

                            if (carGimur.length() >= 1) {
                                degem.append(String.valueOf("\n type: " + carGimur));
                            }
                        }
                    } else {
                        degem.append("invalid");
                    }

                  if(model.length() >= 1){
                      parseJSONTwo();
                   }
                } catch (JSONException e) {
                    degem.append(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                degem.append("Error");
            }
        });

        queue.add(request);
    }


    public void parseJSONTwo() {
        String url = "http://apiurl.com"+model}";
        JsonObjectRequest requestt = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject result = response.getJSONObject("res");
                    JSONArray jsonArray = result.getJSONArray("rec");
                    String test = result.getString("total");

                    if (Integer.parseInt(test) == 1) {

                        for (int i = 0; i < jsonArray.length(); i++) {

                            JSONObject number = jsonArray.getJSONObject(i);

                            String carNefah = number.getString("nefah_manoa");

                            degem.append(String.valueOf("num: " + carNefah));
                         }
                    }
                } catch (JSONException e) {
                    degem.append(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                degem.append("Error");
            }
        });

        queue.add(requestt);
    }
}


now parseJSONTwo will get the correct URL but will not complete the function.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
Skity
  • 131
  • 1
  • 10
  • Please provide a complete example. – swpalmer Oct 11 '19 at 18:28
  • 2
    My guess is that it's doing async requests inside parseJSON methods. If these async methods aren't waited then you don't know when the callback will execute – damian Oct 11 '19 at 18:29
  • 1
    Looks to me its an asynch call in `parseJSON()` that hasn't completed when `parseJSONTWO()` is called. See https://stackoverflow.com/questions/52509120/volley-jsonobjectrequest-response – Richard Chambers Oct 11 '19 at 18:41
  • @RichardChambers Thank you, not sure if I got it correctly but I just moved ```parseJSONTwo``` to ```parseJSON``` on response method, now it does set the variable ```model``` correctly but for some reason, it goes to ```parseJSONTwo``` requestt object, jumps to ```queue.add(requestt)``` method and then just back to where it left on ```parseJSON``` – Skity Oct 11 '19 at 19:01
  • Your post is similar to https://stackoverflow.com/questions/39521956/callback-hell-sequencing-restful-volley-requests-rxandroid/ which wants to chain a series of async network retrieval functions. An answer mentions `flatmap` so see this https://stackoverflow.com/questions/33471526/why-do-we-need-to-use-flatmap My impression is that you need to be able to chain a sequence of asynch calls and you need a method of waiting until the entire chain is complete before moving forward with whatever is done after `OnCreate()` is completely finished including the pending asynch requests. – Richard Chambers Oct 11 '19 at 20:15
  • See also use of `addRequestFinishedListener()` and an `AtomicInteger` to count requests started and finished in this post https://stackoverflow.com/questions/17719225/callback-if-volley-requestqueue-is-done-with-all-its-tasks/ as well as https://stackoverflow.com/questions/25602298/how-to-check-volley-request-queue-is-emptyand-request-is-finished – Richard Chambers Oct 11 '19 at 20:36
  • @RichardChambers Okay, so since yesterday I've been trying a lot of things and nearly lost my mind as to why it isn't working, and only now after tons of debugging I figured it out and I can't even describe how stupid I feel. the initial solution did work, but I accidentally copied the ```if``` statement comparing ```test == 1``` to ```parseJSONTwo``` as well, but it holds true only for ```parseJSON``` so despite it working correctly it just skipped the entire code in ```parseJSONTwo``` . I feel so dumb, but thank you for the inital solution! – Skity Oct 12 '19 at 09:22
  • I'm glad that you figured it out and that it is working now. I've never used Volley, didn't know it existed, and it was interesting to see the basics of how it is used and how it works. Microsoft has something along the same lines with its Universal Windows Program framework which uses asynch programming along with coroutines and I've spent time with that and I've made the same mistake of not chaining the asynch functions together properly. It can be a real hard looking problem that when you figure it out seems so obvious. – Richard Chambers Oct 12 '19 at 14:55

1 Answers1

2

In each of those functions, parseJSON() and parseJSONTWO(), you're making asynchronous network requests. A network request is not immediate, taking anywhere from a few milliseconds to tens of milliseconds which is much slower than your program executes. Since the network requests take some time to resolve the corresponding onResponse() functions are also delayed and fire when the related requests return.

Thus, the order of operations in your code is

  1. call parseJSON() which starts a GET request that make take a while
  2. call parseJSONTwo() which starts another GET request without waiting on the response from the GET request of parseJSON() and that may take a while as well

The function parseJSONTwo() is called immediately after the function parseJSON() returns after starting the GET request, which may not yet have completed since the GET request may take a while. So the GET request of parseJSON() may still be in process and without a response when the function parseJSONTwo() is called to start the next GET request.

When each of the requests returns, its onResponse() callback will fire. However the order that they return is not specified. It will depend on the server and network conditions and how long each request takes to be completed.

And since the function parseJSONTwo() requires the response of the function parseJSON(), you have to chain these functions so that the second function, parseJSONTwo() is called only when the response of the first function's GET request is received.

One way to chain these functions is to call the function parseJSONTwo() in the OnResponse() handler of the function parseJSON().

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
alttag
  • 1,163
  • 2
  • 11
  • 29
  • I see, so how do continue to ``parseJSONTwo``` only after ```parseJSON``` is complete? – Skity Oct 11 '19 at 19:39
  • i agree, maybe @Skity can look at my post here for some info on async operations : https://stackoverflow.com/questions/57330766/how-to-get-data-from-any-asynchronous-operation-in-android – a_local_nobody Oct 11 '19 at 19:43
  • You are correct, the solution I found was just to call ```parseJSONTwo``` at the end of ```parseJSON``` onReponse, that way it executes all the code in ```parseJSON``` and then goes on to ```parseJSONTwo``` – Skity Oct 12 '19 at 09:25