I have an AsyncTask which is called whenever the camera is moved in Google Maps because i want to load new data from web services against the co-ordinates of center point of the map. Now everything is working fine but the thing is that whenever user move the map very fast, the tasks start queuing up. In the result when user stop moving map, the task in queues keep loading the old data until the task which is in the last, come with actual data.
My AsyncTask Class:
private class GetOffersLocations extends AsyncTask<String[], Void, Boolean> {
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
loader.setVisibility(View.VISIBLE);
}
#Override
protected Boolean doInBackground(String[]... params) {
// TODO Auto-generated method stub
[web api called in here and load the data against the center point (latlng)]
}
#Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
this.cancel(true);
}
#Override
protected void onPostExecute(Boolean result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
loader.setVisibility(View.GONE);
if (result) {
[drawing pins on map here]
}
}
}
And here in a function i called this AsyncTask on camera move of the map:
private void showMarkers(String lat, String lng, String radius, int position) {
String[] latLng = { lat, lng, radius, String.valueOf(position) };
new getOffersLocations().execute(latLng);
}
Now this function is called in OnCameraChangeListener and when ever user move the camera this asynctask is called.
So, i think i properly made my question and if not please correct me.
Waiting for help.
Thanks.
You can't stop and start again an AsyncTask, but you can cancel it execution by calling
myGetOffersLocations.cancel(true);
This will interrupt the execution, however, you will need to create a new AsyncTask object to be able to run it again, so you can do this:
myGetOffersLocations.cancel(true);
myGetOffersLocations = new GetOffersLocations();
And then, run it again.
NOTE: Make sure you create an object reference to the AsyncTask, instead of executing it directly as you're doing on your method.
Replace this:
new getOffersLocations().execute(latLng);
With this:
getOffersLocations myOffers = new getOffersLocations();
myOffers.execute(latLng);
in your doInBackground make sure you are doing something like this
while(!isCancelled()){
// Do I/O work
}
and your for loop
for(int i = 0; i < 100 && !isCancelled(); ++i){
// Do something
}
You can cancel it like this
myGetOffersLocations.cancel(true);
myGetOffersLocations = new GetOffersLocations();
Cancelling a task (quoted from AsyncTask | Android Developer)
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled() to
return true.
After invoking this method, onCancelled(Object), instead of
onPostExecute(Object) will be invoked after doInBackground(Object[])
returns.
To ensure that a task is cancelled as quickly as possible, you should
always check the return value of isCancelled() periodically from
doInBackground(Object[]), if possible (inside a loop for instance.)
You can create an object of the AsyncTask class and call execute(..) on it:
GetOffersLocation mGOL = new getOffersLocations();
mGol.execute(latLng)
When you need to cancel the task call:
mGol.cancel(true);
This will send a signal to ongoing task and try to cancel it.
I say try, because AsyncTasks do not terminate everytime necessarily.
Related
I have a method that downloads some data including coordinates of locations from API and populates an array. I am calling this method in onCreate then in onMapReady I am calling another method which puts the markers on map. I only have 2 test items to add to my map now so it doesn't take much time to download them and show, however still sometimes it runs the later method before I am done downloading the data. so it shows no markers.
I tried different approaches such as AsyncTask but this one always runs the later method before first one is finished.
here's my code
private class AsynchronouslyDoSomeStuff extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
Log.v("Donkey", "Async doInBackground Called");
downloadCustomersData();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.v("Donkey", "Async onPostExecute Called");
updateMapMarkers();
}
}
and then calling new AsynchronouslyDoSomeStuff().execute(); in onMapReady but as I said it doesn't work.
Note: I don't want to use things like Thread.wait(some time); because I don't know how much time it takes later for different customers.
Following on from my comment above. A much easier (in my opinion) method of doing this type of thing is the EventBus model.
Please see: http://greenrobot.org/eventbus/
Using this, you can just fire events whenever you want to update your UI.
What you want to do is call a method as soon as two asynchronous tasks have completed (onMapReady and doInBackground).
The easiest way to do that is store the result of the task which finishes first, and then execute the method when the second one finishes.
e.g.
if(customerData == null) {
this.googleMap = googleMap;
} else {
setMapMarkers(customerData, googleMap);
}
if(googleMap == null) {
this.customerData = customerData;
} else {
setMapMarkers(customerData, googleMap);
}
So I solved my question by calling the second method as a callback function of the first method.
and then calling the first method in onLocationChanged of my map.
I called the second method right after for loop.
I'm using AsyncTask to populate SQLite database. I'm downloading data from a certain webpage and putting it in SQLite tables. The thing is, I want to either download 100% of the data or none. So in case the AsyncTask is for some reason interrupted, I want to delete all the data that has been downloaded so far.
This is how I tried to do it:
#Override
protected void onCancelled() {
super.onCancelled();
dbHandler.deleteFromDatabase(razred);
Log.i("TAG", "AsyncTask cancelled");
}
I thought that "onCancelled" will execute if AsyncTask is interrupted in any way but it doesn't. What could I do to erase data that was made with AsyncTask in case it is cancelled in any way? (ex. activity paused, activity destroyed, internet connection interrupted etc.)
You're on the right track, but in your doInBackground() you also need to specifically call isCancelled() to check if it's cancelled and then return from doInBackground(). Then your code will work properly.
Refer to the AsyncTask documentation for "Cancelling a task"
Here's the quote from the documentation for easy reference:
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
EDIT: Per request, some sample code:
private class MyAsyncTask extends AsyncTask<Void,Void,Void> {
private SQLiteDatabase db;
#Override
protected void onPreExecute() {
// any kind of initialization or setup needed before the
// background thread kicks off. remember: this is still on
// on the main (UI) thread
// since youre doing DB I/O, Ill make believe Im initializing the DB here
db = DatabaseHelper.getInstance(MainActvity.this).getWritableDatabase();
}
/*
* The background thread to do your disk and network I/O. If you need
* to pass in any parameters, this is the first Void in the template
*/
#Override
protected Void doInBackground(Void... params) {
// other stuff you need to do in the background. Since you want an
// all-or-nothing type thing, we will use a transaction to manually
// control the db
db.beginTransaction();
try {
// do network I/O to retrieve what you need and then write to DB.
...
... // if theres a loop in here somewhere when reading the data, check !isCancelled() as part of the condition or as one of the first statements and then break
...
db.setTransactionSuccessful(); // assuming everything works, need to set
// this successful here at the end of the try
} catch (InterruptedException ie) { // or some other exception
cancel(true); // heres where you can call cancel() if youve been interrupted
} catch (IOException ioe) { // if your network connection has problems
cancel(true);
} finally {
db.endTransaction();
// other cleanup, like closing the HTTP connection...
// no need to close the DB if you implement it properly
}
return null; // if there was some return value, that would go here
}
#Override
protected void onCancelled(Void result) {
// depending on how you implement doInBackground(), you may not even need this,
// unless you have a lot of other "state" you need to reset aside from the DB transaction
}
#Override
protected void onPostExecute(Void result) {
// any other items to do on main (UI) thread after doInBackground() finishes
// remember, this only gets called if cancel() is not called!
}
}
Hope that helps!
I know this is not exactly what you've asked for, but I have to say you are doing it all wrong by using the AsyncTask.
There are many cases where your async task will be terminated without you being able to do anything. For such critical tasks as this one, use a Service.
With a Service you can till the system to restart your service in case it is terminated prematurely. You then can continue what you started, or start all over again (deleting all previous downloads...etc).
With an AsyncTask, if the system decided to terminate your async task prematurely, you are not notified nor the AsyncTask is restarted. It just dies in complete silence.
I think in the onpostexecute you could handle anything you wanted to.
private class ParseDownload extends AsyncTask<Summary, Void, Boolean> {
#Override
protected Boolean doInBackground(Summary... urls) {
for (Summary url : urls) {
url.dosomething();
if (isCanceled();) { return false;}
}
return true;
}
#Override
protected void onPostExecute(Boolean result) {
if (!result) {
// delete * from yourtable here...
// and mark the download incomplete etc.
}
}
}
Good Luck
I have created an AsyncTask which read JSONArray in the doInBackground Method and return an ArrayList of custom Items (ArrayList). Before this, in the onPostExecute method i move the ArrayList charged in the AsyncTask to another ArrayList of the Main thread, but i think that my AsyncTask never ends and still working. Here I put my code:
Here the AsyncTask:
private class ReadJSONTask extends AsyncTask<Void, Void, ArrayList<Item>>{
#Override
protected ArrayList<Item> doInBackground(Void... params) {
// TODO Auto-generated method stub
ArrayList<Item> auxList = new ArrayList<Item>();
LoadData(auxList); //Method that reads JSON and load information in the ArrayList
return auxList; // Return ArrayList
}
#Override
protected void onPostExecute(ArrayList<Item> result) {
// TODO Auto-generated method stub
Log.i("OnPostExecute", String.valueOf(result.size())); //The size of the array
listMain = result; // Move the data of the AsyncTask to the main Thread
Log.i("OnPostExecute", String.valueOf(listaComic.size())); //The size of the ArrayList I use in the Main Thread
}
}
Here the call to the AsyncTask in the Main Thread:
if (isOnline()){ //Return true if there is internet connection
ReadJSONTask task = new ReadJSONTask();
task.execute();
Log.i("Main Thread - listMain Size", String.valueOf(listMain.size())); //Never executed
//This for loop its only for debug purposes, never executed
for (Item item : listMain){
Log.i("Items", item.toString());
}
}
In the log i see that all Log in the onPostExecute() method are printed, but nothing from the Main Thread.
I don't know where is the error to fix it, i have been working on it 2 days and searching in forums, here in StackOverflow and i can't fix that :S
As name indicates AsyncTask is asynchronous, but you for some reason expects execute() to be blocked unless async task ends, which is wrong. Your code works fine and I expect listMain to simply be empty and once execute() fires asynctask, for would show nothing because async task is not done yet. You should rework your app logic, so async task could tell "main thread" it finished. I.e. move your for loop to separate method and call it from onPostExecute().
I am calling an AsyncTask to stop an previously started service.
But the ProgressDialog does not rotate while the Asyctask is running.
So I think that there is something wrong and I could get problems with an ANR error.
Any ideas?
new asyncTaskZieladresse().execute();
public class asyncTaskZieladresse extends AsyncTask<Void, Integer, Void> {
int progress;
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
final Spinner Fahrerauswahl = (Spinner)findViewById(R.id.spinner1);
final Spinner Fahrzeugauswahl = (Spinner)findViewById(R.id.spinner2);
final Spinner Nutzungsartauswahl = (Spinner)findViewById(R.id.spinner3);
Cursor mcursor = (Cursor) Fahrerauswahl.getSelectedItem();
Fahrer = mcursor.getString(mcursor.getColumnIndexOrThrow("name"));
FahrerID = mcursor.getString(mcursor.getColumnIndexOrThrow("_id"));
mcursor.close();
Cursor mcursor1 = (Cursor) Fahrzeugauswahl.getSelectedItem();
Kennzeichen = mcursor1.getString(mcursor1.getColumnIndexOrThrow("fahrzeug_kennzeichen"));
KennzeichenID = mcursor1.getString(mcursor1.getColumnIndexOrThrow("_id"));
mcursor1.close();
Cursor mcursor2 = (Cursor) Nutzungsartauswahl.getSelectedItem();
Nutzungsart = mcursor2.getString(mcursor2.getColumnIndexOrThrow("nutzungsart"));
NutzungsartID = mcursor2.getString(mcursor2.getColumnIndexOrThrow("_id"));
mcursor2.close();
VariablenUebergebenGpsFahrt();
progressDialog.dismiss();
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
progressDialog = ProgressDialog.show(Main.this, "GPS", "Ziel-Standort wird ermittelt...");
}
#Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
}
#Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
stopService(new Intent(Main.this, GPSService.class));
return null;
}
}
Thats the reason why I have tried to stop the service with the AsyncTask.
This will not help, as I have tried to explain in the comments to your question. If onDestroy() is taking too long, you need to do that work sooner and in a background thread. Putting the stopService() call in a background thread is completely pointless.
The service itself runs already in the background and not in the UI-thread.
No, it is not.
onDestroy() is called on the main application thread. Always.
Your service might also have a background thread of its own for other work, but the lifecycle methods (onCreate(), onStartCommand(), onBind(), and onDestroy()) are always called on the main application thread.
So if the stopping of the service took a long of time, why is the app hanging?
Because you are doing too much work in onDestroy().
The service and the command that waits for the return of the stopped service are not running in the UI-thread.
onDestroy() is called on the main application thread (a.k.a., "UI-thread").
UPDATE based on first comment:
When I call onDestroy() inside the service
You never call onDestroy(). Android calls onDestroy(). You are not Android.
the onDestroy() is not called inside the service for the service itself, rather in the Main thread?
You seem to think that a service is a thread. It is not. You can tell this by reading the documentation ("Note that services, like other application objects, run in the main thread of their hosting process. "). Please read the documentation.
It hard to understand why the ANR, but seems that lot of stuff you exec in the onPostExecute() could be moved on the doInBackground(). Android developers suggest to execute sql lite operation (Content Provider) and every heavyweight operation in a asyn way.
All code in onPostExecute will be executed on the UI thread, only the code in doInBackground will be done in the background...
I have an activity 'Activity1' and also another activity named 'Activity2'. The 'Activity2' is loaded upon clicking a button in 'Activity1'. I wanted to display the progress dialog until the new activity is loaded . Can you please show me the code to do this
Display the progress dialog in Activity2's onCreate method, then do all the time-consuming loading in an AsyncTask. In the AsyncTask's onPostExecute() method, dismiss the progress dialog.
There is two ways to
First approach To use Async Task
If you are doing heavy tasks eg loading data from server or parsing xml in that case use AsynTask<>
If you want to call ActivityB from ActivityA then
*step-1*create a AsyncTask class. write all background tasks inside doBackground() method and after completion of task you want to call an activity that code write inside onPostExecute() post execute method
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.view.View;
public class LoadingDataFromServer extends AsyncTask {
Context currentContext = null;
boolean isCancelled = false;
public LoadingDataFromServer(Context context) {
currentContext = context;
}
#Override
protected void onPreExecute() {
if (DashboardActivity.progressBarLayout != null) {
DashboardActivity.progressBarLayout.setVisibility(View.VISIBLE);
// Log.i(TAG,".....Now make progress bar visible.....");
}
super.onPreExecute();
}
#Override
protected Object doInBackground(Object... params) {
// do background processing
try {
// do background tasks eg sever communication
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Object result) {
// TODO Auto-generated method stub
// progressDialog.dismiss();
// call second Activity
Intent i = new Intent(currentContext, com.ActvityB.class);
super.onPostExecute(result);
}
#Override
protected void onCancelled() {
// TODO Auto-generated method stub
isCancelled = true;
super.onCancelled();
}
}
step-2 In the activity fro where you want to jump to new activity (eg in ActivityA) call the execute() of AsynTask
new LoadingDataFromServer(context).execute(null);
Second approach
First show progress dialog.
create a thread to do all background tasks. when the thread completes
the task then cancel the progress dialog and call the next activity
or
when thread complets the task then call next activity pass this
object (progress dialog) and inside that new activity dismiss this
dialog.
yes by using AsynTask<> you can get your result
in OnPreExecute Show your Progress dialog,in OndoInBackground run your activity,in onPostExecute remove your dialog
get the idea Get Concept
The other answers (using AsynTask) are correct, but the question you need to figure out is what is causing your delay. Is it something happening on the back end of Activity1 or something happening on the front end Activity2. If you're doing some processing before starting Activity2 then follow the advice of Last Warrior or Ted Hopp... if you have some lengthy loading process in Activity2 then you'll need to initiate the progress dialog as the first thing that happens onCreate of Activity2 and then move whatever is taking up processing resources off into an AsynTask (or just another thread) there.
I guess in the unlikely event that both A1 and A2 are requiring extra time on the end and front of each respectively, you'll need to open and close a progress dialog... I don't think there's a way to keep one open in the foreground as you move from one activity to the other.
you can do it through AsyncTAsk. Which code taking time for executing just put that into
doInBackground()
override method of asyncTask and
startActivity(intent) ----just put into onPostExcute()
protected class InitTask extends AsyncTask<Context, Integer, Integer> {
#Override protected Integer onPreExcute( Context... params ) {
//assign progressbar here
}
#Override protected Integer doInBackground( Context... params ) {
//do all the stuffs here
return super.doInBackground( params )
} #Override protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//update progress bar
}
#Override protected void onPostExecute( Integer result ) {
super.onPostExecute(result);
//start activity here
}
}