I have a class which extends AsyncTask, which is intended to serve as a generic task manager class for my application.
Problem:The strange behavior is that the progress dialog shows up, but is never dismissed.
I am sure that onPostExecute() gets called for every task instance, as any Log.d("","") statements fire if placed in here, even the Toast messages show up from within this method, but I am not able to dismiss the static dialog.
I understand that AsyncTask(s) have access to UI thread at only 2 places [onPreExecute() and onPostExecute()], so I think trying to dismiss the dialog in runOnUiThread() is unnecessary.
All calls to executeTask() are made from different onCreate() methods of different activities that need to fetch some data over network before populating some of their UI elements, and I always pass the current activity's context to the tasks.
As I do not switch activities until after the related tasks are completed, I believe the activity context objects are still valid (am I wrong to have assumed this???) I have never found any of them to be null while debugging.
Could this be a timing issue? I have also observed that most of the times DDMS shows all tasks get completed before the activity is displayed. If I use new Handler().postDelayed(runnable_which_calls_these_tasks,10); in the onCreate(), and add delaying code in foo_X(), the activities are displayed without any delay, but the dialog will just not dismiss().
I have read through quite a number of articles on this issue but am still not able to figure out exactly where am I going wrong. I do not want to define each task as private inner class Task1 extends AsyncTask<> in all of my activity classes and I would not want to (unless this is the only solution) load my application object with all activity references either as mentioned in this discussion: Is AsyncTask really conceptually flawed or am I just missing something?.
I have spent a week on this and am absolutely clueless :( It would be great if someone can guide me, and let me know what am I missing.
Following is the class definition: [I've removed some irrelevant application specific code for clarity]
public class NetworkTask extends AsyncTask<Void, Integer, Boolean> {
private Context UIcontext;
private int operationType;
private static ProgressDialog dialog;
private static int taskCount;
private NetworkTask(int operationType Context context){
this.UIcontext = context;
this.operationType = operationType;
if (taskCount++ == 0)
dialog = ProgressDialog.show(context,"","Loading...");
}
public static Boolean executeTask(int operationType, Context context) {
return new NetworkTask(operationType, context).execute().get();
}
#Override
protected void onPreExecute(){
super.onPreExecute();
if (taskCount == 1)
dialog.show();
}
#Override
protected Boolean doInBackground(Void... arg0) {
switch(operationType){
case TYPE_1:
foo1();
break;
case TYPE_2:
foo2();
break;
case TYPE_3:
foo3();
break;
case TYPE_4:
foo4();
break;
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
taskCount--;
if (dialog.isShowing() && taskCount == 0){
dialog.dismiss();
}else {
Toast.makeText(UIcontext, "Task#"+ operationType+", m done, but there are "+taskCount+" more", 5).show();
}
}
}
Edited:
Mystery solved - this one was giving me a bit of trouble. There were a few problems:
The main problem why dialog was not showing was NetworkTask.get(). This is a blocking call that waits for the whole NetworkTask to finish. During that time it blocks UI thread so nothing is drawn (also other UI elements are unresponsive). Remove get():
public static Boolean executeTask(int operationType, Context context){
new NetworkTask(operationType, context).execute();
return true; // return whatever
}
The show() on ProgressDialog is called twice. This shows two dialogs, one after another. Remove show() inside onPreExecute().
ProgressDialog is modal - it prevents changing UI until it is done. Toast.makeText() are called before dialog.dismiss() but since dialog is blocking drawing to screen, they get queued and are shown after dialog is dismissed.
super.onPostExecute(result) and super.onPreExecute() are redundant so can be removed.
I can post the whole working code if you have trouble with it.
Related
I am a beginner to Android and I have some confusions regarding Android UI Thread. Now, I know that no thread apart from the one that created the UI can modify it.
Great.
Here is the Activity from my first Android app which slightly confuses me.
public class NasaDailyImage extends Activity{
public ProgressDialog modalDialog = null;
//------------------------------------------------------------------------------
#Override
protected void onCreate(Bundle savedInstanceState){
//Instantiate progress dialog, skipping details.
Button b = //get reference to button
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
modalDialog.show(); // show modal
Toast.makeText(getApplicationContext(), "Getting feeds", 500).show();
new AsyncRetriever().execute(new IotdHandler()); // Get the feeds !!
}
});
}
//------------------------------------------------------------------------------
public synchronized void resetDisplay(boolean parseErrorOccured,
boolean imageErrorOccured,
IotdHandler newFeeds){
if(parseErrorOccured || imageErrorOccured){
// make a Toast
// do not update display
}else{
// make a Toast
// update display
// based on new feed
}
}
//------------------------------------------------------------------------------
class AsyncRetriever extends AsyncTask<IotdHandler,Void,IotdHandler>{
#Override
protected IotdHandler doInBackground(IotdHandler... arg0) {
IotdHandler handler = arg0[0];
handler.processFeed(); // get the RSS feed data !
return handler;
}
//------------------------------------------------------------------------------
#Override
protected void onPostExecute(IotdHandler fromInBackground){
resetDisplay( // call to update the display
fromInBackground.errorOccured,
fromInBackground.imageError,
fromInBackground);
}
//------------------------------------------------------------------------------
}
1. onCreate is on the UI thread so I can do whatever I want but onClick is not. Why can I make a ProgressDialog and a Toast in that method? Why no error there?
2. The AsyncTask is subclass of the the NasaDailyImage. This means it can access all the methods of NasaDailyImage including resetDisplay() which updates the display. resetDisplay() is called in the onPostExecute which runs on a different thread from UI. So, why can I update the display there and yet get no errors ?
onClick() is indeed on the UI thread. Most of what happens in an Activity happens on the UI thread.
onPostExecte() (and its counterpart onPreExecute()) runs on the UI thread as well. The AsyncTask.onPostExecte() documentation clearly states this. AsyncTask was deliberately designed such that developers could update the UI before and after they do background work.
In general, your code will be running on the UI thread unless you explicitly tell it otherwise. Once you create AsyncTasks, Runnables, or Threads, you need to ensure you understand where your code is executing. In an Activity, it is typically safe to assume you are on the UI thread.
You are extending AsyncTask class , where async task class is calling its sequential method automatically. First onPreExecute then doBackground and finally onPost. If you want to change any ui change you can use onProgressUpdate method.
To use your activity class simple call activityclass.this.resetDisplay(). Because inner class scope sometimes failed to integrate except global varible.
Thanks
Edit
After moving my loading / creation code to an Async Task (see below) - I still have the initial problems that I had with my original splashscreen.
Those being that:
1) On starting the Async task in onCreate, everything is loaded but my Dialog can only be shown when onStart() is called which makes the whole process kind of pointless as there is a long pause with a blank screen, then after everything has loaded, the 'loading' dialog flashes up for a split second before disappearing.
2) I can't move object loading / creation etc to onStart because a) it will be run again even when the app is resumed after being sent to the background which I don't want to happen, and b) when when calling restoring the savedInstanceState in onCreate() I'll get a nullPointerException because i'm restoring properties of objects that won't have yet been created.
Would really appreciate if someone could advise how to get around these problems and create a simple splashscreen. Thanks!
Background
My app uses only one activity and I would like to keep it that way if possible.
I've struggled with this for over a week so really hope someone can help me out.
All I want to do is use a splashscreen with a simple 'loading' message displayed on the screen while my resources load (and objects are created etc.) There are a couple of points:
Conditions
1) The splashscreen should not have it's own activity - everything needs to be contained in a single-activity
2) The splashscreen should not use an XML layout (I have created a Splashscreen class which uses View to display a loading PNG)
3) My app is openGL ES 2.0 so the textures need to be loaded on the OpenGL Thread (creation of objects etc that don't use GL calls are OK to go on another thread if necessary).
What I've attempted so far
What I did so far was to create a dialog and display it in my onStart() method with:
Dialog.show();
then let everything load in my onSurfaceCreated method before getting rid of it with:
Dialog.dismiss();
However I needed to change this for varioius reasons so now I create my objects from a call within my onCreate() method and just let the textures load in my GL Renderer's onSurfaceCreated method.
However, this means that because the dialogue isn't displayed until after onCreate, I still get a delay (blank screen) while everything is created before the splash-screen is shown, this then stays on the screen until the textures have loaded. There are other issues with this too which can wait for another day!
So my approach is obviouly very wrong. I read every tutorial I could and every splash-screen related question I could find on SO and Gamedev.SE but I still can't find an explanation (that makes sense to me), of how this can be achieved.
I'm hope someone here can explain.
You should be able to use AsyncTask to load resources in the background and then just dismiss your splash
Here's an AsyncTask that I use to load data from a remote db. This displays a loading progress circle until the task is complete but should be easily re-purposed to display your splash
AsyncTask that runs in the background
private class SyncList extends AsyncTask<Void, ULjException, Void> {
private static final String TAG = "SyncList";
private final class ViewHolder {
LinearLayout progress;
LinearLayout list;
}
private ViewHolder m;
/**
* Setup everything
*/
protected void onPreExecute() {
Log.d(TAG, "Preparing ASyncTask");
m = new ViewHolder();
m.progress = (LinearLayout) findViewById(R.id.linlaHeaderProgress);
m.list = (LinearLayout) findViewById(R.id.listContainer);
m.list.setVisibility(View.INVISIBLE); //Set the ListView that contains data invisible
m.progress.setVisibility(View.VISIBLE); //Set the loading circle visible you can sub in Dialog.show() here
}
/**
* Async execution performs the loading
*/
#Override
protected Void doInBackground(Void... arg0) {
try {
Log.d(TAG, "Syncing list in background");
dba.open(ListActivity.this);
dba.sync();
} catch (ULjException e) {
publishProgress(e);
}
return null;
}
/**
* Display exception toast on the UI thread
*/
protected void onProgressUpdate(ULjException... values) {
Log.e(TAG, values[0].getMessage());
Toast.makeText(ListActivity.this, "Sync failed", Toast.LENGTH_LONG).show();
}
/**
* Finish up
*/
protected void onPostExecute(Void result) {
Log.d(TAG, "ASyncTask completed, cleaning up and posting data");
fillData();
m.list.setVisibility(View.VISIBLE); //Show the list with data in it
m.progress.setVisibility(View.INVISIBLE); //Hide the loading circle sub in Dialog.dismiss()
}
}
Calling the Task
protected void onStart() {
super.onStart();
// Init the dba
dba = DBAccessor.getInstance();
new SyncList().execute();
}
It should be noted that the AsyncTask is an inner class of the Activity its related to here
Edit
onCreate Method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Dialog.show();
//This launches a new thread meaning execution will continue PAST this call
//to onStart and your loading will be done concurrently
//Make sure to not try to access anything that you're waiting to be loaded in onStart or onResume let your game start from onPostExectue
new AsyncTask.execute();
}
doInBackground
protected Void doInBackground(Void... arg0) {
Load all resources here
}
onPostExecute
protected void onPostExecute(Void result) {
Dialog.dismiss();
Call a method that starts your game logic using your newly loaded resources
}
I have 30 threads (AsyncTask) executed in the same time. Before execute them, I will show progressdialog. After threads finish execution, I will dismiss progressdialog.
Any suggestion on How to dismiss ProgressDialog after finish all threads?
If you are using exact 30 threads then initialze a variable with 30 and after every task completion decrease the count by one and also check if the variable is zero . if it is zero it indicate that all the tasks has been completed. so you can dismiss the ProgressDialog.
The best way to do this is use an AsyncTask() to execute your work and dismiss the dialog in onPOstExecute() of it. This will ensure that the task has completed and the ProgressDialog finished. If you still want to continue using Threads , you need to impplement a custom listener which is fired whenever a thread has completed, and you can maintain a counter of how many times it has been fired. I could have given you a code, better example is at this link: How to know if other threads have finished?
you can use a global class:
public abstract class Global{
private static int counter = 30;
private static ProgressDialog pd;
private static Activity a;
public static synchronized void updateCounter(){
counter--;
if(counter<=0){
a.runOnUiThread(new Runnable() {
#Override
public void run() {
pd.dismiss();
}
});
}
}
}
you need to use the "synchronized" because of the concurrent access of the threads.
And in your main activity start the ProgressDialog and initialize the variables:
Global.a = this;
Global.pd = ProgressDialog.show(this, "Tittle","Message ...", true);
and then start the threads.
On the end of each thread you can then call Global.updateCounter();
I have 3 AsyncTasks and 1 ProgressBar. I want when any of task executes, the progress bar is visible and when all of them finish, the progress bar is invisible.
In Java, there is ExecutorService::isTerminated to check if all runnables finished but Android doesn't have it.
Update: 3 tasks execute at the same time.
Figure.
Nice graphic. But I am afraid there is no build in mechanism for this. You'll have to implement it by yourself. There are few solutions you could use -
Keep a reference to all 3 task. When task finishes check if the other two tasks are finished too, if yes than close the progress dialog if no wait for some other task to finish and check again. Make sure you free the references when you're done.
If you don't want to keep a reference store a counter. When the task finishes, increment the counter and check if it's equal to 3. If all tasks finished and you are done. If you implement this make sure to synchronized the access to the counter.
Try using AsyncTask.getStatus(). This works perfectly fine. Refer below sample code.
List<AsyncTask<String, String, String>> asyncTasks = new ArrayList<AsyncTask<String, String, String>>();
AsyncTask<String, String, String> asyncTask1 = new uploadTask().execute(string);
AsyncTask<String, String, String> asyncTask2 = new downloadTask().execute(string);
AsyncTask<String, String, String> asyncTask3 = new createTask().execute(string);
asyncTasks.add(asyncTask1);
asyncTasks.add(asyncTask2);
asyncTasks.add(asyncTask3);
You can later loop the AsyncTaskList and find each of the tasks' status as below.
for(int i=0;i<asyncTasks.size();i++){
AsyncTask<String, String, String> asyncTaskItem = (AsyncTask<String, String, String>)asyncTasks.get(i);
// getStatus() would return PENDING,RUNNING,FINISHED statuses
String status = asyncTaskItem.getStatus().toString();
//if status is FINISHED for all the 3 async tasks, hide the progressbar
}
A simple workaround would be to use three boolean variables one each for each AsyncTask and then check them accordingly.
A better approach would be to create a separate class that extends AsynTask and defines a callback interface which is fired in onPostExecute.
create a field to hold all tasks:
private ArrayList<HtmlDownloaderTask> mTasks;
Start your tasks this way:
HtmlDownloaderTask = new HtmlDownloaderTask(page.getHtml());
task.execute(page.getUrl());
//if you want parallel execution try this:
//task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,page.getUrl());
mTasks.add(task);
on the onPostExecute of MyAsyncTask:
int unfinishedTasks = 0;
for (HtmlDownloaderTask myDT : mTasks){
if(!(myDT.getStatus() == AsyncTask.Status.FINISHED)){
unfinishedTasks++;
}
}
if (unfinishedTasks == 1){
//We are all done. 1 Because its the current one that hasnt finished post execute
callWhateverMethod();
}
Well as you do know when an AsyncTask ends (when onPostExecute gets called):
one solution could be to create a method setProgressBarVisible() that keeps a counter and when first called sets visible, and a method setProgressBarInvisible() that decreases the counter and when zero sets the progress bar invisible.
:-? i think it's just a trick. you will return some message at onPostExecute of each Asyntask and compare it. (this message can contain a time, for example)
A official support of CompletableFuture was introduced since API level 24.
It's also available in Java 8 here.
Can use simply use something like:
taskA.thenCombine(taskB).thenCombine(taskC)
I would simply notify it at onPostExecute(), refer to onPostExecute and 4 steps in the document for detail and you can use EventBus to do some subscribe things.
This is a common question when you want to run a bunch of AsynTasks on a THREAD_POOL_EXECUTOR. It's much more faster than if you just call .execute() and all your tasks are done one by one.
So if you have multiple jobs and objects are not depending on each other states - try to run on a thread pool.
But the question is: how do I know that all of my tasks are done?
There is no built in methods in AsyncTask so you should do a little workaround.
In my case I added a static Hashmap field to my Asynctask class to keep track of all started and finished tasks. As a bonus of a map I can always know which task is currently in progress.
private static HashMap<Uri, Boolean> mapOfAttachmentTasks = new HashMap<>();
and ad simple three methods to access this map.
Important: they should be synchronized
public static synchronized void addTask(Uri uri){
mapOfAttachmentTasks.put(uri, true);
}
public static synchronized void removeTask(Uri uri){
mapOfAttachmentTasks.remove(uri);
}
public static synchronized boolean isTasksEmpty(){
return mapOfAttachmentTasks.isEmpty();
}
You want to add a new item to the tracking Map in an AsyncTask constructor and remove it in onPostExecute():
public AttachmentTask(Uri uri) {
this.uri = uri;
addTask(uri);
}
#Override
protected void onPostExecute(Attachment attachment) {
removeTask(uri);
if(isTasksEmpty())
EventBus.getDefault().post(new AttachmentsTaskFinishedEvent(attachment));
}
Everytime a task is finished it calls onPostEexecute and you check if it was the last task. If there is no tasks left - send a signal that you're done.
Now, here I used EventBus to send event to my Fragment but you can use a callback. In this case you should create an interface with callbackMethod, your Fragment (any of your UI components which are waiting for the event) should implement this interface and have that method. Then in AsyncTask constructor you get your Fragment as an argument and keep a reference to it, so you can call it's callback method when everything is done.
But I dont like such approach. First you need to keep the reference of your Fragment (or any other UI) in a WeakReference wrapper becasue you will get a memory leak when your fragment is dead (but still kept in memory becasue your AsyncTask has it's reference).
Also you would need to make a lot of checks and it will look something like that:
private boolean isAlive() {
return mFragmentWeakReference != null
&& mFragmentWeakReference.get() != null
&& mFragmentWeakReference.get().isAdded()
&& mFragmentWeakReference.get().getActivity() != null
&& !mFragmentWeakReference.get().getActivity().isFinishing();
yep, in production you should be a little paranoic and do all these checks :)
That's why you can use EventBus and if your UI is dead - whatever.
try this, maybe can help you...
final ImageUploader _upload = new ImageUploader();
_upload.setValue(getApplicationContext(), _imagepath, _urlPHP);
_upload.execute();
Runnable _run;
Handler _h2;
_run = new Runnable() {
public void run() {
_h2 = new Handler();
_h2.postDelayed(this, 1000);
Toast.makeText(getApplicationContext(), "not finished", Toast.LENGTH_LONG).show();
if (_upload.getStatus() == AsyncTask.Status.FINISHED) {
Toast.makeText(getApplicationContext(), "finished", Toast.LENGTH_LONG).show();
_h2.removeCallbacks(_run);
}
}
};
_h2 = new Handler();
_h2.postDelayed(_run, 1);
I'm running an activity, which has to download a fairly large image from the Internet, and then display it. This works: the download is done via an AsyncTask, and a progress dialog is shown. And when the download is finished, the activity showing the image is called.
The problem I have is that the real work is done in an external class. This as other activities call the same routine to fetch an image. So I can not immediately call postUpdate() to set the update as this call would be done in another class. Now I wonder how I can get the progress updates back to my AsyncTask so my progress dialog can show the actual progress made
The AsyncTask subclass currently looks like this:
private class StartShowImage extends AsyncTask<String, Integer, String> {
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
// Toon een dialog met draaiend wiel terwijl we de foto
// aan het ophalen zijn.
dialog = new ProgressDialog(ShowDeaddrop.this);
dialog.setTitle(R.string.progress_dialog_title);
dialog.setMessage(getResources().getString(
R.string.progress_dialog_fetching_image));
dialog.show();
}
/**
* De hoofdroutine; haalt de foto op.
*/
#Override
protected String doInBackground(final String... params) {
final String imageName = params[0];
String result = null;
try {
result = DeaddropUtil.getImage(context, imageName, ""
+ deaddropID, true);
} catch (final IOException e) {
Log.v(TAG, "Failed to download image " + imageName);
Log.v(TAG, "" + e);
}
return result;
}
/**
* Als we de foto hebben, start ShowImage.
*/
#Override
protected void onPostExecute(final String imageName) {
dialog.dismiss();
if (isActive)
if (imageName == null)
Toast.makeText(context, R.string.toast_show_image_failed,
Toast.LENGTH_SHORT).show();
else {
final Intent i = new Intent(ShowDeaddrop.this,
ShowImage.class);
i.putExtra("imageName", imageName);
startActivityForResult(i, SHOW_IMAGE);
}
}
}
isActive is a boolean that keeps track of whether this activity is active - it's set in onCreate and onResume and unset in onPause.
I've been looking into a broadcast intent - I've seen an example on how to send back such an intent to the main activity, but the problem is that the listener has to be registered/unregstered in onResume/onPause - and AsyncTask is a separate thread. So it seems this method can not be used safely that way.
Edit restating the question, hoping to get answers that address my question.
AsyncTask sets up progress dialog.
The onExecute() thread can directly update the progress dialog. No problem there.
The actual work is done in an external class, so the progress information is known by that external class, which has to communicate it back to the AsyncTask one way or another.
AsyncTask will have to have some kind of listener, or handler, or something that the external class can call back to, in order to give progress updates.
The question is: how to perform this last part of the process? What is a suitable listener? How to implement such a listener? Which thread does the listener end up in - the UI thread like .onPreExecute() and .onPostExecute(), or the work thread from .doInBackground()?
Solved by using a BroadcastIntent.
Main activity creates BroadcastReceiver, IntentFilter and progress dialog; AsyncTask registers/unregisters this receiver and shows/dismisses the dialog in onPreExecute/onPostExecute respectively.
With extra bits in onResume and onPause to not have active receivers when the activity itself is inactive.
It took me a while to understand this intent broadcast, but after that it was a very quick and easy implementation.
If I understand correctly you are wanting to update the progress bar from the async task. I would probably look at the onProgressUpdate of async task. http://developer.android.com/reference/android/os/AsyncTask.html
Your class StartShowImage doesn't have member. This object you have used to create toast also. if you make member Activity and construction declaration for it you can initialize it by constructor like this before you call it's public method which you want to do :
private Activity context;
public StartShowImage(Activity context){
this.context=context;
}
StartShowImage object=new StartShowImage(this)
object.postUpdate();
This should work and your calling class should extent Activity