The following code creates a background task and executes it.
String dateString = null;
if (dateSelected)
dateString = Utils.parseDateToMsTimestamp(selectedDate);
final String ori = originCode;
final String dest = destinationCode;
RequestScheduleTask requestScheduleTask = new RequestScheduleTask();
requestScheduleTask
.execute(ori, dest, dateString);
originCode and destinationCode are instance variables.
The following is what the background task does.
private class RequestScheduleTask extends
AsyncTask<String, Void, List<CUSchedule>> {
#Override
protected List<CUSchedule> doInBackground(String... args) {
List<CUSchedule> cuSchedules = null;
try {
cuSchedules = CURestCommunicator
.requestSUScheduleByOriginAndDestination(args[0],
args[1], args[2]);
} catch (NetworkException e) {
}
return cuSchedules;
}
#Override
protected void onPostExecute(List<CUSchedule> result) {
if (result == null) {
raiseError("Server Error");
}
InnoBusApplication innoBusApplication = (InnoBusApplication) getApplication();
innoBusApplication.setCuSchedules(result);
super.onPostExecute(result);
}
}
The following is part of what the http call does.
public static List<CUSchedule> requestSUScheduleByOriginAndDestination (
String origin, String destination, String date) throws NetworkException {
Log.d("upload", "up");
origin = Utils.shortNameForCity(origin);
destination = Utils.shortNameForCity(destination);
HttpClient client = null;
String url = "http://" + SVR + "/innobussvr/BusSchedulesSearchByOrgDestStartTimeEndTime/"
+ origin + "/" + destination;
Log.d("url", url);
...
}
The following is the URL that results.
http://192.168.0.150/innobussvr/BusSchedulesSearchByOrgDestStartTimeEndTime/null/null
I understand that this is a thread visibility problem. How can I solve it?
Simple Solution just create a Constractor for RequestScheduleTask that takes 2 String and pass them then create inside the Task class 2 local string var.
Related
I need to submit a sequence of API calls where the response of the first call contains the API address needed for the second call. I then want to send the results of the second call back into MainActivity. Additionally, each response is updated over time as results of those calls are returned.
I have set up my code to use a sequence of AsyncTasks with success and fail criteria to keep them active. Despite researching practically every article I can on this, none of them seem to show how to reference and parse a returned variable from the execute() method in the code instead of directly updating the GUI. I also need to do all of this without leaking memory or blocking the main threads.
class CheckItemStatus extends AsyncTask<Void, Void, String> {
private final String itemName;
String mResponse = "";
String submitReportURL = "stackoverflow.com";
public CheckItemStatus(String itemName) {
this.itemName = itemName;
}
protected String doInBackground(Void... urls) {
new RetrieveFeedTask(submitReportURL).execute();
return mResponse;
}
protected void onPostExecute(String response) {
super.onPostExecute(response);
String reportURLResponse = "";
// TODO: Retrieve new response
Log.i("APP", "Item check submitted! Response: " + reportURLResponse);
String reportURL = ""; // TODO: Parse new response for report URL
new CheckReportResults(itemName, reportURL).execute();
}
}
class CheckReportResults extends AsyncTask<Void, Void, String> {
private final String itemName;
private final String reportURL;
String mResponse = "";
public CheckReportResults(String itemName, String reportURL) {
this.itemName = itemName;
this.reportURL = reportURL;
}
protected String doInBackground(Void... urls) {
Log.i("APP", "Waiting for results for item " + itemName);
new RetrieveFeedTask(reportURL).execute();
return mResponse;
}
protected void onPostExecute(String response) {
super.onPostExecute(response);
}
}
class RetrieveFeedTask extends AsyncTask<Void, Void, String> {
String URL;
public RetrieveFeedTask(String URL) {
this.URL = URL;
}
protected String doInBackground(Void... urls) {
String response="{\n" +
" \"reportURL\": \"stackoverflow.com\"\n" +
"}";
// Open Connection and Get Response
return response;
}
protected void onPostExecute(String response) {
super.onPostExecute(response);
if (response == null) {
response = "THERE WAS AN ERROR";
}
Log.i("INFO", "Response: " + response);
}
}
Currently, the code runs, but I have no idea how to parse and reference those responses outside of the AsyncTask class.
Application fetches data from OpenWeatherMap API and works correctly when I indicate a city, e.g. "London". There is an error when I replace "London" with a variable "place".. Underneath you can find the code.
Can anyone help me?
**MAIN ACTIVITY**
public class MainActivity extends AppCompatActivity {
//create the fields on the Activity
private TextView cityName;
private TextView temp;
private TextView description;
private TextView humidity;
private TextView pressure;
private TextView wind;
private TextView sunrise;
private TextView sunset;
private TextView updated;
private Exception error;
Weather weather = new Weather();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//set up all the resources for the Views here
cityName = (TextView)findViewById(R.id.cityText);
temp = (TextView)findViewById(R.id.tempText);
description = (TextView)findViewById(R.id.CloudText);
humidity = (TextView)findViewById(R.id.HumidText);
pressure = (TextView)findViewById(R.id.PressureText);
wind = (TextView)findViewById(R.id.WindText);
sunrise = (TextView)findViewById(R.id.RiseText);
sunset = (TextView)findViewById(R.id.SetText);
updated = (TextView)findViewById(R.id.UpdateText);
//instantiate a CityPreference object from the MainActivity that
carries the city with it..
//render the data based on the city you get through the parsing
CityPreference cityPreference = new CityPreference(MainActivity.this);
renderWeatherData(cityPreference.getCity());
}
public void renderWeatherData (String city){
// we want to do all of our tasks in the background, use AsyncTask for this
WeatherTask weatherTask = new WeatherTask();
weatherTask.execute(new String[]{city} + "&units=metric");
}
//last parameter is Weather, this is what we will be populating
private class WeatherTask extends AsyncTask<String, Void, Weather>{
//AsyncTask <PARAMETERS, TASK, RESULT>
#Override
protected Weather doInBackground(String... params) {
//background computation
//data variable holds all the data that we have got..
//instantiate the weather client and get the weather data..
//getWeatherData gets all necessary data from HTTPClient
//getWeather parses all the data from the JSONParser
try {
String data = ((new WeatherHTTPClient().getWeatherData(params[0])));
weather = JSONWeatherParser.getWeather(data);
//create a log to test if everything is working
Log.v("Data:",weather.currentCondition.getDescription());
return weather;
} catch (Exception e) {
error = e;
}
return null;
}
//here you will populate the data so you can show it to the user
#Override
protected void onPostExecute(Weather weather) {
super.onPostExecute(weather);
try {
//gets time and decimal formats and applies it to the data underneath
DateFormat df = DateFormat.getTimeInstance();
String sunriseDate = df.format(new Date(Weather.place.getSunrise()));
String sunsetDate = df.format(new Date(Weather.place.getSunset()));
String updateDate = df.format(new Date(Weather.place.getLastupdate()));
DecimalFormat decimalFormat = new DecimalFormat("#.#");
String tempFormat = decimalFormat.format(weather.currentCondition.getTemperature());
//gets the value from the JSON and parses it in the view of the activity
cityName.setText(weather.place.getCity() + "," + weather.place.getCountry());
temp.setText("" + tempFormat + "C");
humidity.setText("Humidity" + weather.currentCondition.getHumidity() + "%");
pressure.setText("Pressure" + weather.currentCondition.getPressure() + "hPa");
wind.setText("Wind" + weather.wind.getSpeed() + "mps");
sunrise.setText("Sunrise" + sunriseDate);
sunset.setText("Sunset" + sunsetDate);
updated.setText("Updated" + updateDate);
description.setText("Condition" + weather.currentCondition.getCondition() + "(" + weather.currentCondition.getDescription() + ")");
} catch(Exception e) {
error = e;
}
}
}
//create an alert dialog to put in the city you want to parse the data for - A subclass of Dialog that can display one, two or three buttons.
private void showInputDialog () {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Change City");
final EditText cityInput = new EditText(MainActivity.this);
cityInput.setInputType(InputType.TYPE_CLASS_TEXT);
cityInput.setHint("put down city name");
builder.setView(cityInput);
builder.setPositiveButton("Submit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which){
CityPreference cityPreference = new CityPreference(MainActivity.this);
cityPreference.setCity(cityInput.getText().toString());
String newCity = cityPreference.getCity();
renderWeatherData(newCity);
}
});
builder.show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//inflate the menu, this adds items to the action bar if it is present
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.change_cityId){
showInputDialog();
}
return super.onOptionsItemSelected(item);
}
}
**HTTP CLIENT**
public class WeatherHTTPClient {
public String getWeatherData (String place) throws IOException {
// setting things up.., create connection between application and the web, everything we get from the web comes as an inputstream
HttpURLConnection connection = null;
InputStream inputStream = null;
// api.openweathermap.org/data/2.5/weather?q=London
// errors may occur when you connect to the internet, build this in the model with try...catch
WHEN I REPLACE "place" by "London" in the next statement, the application works. THERE MUST BE AN ERROR WITH THIS PART, BUT I CANNOT FIND IT.. I NEED HELP..
try {
connection = (HttpURLConnection) (new URL(Utils.BASE_URL + place +"&APPID=f77c39703fb6be71e2c5a96e58edc077")).openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.connect();
//read the response, buffer "bucket" where you are going to put your data
StringBuffer stringBuffer = new StringBuffer();
//you get a stream of bits and data to your device - everything comes in as an inputstream
inputStream = connection.getInputStream();
//BufferedReader is the only thing that can read the stream of data - hold it and start reading
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
//line is set as the data read by the BufferedReader, Stringbuffer reads the Buffer and goes to the next one
while ((line = bufferedReader.readLine()) != null){
stringBuffer.append(line + "\r\n");
}
inputStream.close();
connection.disconnect();
return stringBuffer.toString();
} catch (IOException e) {e.printStackTrace();}
return null;
}
}
**WEATHER DATA MODEL JSON**
public class Weather {
public static Place place;
public String IconData;
public CurrentCondition currentCondition = new CurrentCondition();
public Temperature temperature = new Temperature();
public Wind wind = new Wind();
public Snow snow = new Snow();
public Clouds clouds = new Clouds();
}
**PLACE DATA MODEL, left out getters and setters**
**LEFT OUT THE FULL DATA MODEL**
public class Place {
private float lon ;
private float lat ;
private long sunset;
private long sunrise;
private String country;
private String city;
private long lastupdate;
**THE WEATHER PARSER**
public class JSONWeatherParser {
public static Weather getWeather(String data){
//we call the top JSON object weather
Weather weather = new Weather();
//create JSONObject that holds all of the data you get from the web
//place is the parameter you use to get all the data for
//the rest of the variables you have to "calculate" from your data model
//tagName needs to be EXACTLY as it is in the JSON
try {
JSONObject jsonObject = new JSONObject(data);
Place place = new Place();
//for each "header" in JSON you create a JSONObject or a JSONArray
JSONObject coorObj = Utils.getObject("coord", jsonObject);
//you set the latitude by getting the "lat" variable from the coorObj from the top JSON Object weather
place.setLat(Utils.getFloat("lat",coorObj));
place.setLon(Utils.getFloat("lon",coorObj));
//get the sys object
JSONObject sysObj = Utils.getObject("sys", jsonObject);
place.setCountry(Utils.getString("country", sysObj));
//dt Lastupdate is found directly under the jsonObject, hence ...
place.setLastupdate(Utils.getInt("dt", jsonObject));
place.setSunrise(Utils.getInt("sunrise",sysObj));
place.setSunset(Utils.getInt("sunset",sysObj));
place.setCity(Utils.getString("name",jsonObject));
weather.place = place;
//get the weather info, it is a JSONArray of the main jsonObject (the whole thing) - starts with square brackets
JSONArray jsonArray = jsonObject.getJSONArray("weather");
//get the underlying Jsonobject from the jsonarray
JSONObject jsonWeather = jsonArray.getJSONObject(0);
weather.currentCondition.setWeatherId(Utils.getInt("id",jsonWeather));
weather.currentCondition.setDescription(Utils.getString("description", jsonWeather));
weather.currentCondition.setCondition(Utils.getString("main",jsonWeather));
weather.currentCondition.setIcon(Utils.getString("icon",jsonWeather));
JSONObject mainObj = Utils.getObject("main", jsonObject);
weather.currentCondition.setHumidity(Utils.getInt("humidity",mainObj));
weather.currentCondition.setPressure(Utils.getInt("pressure",mainObj));
weather.currentCondition.setMin_temp(Utils.getFloat("temp_min",mainObj));
weather.currentCondition.setMax_temp(Utils.getFloat("temp_max", mainObj));
weather.currentCondition.setTemperature(Utils.getDouble("temp",mainObj));
JSONObject windObj = Utils.getObject("wind", jsonObject);
weather.wind.setSpeed(Utils.getFloat("speed", windObj));
weather.wind.setDeg(Utils.getFloat("deg",windObj));
JSONObject cloudObj = Utils.getObject("clouds", jsonObject);
weather.clouds.setPrecipitation(Utils.getInt("all", cloudObj));
return weather;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
If you look at the URL
https://api.openweathermap.org/data/2.5/weather?q=[Ljava.lang.String;#34e4306&units=metric&APPID=f77c39703fb6be71e2c5a96e58edc077
The value for the query field (q) is [Ljava.lang.String;#34e4306, which is invalid. Ensure that you are sending a proper string value in this place.
Can this line be modified from:
weatherTask.execute(new String[]{city} + "&units=metric");
to the code below:
weatherTask.execute(city + "&units=metric");
Hope this would help.
So I have a URL within Method1 like so
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute();
}
I need to pass the Url into my AsyncTask which is as follows
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}
I need it so the ServiceUrl = the Url from the method. I can't figure out how to do this even from looking at other peoples questions and answers
The first parameter on AsyncTask<First, Second, Third> will define the parameter to be passed on execute(), so you define it as String and pass the url. Then:
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute(url);
}
On your AsyncTask, you can get it on the arg0 (array), i(ndex based on the order on how you passed it on execute())
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
String url = arg0[0]; // this is your passed url
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}
I try to return array of objects with this function:
public static JSONEvent[] invokeFunction(String funName, String requestContent) {
final String functionName = funName;
final String requestPayload = requestContent;
new AsyncTask<Void, Void, InvokeResult>() {
#Override
protected InvokeResult doInBackground(Void... params) {
try {
final ByteBuffer payload =
ENCODER.encode(CharBuffer.wrap(requestPayload));
final InvokeRequest invokeRequest =
new InvokeRequest()
.withFunctionName(functionName)
.withInvocationType(InvocationType.RequestResponse)
.withPayload(payload);
final InvokeResult invokeResult =
AWSMobileClient
.defaultMobileClient()
.getCloudFunctionClient()
.invoke(invokeRequest);
return invokeResult;
} catch (final Exception e) {
Log.e("LAMBDA", "AWS Lambda invocation failed : " + e.getMessage(), e);
final InvokeResult result = new InvokeResult();
result.setStatusCode(500);
result.setFunctionError(e.getMessage());
return result;
}
}
#Override
protected void onPostExecute(final InvokeResult invokeResult) {
try {
final int statusCode = invokeResult.getStatusCode();
final String functionError = invokeResult.getFunctionError();
final String logResult = invokeResult.getLogResult();
if (statusCode != 200) {
//showError(invokeResult.getFunctionError());
} else {
final ByteBuffer resultPayloadBuffer = invokeResult.getPayload();
//resultPayloadBuffer.rewind();
// while (resultPayloadBuffer.hasRemaining())
// Log.e("BUFFER",resultPayloadBuffer.position() + " -> " + resultPayloadBuffer.get());
// User a = new User(23, 24);
//
// User b = new User(58, 59);
// User[] ab = new User[] {a, b};
// User [] events = new User[3];
ObjectMapper mapper = new ObjectMapper();
final String resultPayload = DECODER.decode(resultPayloadBuffer).toString();
Log.e("LAMBDA-SUCCESS", resultPayload);
try {
// String s2 = getJson2(ab);
// Log.e("S2", s2);
//User[] user2 = mapper.readValue(resultPayload, User[].class);
events = mapper.readValue(resultPayload, JSONEvent[].class);
// for (JSONEvent u : events)
// Log.e("USER",u.getLocationLat()+"");
Log.e("ARRAY",Arrays.toString(events));
} catch (Exception e) {
e.printStackTrace();
}
//return resultPayload;
// mResultField.setText(resultPayload);
}
if (functionError != null) {
Log.e("LAMBDA", "AWS Lambda Function Error: " + functionError);
}
if (logResult != null) {
Log.d("LAMBDA", "AWS Lambda Log Result: " + logResult);
}
}
catch (final Exception e) {
Log.e("LAMBDA", "Unable to decode results. " + e.getMessage(), e);
//showError(e.getMessage());
}
}
}.execute();
return events;
}
The problem is that I call invokeFunction in diffrent activity and it returns null but in onPostExecute the array is not null. It seems that it returns array before calling OnPostExecute. How to solve that?
The problem is that the method invokeFunction is finishing before onPostExecute (asynchronous)
You could use an interface to communicate AsyncTask and activity.
Interface (pseudocode):
public interface AsyncCom {
public void sendUsers(User [] events);
}
Your asynFunction (pseucode):
public void invokeFunction(String funName, String requestContent, AsyncCom listener) {
...
And call the function of the listener in postExecute (pseudocode):
protected void onPostExecute(final InvokeResult invokeResult) {
...
listener.sendUsers(events);
}
Declare the interface in your activity and call your method with the listener (pseudocode):
public class MyActivity implements AsyncCom {
...
invokeFunction(funName, requestContent, this);
...
Finally, in your activity, implements the returned method (pseudocode):
public void sendUsers(User [] events){
// do wathever you want with users
}
But remeber that the response will bw asynchonous
private void invokeFunction(String funName, String requestContent{
YourTask task = new YourTask();
task.execute(new String[]{funName, requestContent});
}
static class YourTask extends AsyncTask<String, Void, InvokeResult> {
#Override
protected InvokeResult doInBackground(String... params) {
String funName = params[0];
String requestContent = params[1];
// ...
}
#Override
protected void onPostExecute(final InvokeResult invokeResult) {
/// . ..
doWhatYouNeedWithTheResult(result);
}
};
}
I am calling a web service to get a object out of the json, then I need to pass that object that I have made a string to another web service. I have each web service call in a AsyncTask. I am unable to pass that first jsonObject named map to another subClass. Don't worry about the actaul calls, I want to pass the string "map" to the second AsyncTask to be able to use that in the web call. Both these classes are sub classes.
public class aisleStoreID extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
String urlExtension = "getpartnerstore?latitude=" + lat + "&longitude=" + lng + "&partner_id=" + "60" + "&vendor_store_nbr=" + id + "";
final String authToken = Utils.md5(urlExtension);
String urlString = "http://aisle411.ws/webservices2/getpartnerstore.php?latitude=" + lat + "&longitude=" + lng + "&partner_id=" + 60 + "&vendor_store_nbr=" + id + "&auth=" + authToken;
InputStream inputStream = null;
try {
HttpResponse response = ConnectionHelper.executeHttpGetStoreMap(urlString);
HttpEntity entity = response.getEntity();
inputStream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
result = sb.toString();
if (response != null) {
result = response.toString();
JSONObject jsonObject = new JSONObject(String.valueOf(sb));
JSONArray arr = jsonObject.getJSONArray("stores");
JSONObject jObj = arr.getJSONObject(0);
String map = jObj.getString("store_map_url");
Intent intent = new Intent(this, getStoreMap.class);
intent.putExtra("retailer_store_id", map);
startActivity(intent);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return e.getMessage();
}
return result;
}
#Override
protected void onPostExecute(String result) {
System.out.println(" What's in it" + result);
}
}
public class getStoreMap extends AsyncTask<String, String, String> {
Bundle bundle = getIntent().getExtras();
String storeMap = bundle.getString("retailer_store_id");
#Override
protected String doInBackground(String... params) {
String urlExtension = "map?latitude=" + lat + "&longitude=" + lng + "&partner_id=" + "60" + "&retailer_store_id=" + map + "";
final String authToken = Utils.md5(urlExtension);
String urlString = "http://aisle411.ws/webservices2/map.php?latitude=" + lat + "&longitude=" + lng + "&partner_id=" + 60 + "&retailer_store_id=" + map + "&auth=" + authToken;*/
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
Try to move your code of extracting storeMap from extras to doInBackground method.
You need to do this in your aisleStoreID AsyncTask' onPostExecute():
#Override
protected void onPostExecute(String result) {
System.out.println(" What's in it" + result);
String params[]=new String[1];
params[0]=result;
new getStoreMap().execute(params);
}
and then get the params in doInBackground of getStoreMap AsyncTask.
Note: Intent is used to send extras from one to another component(like activity,services,Broadcast Receivers or Content Providers) , not between classes or subclasses.
An Async Task isn't meant to be an activity. It's meant to work in the background while you get the data for the activity. The difference is important and I would recommend reading up on it in the android docs
I would try returning map from doInBackground and then calling the other AsyncTask in the onPostExecute also you might be able to collapse some of those variables from
JSONObject jsonObject = new JSONObject(String.valueOf(sb));
JSONArray arr = jsonObject.getJSONArray("stores");
JSONObject jObj = arr.getJSONObject(0);
to this
JSONObject jsonObject = new JSONObject(String.valueOf(sb)).getJSONArray("stores").getJSONObject(0);
In onPostExecute
public class aisleStoreID extends AsyncTask<String, String, String> {
#Override
String doInBackground(String... params) {
// ...
return map;
}
#Override
protected void onPostExecute(String result) {
new getStoreMap().execute(result);
}
}