This is the flow of my app:
1) The user takes a picture or video
2) The media is saved to internal storage
3) The path is assigned to a custom object
4) The UI is updated to indicate that the user can continue
The UI updates only if the custom object has either an imagepath or a videopath. I've just started using AsyncTask to save to internal storage in a background thread so the app will not hang while saving large files, but I'm having some problems.
What I want to do: Display a ProgressDialog until doInBackground() finishes, then assign the path to my Object, and then continue on the main thread to update the UI.
Right now, the main thread will continue while AsyncTask is still working, and the UI will not update correctly since the path has not yet been assigned to the Object.
I've read about AsyncTask#get(), but I'm not sure how to implement it with the ProgressDialog. I tried, and the main thread didn't seem to wait for the results before continuing.
I'd really appreciate any help. Thank you!
My AsyncTask:
private class SaveMediaTask extends AsyncTask<Integer, Void, String>
{
private ProgressDialog progressDialog;
private int mediaType;
public SaveMediaTask()
{
progressDialog = new ProgressDialog(getActivity(), ProgressDialog.THEME_HOLO_LIGHT);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
this.progressDialog.setTitle("Processing");
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
this.progressDialog.show();
}
protected String doInBackground(Integer... mediaType)
{
//save to internal storage and return the path
return path;
}
protected void onPostExecute(String path)
{
//by the time this runs, the UI has already tried to update itself on the main thread,
//and found that myObject does not yet have a path. Once this runs, it is too late.
myObject.setPath(path);
if (progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
How I call it, immediately after the user leaves the camera:
new SaveMediaTask().execute(MEDIA_TYPE_IMAGE);
//WAIT HERE AND DISPLAY PROGRESSDIALOG UNTIL TASK IS DONE
//update UI
Your onPostExecute should notify your activity, that it can continue. So basically:
// Start the task from your activity
new SaveMediaTask().execute(MEDIA_TYPE_IMAGE);
// Method that will be called when task is completed
public void taskComplete() {
// update UI
}
...
protected void onPostExecute(String path) {
myObject.setPath(path);
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
((YourActivity)getActivity()).taskComplete();
}
the main thread didn't seem to wait for the results before
continuing.
the main thread wouldn't wait. This isn't how AsyncTask work. AsyncTask runs in background along with your main thread.
then continue on the main thread to update the UI.
You don't need to continue on the main thread to update the UI once AsyncTask task is done, you could simply execute that post doInBackground task in onPostExecute() after dismissing the progressDialog, i.e progressDialog.dismiss(); because onPostExecute runs in the UI thread.
Also, the good way is to start progressDialog inside onPreExecute() method and dismiss it in onPostExecute without checking if the progessDialog is still showing or not because onPostExecute() would only run if the doInBackground() method is finished doing its job.
What I want to do: Display a ProgressDialog until doInBackground()
finishes, then assign the path to my Object, and then continue on the
main thread to update the UI.
Start ProgressDialog in onPreExecute,
Assign the path to your object in doInBackground
Dismiss ProgressDialog in onPostExecute,
Continue your main thread work in onPostExecute since it runs in UI thread.
You can also update your UI thread while doInBackground is still running by invoking publishProgress(). Each call to this method will trigger the execution of onProgressUpdate() on the UI thread.
Tip: Its a good idea to dismiss progressDialog inside onCancelled() method beside dismissing it in onPostExecute() in case if you ever cancel your task inside doInBackground
You can move your code marked with //update UI to the end of your onPostExecute method. onPostExecute is always called on UI thread and is a good place to update UI to reflect the AsyncTask work results.
Related
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
asyntask.execute();
}
I'm reading data from some API. Is it possible to call doInBackground() from onPostExecute?
I want do it recursively like (network task and update in UI ) for 5 times. Thanks in advance.
Starting the AsyncTask again from inside onPostExecute is a horrible idea. As you want to do it recursively like 5 times for network calls along with UI update, I would like to suggest you to keep an interface to keep track of the AsyncTask call.
So here's an example about how you can achieve the behaviour. You may create an interface like this.
public interface MyResponseListener {
void myResponseReceiver(String result);
}
Now you declare the interface in your AsyncTask class too. So your AsyncTask may look like this.
public class YourAsyncTask extends AsyncTask<Void, Void, String> {
// Declare an interface
public MyResponseListener myResponse;
// Now in your onPostExecute
#Override
protected void onPostExecute(final String result) {
// Send something back to the calling Activity like this to let it know the AsyncTask has finished.
myResponse.myResponseReceiver(result);
}
}
Now you need to implement the interface you've created already in your Activity like this. You need to pass the reference of the interface to the AsyncTask you're starting from your Activity
public class MainActivity extends Activity implements MyResponseListener {
// Your onCreate and other function goes here
// Declare an AsyncTask variable first
private YourAsyncTask mYourAsyncTask;
// Here's a function to start the AsyncTask
private startAsyncTask(){
mYourAsyncTask.myResponse = this;
// Now start the AsyncTask
mYourAsyncTask.execute();
}
// You need to implement the function of your interface
#Override
public void myResponseReceiver(String result) {
if(!result.equals("5")) {
// You need to keep track here how many times the AsyncTask has been executed.
startAsyncTask();
}
}
}
doInBackground() is called before calling onPostExecute.
Since you cannot perform network tasks on the UI thread, an AsyncTask is created.
The AsyncTask executes in the background, performing the network task on the worker thread. Then after the background task is completed, onPostExecute() is called that makes the UI changes on the UI thread.
This should help : https://developer.android.com/reference/android/os/AsyncTask.html
https://developer.android.com/training/basics/network-ops/index.html
The AsyncTask class is used to do some work in background and publish the results to the MainThread so it's generally not possible because the work that's being done in the worker thread may not be doable in MainThread, (like the NetworkOnMainThreadException when you do networking in MainThread).
I suggest you to make an array of your jobs and call the execute() method of your subclass of AsyncTask and it will serialize the jobs to be done in a worker thread.
I am using AsyncTask so that the function I want executes immediately and does not wait till the end to be executed..
but for some reason I don't why it executes in at the end of all process!
I looked at other solutions and found that Thread should be executed at the end but for AsyncTask it should be executed whenever it is called..
here is my code
private void LogMeIn()
{
string CheckValue;
// Here I call the AsyncTask
new GCM().execute(null,null,null);
//gcmRegID is a public variable and should has GCM value assigned to it by now, but I it is empty as GCM() has not been executed yet
//This is always return empty string
CheckValue = gcmRegID;
}
This is the AsyncTask that wait till the end to be executed
//This is the AsyncTask
private class GCM extends AsyncTask<String, String, String> {
private String resp;
private Context context;
#Override
protected String doInBackground(String... params) {
GCMHelper gcmRegistrationHelper = new GCMHelper (
getApplicationContext());
try {
gcmRegID = gcmRegistrationHelper.GCMRegister("123456789");
} catch (Exception e) {
e.printStackTrace();
}
return gcmRegID;
}
}
I tried to put the call for GCMRegister in onPreExecute but i get an error that it has to be in the main thread
it is like i am going in circles....
the call has to be in the main thread and the main thread will be executed at the end of the function...
it is like no way to get the GCM code in the middle!!!!
How can I make this AsyncTask executes when it called??
Thanks
Without seeing more of your code it's hard for me to tell but I would take a look at where you are calling LogMeIn(). Because your AsyncTask and call to execute are nested in the LogMeIn() function, it won't be called until LogMeIn() is first called.
AsyncTask goes through the following 4 steps in order after calling execute():
onPreExecute()
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)
These can be added to your GCM class and used however you like. Note that you don't call them directly. AsyncTask does it automatically after calling .execute().
Only the tasks specified in doInBackground(Params...) are executed on a background thread. The rest are all done on the UI(or main) thread. I would suggest putting either a toast or a log inside onPreExecute() and in onPostExecute() to debug where/when GCM().execute is actually being called and then to tell you when the background task is complete. This will give you a better idea of what is going on.
Make sure you aren't trying to update the UI in doInBackground().
Would love to help more but we would need to see more of your code.
#Override
public void onPreExecute() {
super.onPreExecute();
Log.d("GCM", "onPreExecute: called");
}
#Override
public void onPostExecute(String resultOfDoInBackground) {
super.onPostExecute(resultOfDoInBackground);
Log.d("GCM", "onPostExecute: called");
}
AsyncTask keeps a queue of tasks and a thread pool,the thread pool execute the tasks one by one,so if you have too more tasks ,you will find it not execute your tasks immediately.
And in one process ,all your AsyncTask share one thread pool.In this case,you should make one task queue by yourself,you can just use HandleThread to execute a timely task.
Try placing your new GCM().execute(null,null,null); in the protected void onCreate(Bundle savedInstanceState) method. this way it will be called once the app is ran. This way you will have your GCM id before you get to the LogMEIn method.
Try this -
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
new GCM().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);
} else {
new GCM().execute();
}
I am working on an android application extending activity and implementing Runnable.
The problem is that the run() function is not launched and I'm not sure if implementing Runnable make it launch automaticly as AsyncTask?
Here is my code
public class MainActivity extends FragmentActivity implements
ActionBar.TabListener, Runnable {
...
#Override
public void run() {
// mediaPlayer is myMediaPlayer
// progress is my SeekBar
Log.w("Tunesto", "testabababab");
int currentPosition = 0;
int total = mediaPlayer.getDuration();
progress.setMax(total);
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(currentPosition);
Log.w("MyApp", String.valueOf(currentPosition));
}
}
}
Obviously if I launch Run fro, the UI Thread it will freeze the screen because of the sleep method, so how should I do to let the Run method to run?
Thank you!
You will have to use AsyncTask in Android.
Steps to perform:
Create a nested class which extends AsyncTask
Implement the doInBackground method, where your logic should reside
Call the execute method on your nested class instance from where you need to call run()
The fact is i'm not sure if it's possible to stop an asyntask?
You can cancel the async task using the cancel() method on the AsyncTask. Refer to Android Documentation for more details on this.
Edit
To move your code inside the run() function to an AsyncTask, you will put anything that is "setting up" UI stuff inside onPreExecute() such as creating a ProgressBar. Then put your work in such as calling sleep() and other things that can't be done on the UI Thread inside doInBackground(). If you need to update your seek bar you can do that in onProgressUpdate() by calling publishProgress() inside doInBackground(). Finally, if you need to dismiss the ProgressBar or do some ending UI stuff you will do that in onPostExecute().
The reason for that explanation is because all methods of AsyncTask run on the UI Thread except for doInBackground().
AsyncTask Docs
Vogella: Background tasks in Android
new Thread(this).start();
Although an anonymous inner class on either the handler or async would be better than making the activity itself Runnable.
I want to create a method which can update(in particular time interval) the user about the progress of tasks it is performing & finally, can return a result true/false or any object.
For eg I have to create a method which encrypts or decrypts a file/directory. So I have to show the progress of encryption/decryption & at the end I have to return true or false value whether file is encrypted/decrypted successfully or not.
I want to create this in Android(Java) & the method should be an independent one.
You can use an AsyncTask
AsyncTask
Example
You can even put this in a service running alone even if the app is in background..
you can use AsyncTask.
in AsyncTask you can do things in doInBackground. during the task you can post progress by calling publishProgress method which will be catched by onProgressUpdate method. also when the task is finished the onPostExecute method will be called. So that is what you want.
So all you need to do this something like that
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
// Do your task and during the task calculate the progress and call publishProgress to publish the progress
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
progress received
}
protected void onPostExecute(Long result) {
task finihshed
}
}
I have following piece of code:
public class SomeActivity extends Activity {
Context context;
List<MenuItem> menuItems;
public void importList(View v) {
menuItems = new ArrayList<MenuItem>();
ProgressDialog dialog = ProgressDialog.show(this.context, "TITLE", "MSG");
MyAsyncTask task = new MyAsyncTask(context); // Context is here because I tried to create ProgressDialog inside pre/postExecute, but it doesn't work either
task.execute();
try {
// menuItems = task.get();
} catch(Exception e) {
// : (
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
this.context = this;
}
}
When I comment the line, where i get values from AsyncTask ("menuItems = task.get()") everythings work ok. But when I uncomment it, ProgressDialog appears AFTER the task is finished, and value returned. Why is that?
I think that it has sth to do with these contexts (that's why I included onCreate method) but I don't have any idea how to fix it. Obviously, I want ProgressDialog to display BEFORE task is finished, not after.
Not sure if relevant - MyAsyncTask is doing http request and some json parsing.
I think that it has sth to do with these contexts
Not at all. Also, when sending Context from an Activity you don't need to create a variable. Simply use this or ActivityName.this.
MyAsyncTask task = new MyAsyncTask(this);
But when I uncomment it, ProgressDialog appears AFTER the task is finished, and value returned. Why is that?
Calling get() blocks the UI, this is why you don't see the progress until it is done. You don't need to call this. The point of onProgressUpdate() is so the background thread (doInBackground()) can call it to update the UI since doInBackground() doesn't run on the UI.
Remove that line from your code and never look at it again. If there is a reason that you think you need it then please explain and we can help you find a better way.
Edit
See this SO answer on updating when the task is finished