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...
Related
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.
Basically, do I have to put code I want to run on another thread inside doInBackground, or can I call another function/class/whatever-it-is-functions-are-called-in-JAVA within doInBackground and have it run asynchronously? IE: (example code I found online)
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
is how I have seen it done, but can I instead do:
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
and have it call a function I already wrote and then have that function run in another thread? Sometimes my HTTP server is a bit slow to respond (it is but a lowly testing server at the moment) and Android automatically pops up the kill process box if my postToServer() call takes more than 5 seconds, and also disables my UI until the postToServer() call finishes. This is a problem because I am developing a GPS tracking app (internally for the company I work for) and the UI option to shut the tracking off freezes until my postToServer() finishes, which sometimes doesn't ever happen. I apologize if this has been answered, I tried searching but haven't found any examples that work the way I'm hoping to make this work.
You can do that, but you will have to move the UI updates to onPostExecute as it is run on the UI thread.
public MyAsyncTask extends AsyncTask<foo, bar, baz> {
...
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
protected void onPostExecute(Long result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
....
}
You may want to pass in the TextView to the constructor of the AsyncTask and store it as a WeakReference.
private final WeakReference textViewReference;
public MyAsyncTask(TextView txt) {
textViewReference = new WeakReference<TextView>(txt);
}
And then in onPostExecute you would make sure that the TextView reference still exists.
protected void onPostExecute(Long result) {
TextView txt = textViewReference.get();
if (txt != null)
txt.setText("Executed");
}
If you want to notify the user that the task is executing I would put that before invoking the AsyncTask.
myTextView.setText("Update in progress...");
new MyAsyncTask().execute();
then in onPostExecute set the TextView to say "Update complete."
Have you tried it the second way?
From what you've posted it seems like it should work fine how you have it in the second example.
However (perhaps unrelated to your question?) in your first example I think it will fail because you are trying to change the UI from a background thread. You'd want to put the parts that manipulate the TextView inside of onPostExecute() rather than doInBackground()
Yes you can, the call to your postToServer method (that's the name in java) will run off the main thread.
Everything inside the doInBackground method of an AsyncTask is run on a pooled thread, but be sure to NOT invoke it directly! Call execute on your asynktask instead, the android framework will do the work for you and run doInBackground on another thread.
try doing something like this:
new AsyncTask<Void, Void, Void>() {
#Override
// this runs on another thread
protected Void doInBackground(Void... params) {
// assuming x, y, z, h are visible here
postToServer(x, y, z, h);
return null;
}
#Override
// this runs on main thread
protected void onPostExecute(Void result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
}.execute(); // call execute, NOT doInBackGround
Also, notice that every other method of AsyncTask, such as onPostExecute runs on the main thread, so avoid heavy loading them.
Basically The Bottom Line Is the doInBackground() method is Can't interact with The Ui Thread Or The Main thread. that's Why When You are Try To Interact With The TextView in doInBackground () it Will Crash the UI Thread Cuz It's Illegal.
so if anytime You want to Interact with the UI Thread,When You are Working on doInBackground You need to Override
OnPostExecute() //this Function is Called when The doInBackground Function job is Done.
So You can Update The UI Thread Content By this When You're Job is Done In doInBackground () or You are In doInBackground ()
I use AsyncTask to change text of TextView like this:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
response += url;
}
return response;
}
#Override
protected void onPostExecute(String result) {
textView.setText(result);
}
}
Everything will fine if I call it in OnClick event:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = (TextView) findViewById(R.id.txt);
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new LongOperation().execute(new String[]{"Hello"});
}
});
}
But the problem when I called it in my thread, the program forced close
this.closeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread t= new Thread(){
#Override
public void run(){
try{
//Do something
//Then call AsyncTask
new LongOperation().execute(new String[]{"Hello"});
}catch(Exception e){}
}
};
t.start();
}
});
Where am I wrong? I dont' understand how difference call AsyncTask in thread or not.
I recommend you consult the AsyncTask documentation and Processes and Threads for a better understanding of how it works. Essentially, you should create your AsyncTask subclass on the main thread.
When you call AsyncTask.execute(), your provided, AsyncTask.onPreExecute is called on the main thread, so you can do UI setup.
Next AsyncTask.doInBackground method is called, and runs in its own thread.
Finally, when your AsyncTask.doInBackground method completes, a call is made to AsyncTask.onPostExecute on the main thread, and you can do any UI cleanup.
If you need to update the UI from within your AsyncTask.doInBackground method, call AsyncTask.publishProgress, which will invoke onProgressUpdate in the main thread.
When you call it from the UI thread, the associated Context is the running Activity. When you call it from a regular thread, there is no valid Context associated with that thread. AsyncTask executes in its own thread, you shouldn't be creating its own thread. If that is actual code, then you have missunderstood the point of AsyncTask. Search for tutorials on how to use it.
Adding to what the others have said: I think you can use AsyncTask to launch off a task in another thread, even if you start the AsyncTask from a different thread than the UI already.
But in that case, the only way you'll only be able to modify the UI indirectly, for example: pass the handler of the current Activity somehow to this AsyncTask instance, and send messages to it (handler messages get processed on the UI thread). Or use broadcast intents that the Activity catches and updates the UI accordingly, etc. These solutions seem to be overkills though.
I have several AsyncTasks doing network operations. I was recently asked to add, after each of these operations, another network call that invokes a remote service sending some statistics. I'd like this statistics call not to delay 'main' network calls, and so I was thinking of executing them in a thread created and started inside the AsyncTask's doInBackground(). This thread would most probably end after the doInBackground() and possibly the whole AsyncTask has ended. I tried this and it works, but I was wondering if there are side effects, such as memory leaks or similar?
Thanks ;)
Try starting a second AsyncTask in the first AsyncTasks 'onPostExecute' Method.
For me this worked without any issues.
If you want to start thread in doInBackground method, you can start it in onProgressUpdate() method
Example:
protected class MyTask extends AsyncTask<Void,Integer,Void> {
public static final int START_THREAD = -1;
#Override
protected void onProgressUpdate(Integer... values) {
if(values[0] == START_THREAD){
startThread();
}
}
#Override
protected Void doInBackground(Void... params) {
publishProgress(START_THREAD);
return null;
}
}
The documentation at http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase%28%29 states:
Database upgrade may take a long time,
you should not call this method
[getWritableDatabase] from the
application main thread, including
from ContentProvider.onCreate().
This begs the question: for best practice, where should getWritableDatabase be called from?
My feeling is that, perhaps, it should be called once upon application launch with a callback to mark the database as ready. Is this correct?
For small and agile databases I imagine this isn't much of an issue.
Otherwise, I'd use an always-wonderful AsyncTask, called from onCreate.
It can be called from anywhere, but it should not be called from the UI thread because you don't know how long the process will take (especially with the different file systems in use). Even if you know the database should be small, you don't know about the file system (can it perform more than one job at a time? are there are thousand other jobs waiting in line already?). You can use an AsyncTask or a Thread to call getWriteableDatabase.
It seems that the intended use of the open helper framework, is to open the db on activity start, and close it when the Activity is destroyed.
In an AsyncTask from within onCreate()...
new StartupTask().execute();
The AsyncTask Thread.sleep() below is just to give enough time to show the dialog so that you can see it work. Obviously take that out when you're done playing. ;)
private class StartupTask extends AsyncTask
{
private ProgressDialog progressDialog;
#Override
protected Object doInBackground(final Object... objects)
{
openHelperRef.getWritableDatabase();
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
runOnUiThread(new Runnable()
{
public void run()
{
progressDialog = ProgressDialog.show(
MyActivity.this, "Title",
"Opening/Upgrading the database, please wait", true);
}
});
}
#Override
protected void onPostExecute(Object object)
{
super.onPostExecute(object);
progressDialog.dismiss();
}
}
in onDestroy()...
openHelper.close();