Basically in my android app I want user to search cities around the world, thus I am using an api to get all the cities of the world and storing in an ArrayList, this has been done in the onResponse method of okhttp library and after that the list becomes empty. This array list holds values only in onResponse but I want to use it in my entire class after the execution. Can anyone give me any ideas on that? Here is the code.
onCreate(){
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://raw.githubusercontent.com/David-Haim/CountriesToCitiesJSON/master/countriesToCities.json")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
}
#Override
public void onResponse(Response response) throws IOException {
try {
fullObject = new JSONObject(response.body().string());
JSONArray s = fullObject.names();
for(int i=0; i<s.length(); i++) {
JSONArray citiesOfOneCoutry = null;
citiesOfOneCoutry = fullObject.getJSONArray(s.getString(i));
for(int j=0; j<citiesOfOneCoutry.length();j++) {
allCities.add(citiesOfOneCoutry.getString(j));
}
Log.d(TAG, "onResponse: in for "+allCities.size());
}
Log.d(TAG, "onResponse: outside for "+allCities.size()); //gives full size.
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(TAG, "onResponse: outside try "+allCities.size()); //gives full size
}
});
Log.d(TAG, "outside response inside oncreate"+allCities.size()); //gives 0
}
I see in the logs that message from outside onResponse one is first and then the callback is getting executed. that is quite understandable but I want trick to get this ArrayList after response execution.
That is the nature of asynchronous operations, they don't complete in the order you wrote them. allCities data will not be available in your onCreate method because it hasn't had a chance to execute yet. The trick to using it outside of onResponse is to move the code that relies on the response to its own method.
private void updateUI() {
// Your code that relies on 'allCities'
}
and then in onResponse, call updateUI (or whatever you call it) after you populate allCities --
#Override
public void onResponse(Response response) throws IOException {
try {
fullObject = new JSONObject(response.body().string());
JSONArray s = fullObject.names();
for(int i=0; i<s.length(); i++) {
JSONArray citiesOfOneCoutry = null;
citiesOfOneCoutry = fullObject.getJSONArray(s.getString(i));
for(int j=0; j<citiesOfOneCoutry.length();j++) {
allCities.add(citiesOfOneCoutry.getString(j));
}
Log.d(TAG, "onResponse: in for "+allCities.size());
}
Log.d(TAG, "onResponse: outside for "+allCities.size()); //gives full size.
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(TAG, "onResponse: outside try "+allCities.size()); //gives full size
updateUI();
}
Related
I am trying to get the id numbers of foods from the USDA database. My issue is that I can get the JSON data, but I can not take it out of the OnPostCreate() function. When I iterate through JSONdataWIthIDs in the OnPostCreate() all the data is there. But when I try to use the data someplace else, it is empty. How do I store the data into an array?
Here is the constructor.
public USDA(String nameOfFoodToSearchFor) {
setJSONdataWIthIDs(nameOfFoodToSearchFor);
// This returns 0
Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size());
// This does not work.
for(String s: JSONdataWIthIDs)
{
Log.i("TAG OUTPUT", s);
}
}
Here is the code with the OnPostCreate.
private void setJSONdataWIthIDs(String nameOfFood) {
final String url1 = "https://api.nal.usda.gov/ndb/search/?format=json&q=" + nameOfFood + "&sort=n&max=25&offset=0&api_key=x5qM9v8PkjZrTf2cVSHzoK7y4GsSBgoQEmJsbwqV";
AsyncTask asyncTask = new AsyncTask() {
#Override
protected Object doInBackground(Object[] objects) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url1)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
return response.body().string();
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Object o) {
String lines[] = o.toString().split("\\r?\\n");
for(int i = 0; i < lines.length; i++)
{
JSONdataWIthIDs.add(lines[i]);
}
// This works
for(String s : JSONdataWIthIDs)
{
Log.i("TAG", s);
}
}
}.execute();
}
The problem is quite simple. The Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size()); statement is executed before the onPostExecute(). So the JSONdataWIthIDs is empty.
The call to async task is done asynchronously (doInBackground()). And you are saving the value to JSONdataWIthIDs in onPostExecute(). So you can access the id's only after the async task is done.
How do I store the data into an array?
The data is already stored into the array in onPostExecute(). The only problem is that you can access it only after the onPostExecute() is called.
onPostExecute is where the execution flow comes back from the worker thread (that is what supports AsyncTask) to the UI thread. Your log line,
Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size());
happens in the UI thread and is running in parallel with the working thread at that moment. So the result is not available.
I am trying to get information from my AsyncHttpClient in my Android app, and I need to use an interface to set the variable so I can use it in my main method. However, when I run System.out.println(PostResponse);, I am getting null.
I don't understand why, because if I put the line in my callback() method, I get the values.
From my main method:
try {
JSONArray PostResponse = PerformPostRequest(new OnJSONResponseCallback() {
#Override
public JSONArray onJSONResponse(boolean success, JSONArray response) {
System.out.println("Response: " + response); //This is returning the correct value
return response;
}
}, PostData);
System.out.println("Useable: " + PostResponse); //This is returning null.
} catch (Exception e) {
e.printStackTrace();
}
The interface:
public interface OnJSONResponseCallback {
public JSONArray onJSONResponse(boolean success, JSONArray response);
}
The AsyncHttpClient:
public JSONArray PerformPostRequest(final OnJSONResponseCallback callback, JSONObject PostData) {
//To authenticate against the API we need the user's credentials
String Email = getSharedPreferences(ctx).getString("Email","");
String Password = getSharedPreferences(ctx).getString("Password","");
final JSONArray[] ResponseStorage = new JSONArray[1];
//Add the credentials to post data
try{
PostData.put("email", Email);
PostData.put("password", Password);
} catch (Exception e){
e.printStackTrace();
}
//Then we need to put the post data into request parameters so we can send them in the call.
RequestParams RequestParameters = new RequestParams();
RequestParameters.put("data", PostData);
//This is the client we will use to make the request.
AsyncHttpClient client = new AsyncHttpClient();
client.post(AppHost + "MyMeetings.php", RequestParameters, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
String ResponseString = new String(responseBody);
ResponseStorage[0] = new JSONArray(ResponseString);
System.out.println(ResponseStorage[0] + "<============="); //Returns with the array
callback.onJSONResponse(true, ResponseStorage[0]);
} catch (Exception e) {
Log.e("Exception", "JSONException on success: " + e.toString());
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
try {
Toast.makeText(ctx, "Error: " + statusCode, Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.e("Exception", "JSONException on failure: " + e.toString());
}
}
});
JSONArray ResponseArray = new JSONArray();
try{
System.out.println(ResponseStorage[0] + "<==============="); //Returning null?
ResponseArray = ResponseStorage[0];
} catch (Exception e){
e.printStackTrace();
}
System.out.println("ResponseArray" + ResponseArray); //Returns null
return ResponseArray;
}
Where am I going wrong? I think it is something to do with my call in the main method.
Edit:
1) I tried to return the ResponseArray (set in onsuccess) but I can't return it from onsuccess because it is public void. When I tried to change it to public JSONArray, I get an incompatible return type error.
2) I have updated the method so it returns something other than null, however, it still returns as null, even when I am printing it inside the AsyncHttp.
The general idea behind the asynchronous calls is that:
the asynchronous method call (in your case PerformPostRequest) returns immediately and does not return the expected result - instead it returns either just an accept confirmation or an object from which you can sometimes in the future get the result (such as an instance of a Future)
you provide the method a callback interface (in your case OnJSONResponseCallback) or the method returns an instance of a callback interface, and you check regularly if there is already a result ready.
You should not expect that the asynchronous method returns the result immediately, this is exactly the opposite of asynchronous call.
Here is the rough idea expressed by pictures. It is just an overall picture of the whole idea, so the implementation details may be quite different!
I was trying to set the variable from the PerformPostRequest(), which by default does not get called. At the top of my class, I set a
public JSONArray[] PostResponse = new JSONArray[1];
and updated the bit where I was calling the post request to the following:
//Make a post request
try {
JSONObject PostData = new JSONObject();
PostData.put("action","test");
PerformPostRequest(new OnJSONResponseCallback(){
#Override
public JSONArray onJSONResponse(boolean success, JSONArray response) {
PostResponse[0] = response;
System.out.println(PostResponse[0]); //This will be replaced by calling the method I want to call.
return PostResponse[0];
}
}, PostData);
System.out.println(PostResponse[0]);
} catch (Exception e){
e.printStackTrace();
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
My application on Android LOLLIPOP runs well, but in the lower version, program crashe and displays this error:
Exception caught
java.lang.RuntimeException: An error occured while executing doInBackground()
And points to this line of code:
OkHttpClient client = new OkHttpClient();
this is my code:
private void getMoviesFromDBz(int id) {
AsyncTask<Integer, Void, Void> asyncTask = new AsyncTask<Integer, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Integer... movieIds) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(linkkk + movieIds[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
Movie movie = new Movie(object.getInt("id") , object.getString("per") , object.getString("movie_name"),
object.getString("movie_image"), object.getString("movie_genre") , object.getString("movie_discription") , object.getString("movie_lat"), object.getString("movie_lon") , object.getString("movie_marker") , object.getString("sort") , object.getString("price") , object.getString("email") , object.getString("tell") , object.getString("location") , object.getString("count"));
ItemOneFragment.this.movies2.add(movie);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
adapter2.notifyDataSetChanged();
getMoviesFromDB(0);
}
};
asyncTask.execute(id);
scroll2 = 1;
}
You're trying to access the Main/UI Thread in a background Thread:
ItemOneFragment.this.movies2.add(movie);
just return the movie object and execute the above line on the onPostExecute() method, also I wouldn't recommend instantiating an OkHttpClient for each request.
You're trying to performing action the on UI Thread from background Thread. So you can userunonuithread
runOnUiThread(new Runnable() {
#Override
public void run() {
ItemOneFragment.this.movies2.add(movie);
}
});
see the changes below in your code.
#Override
protected Void doInBackground(Integer... movieIds) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(linkkk + movieIds[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
Movie movie = new Movie(object.getInt("id") , object.getString("per") , object.getString("movie_name"),
object.getString("movie_image"), object.getString("movie_genre") , object.getString("movie_discription") , object.getString("movie_lat"), object.getString("movie_lon") , object.getString("movie_marker") , object.getString("sort") , object.getString("price") , object.getString("email") , object.getString("tell") , object.getString("location") , object.getString("count"));
runOnUiThread(new Runnable() {
#Override
public void run() {
ItemOneFragment.this.movies2.add(movie);
}
});
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
I think this may solve your problem.
You don't need any Asynctask with Okhttp
Look up examples that use the enqueue method
// Build the client and request in the main thread
// Start an asynchronous method
client.newCall(request).enqueue(new Callback() {
#Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
JSONObject json = new JSONObject(responseBody.string());
}
}
});
If you want to use JSON objects with Okhttp, Retrofit would be a better library
OK i am not sure if i am asking the right question, but this is the only way i can think of to fix my problem.
I have to call an API to get information that needs to be used inside of this method.
public void setBuses(JSONArray theBuses)
{
try {
for(int x = 0; x < theBuses.length() ; x++)
{
busExists = false;
if(x >= buses.size())
{
currentBus = new Bus();
}
else
{
busExists = true;
currentBus = buses.get(x);
currentMarker = busMarkers.get(x);
}
if(theBuses.getJSONObject(x).has("name"))
{
currentBus.setName(theBuses.getJSONObject(x).getString("name"));
}
if(theBuses.getJSONObject(x).has("driver"))
{
currentBus.setDriver(theBuses.getJSONObject(x).getString("driver"));
}
if(theBuses.getJSONObject(x).has("route"))
{
currentBus.setRoute(theBuses.getJSONObject(x).getString("route"));
}
if(theBuses.getJSONObject(x).has("active"))
{
currentBus.setActive(theBuses.getJSONObject(x).getBoolean("active"));
}
if(theBuses.getJSONObject(x).has("lastStop"))
{
currentBus.setLastStop(theBuses.getJSONObject(x).getString("lastStop"));
}
if(theBuses.getJSONObject(x).has("lastLong"))
{
currentBus.setLongi(theBuses.getJSONObject(x).getDouble("lastLong"));
}
if(theBuses.getJSONObject(x).has("lastLat")) {
currentBus.setLat(theBuses.getJSONObject(x).getDouble("lastLat"));
}
if(currentMarker != null)
{
currentMarker.remove();
}
if(currentBus.isActive())
{
BusStop nextStop = routes.getNextStop(currentBus.getLastStop(), currentBus.getRoute());
AsyncHttpClient client = new AsyncHttpClient();
client.setTimeout(5000);
client.get("https://maps.googleapis.com/maps/api/directions/json?origin="+currentBus.getLat()+"," +
""+currentBus.getLongi()+"&destination="+nextStop.getLat()+","+nextStop.getLongi()+"&departure_time=1541202457&traffic_model=best_guess&key=AIzaSyCADdN-VW0vFCKz4uWqdL97Idk8ezENfHk"
, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject distanceObject) {
setBusHelper(distanceObject);
}
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
}
});
}
if(busExists) //this needs to be moved to the helper function and the varibles x, currentBus, mMap, and nextStop need to be passed to that function
{
Bus temp = buses.set(x,currentBus);
Marker tempM = busMarkers.set(x,currentMarker);
}
else
{
buses.add(currentBus);
busMarkers.add(currentMarker);
}
}
} catch (JSONException ex) {
ex.printStackTrace();
}
}
The pull from google calls this method to get the data from the API
private void setBusHelper(JSONObject distanceObject) {
String timeToNextStop = "Unknown";
try {
timeToNextStop = distanceObject.getJSONArray("routes").getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("duration").getString("text");
currentMarker = mMap.addMarker(new MarkerOptions().position(new LatLng(currentBus.getLat(), currentBus.getLongi())).title("Time to Next Stop:" + timeToNextStop).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)));
} catch (JSONException e) {
e.printStackTrace();
}
}
The problem i am having is since the call is aSync i don't have the marker to store in my array. I need to be able to pass the information i need for the adding of the marker and storage of the array to the onSuccess method in the JSONhandler. I have no idea how do this and i cant find any examples online. Any help you can provide would be very appreciated.
I will mention i have tried using SyncHttpClient but it does not seem to work synchronously.
OK so the answer was to not use AsyncHttpClient at all. I used AsyncTask to call Apaches HTTP client which kept everything in the same thread and allowed me to make multiple calls. Program is working great now.
I have an onClickListener that uses Okhttp to asynchronously get some stuff in the background. Here's the OnClickListener:
mGetChartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String companyName = mSymbolValue.getText().toString();
getRequest(companyName, "chart");
Log.i(TAG, mChartProfile.getSizeDates()+""); // Null exception happens here
}
});
And here is the Okhttp snippet:
OkHttpClient client = new OkHttpClient();
Request request = new Request
.Builder()
.url(completeUrl)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
String jsonData = response.body().string();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
if (requestType.equals("quote")) {
isValidSearch = getQuote(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (isValidSearch) {
updateDisplay();
}
toggleFacts(isValidSearch);
}
});
}
else{
getChartInfo(jsonData);
}
} else {
alertUserAboutError();
}
} catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
} catch (JSONException e) {
Log.e(TAG, "JSONException caught: ", e);
Toast.makeText(BuyActivity.this, "oops!", Toast.LENGTH_LONG).show();
}catch (ParseException e){
Log.e(TAG, "Unable to parse", e);
}
}
});
// How do I do a thread.join() here?
private void getChartInfo(String jsonData) throws JSONException, ParseException{
JSONObject wholeChartData = new JSONObject(jsonData);
JSONArray dates = wholeChartData.getJSONArray("Dates");
mChartProfile = new ChartProfile();
// ChartProfile contains ArrayList of ChartDate and ArrayList of ChartValue
for (int i = 0; i < dates.length(); i++){
ChartDate chartDate = new ChartDate(dates.getString(i));
mChartProfile.addToDates(chartDate);
}
JSONArray values = close.getJSONArray("values");
for (int i = 0; i < values.length(); i++){
ChartValue chartValue = new ChartValue(values.getDouble(i));
mChartProfile.addToValues(chartValue);
}
}
Right now, I'm getting an error of thread exiting with uncaught exception. And this is caused by a null exception because when calling mChartProfile.getSizeDates(), the values haven't been written in yet. My intuition is that the call to getChartInfo(jsonData) doesn't finish and the main UI thread is already returning from the getRequest() function. Hence, it will continue next line, and try to access an empty array that has not been initialized. Hence, I get a null exception. My solution is to have the main thread wait on the worker thread by calling thread.join() but I am not sure of how to do this through this Okhttp interface. Any help is deeply appreciated.