I'm having trouble trying to put a ProgressDialog into my app. In my GameEngine class (which doesn't extend anything), I have the code shown below. The first line produces a runtime exception, and although I came across this thread that seems to be about the same error: Android TimerTask throws RuntimeException if Show ProgressDialog is added in run(), I don't really understand how to implement the solution. Any help would be much appreciated, thanks.
//Create ProgressDialog
ProgressDialog dialog = ProgressDialog.show(context, "",
"Loading...", true);
//Set Clusters before level starts
for (int i = 0; i < 80; i++)
{
updateBacteria();
updateAttraction();
checkCollisions();
moveObjectsAwayFromWalls();
}
dialog.dismiss();
You can only show dialogs in the UI thread (which is your main class which extends Activity). To be able to do this, you can write a Handler and use it to send messages from the non UI thread to the UI thread. Android have an example of this in their ProgressDialog example. View the snippet of code they have under "Example ProgressDialog with a second thread".
You can also follow the same method as written in the answer of the link you provided, although a Handler is a more robust approach.
If this method is not running in your Main Activity thread, you should change it. how? Set a Handler in the main activity and pass it to the thread (above). In the handler you should implement the GUI related part of your method (i.e. ProgressDialog). When you need to show the ProgressDialog, just call your Handler and than keep processing (your loop in this case). Same for the dismiss().
Related
I have a web service request in my Android app which might take a second or two to run. I have an event handler which fires when it completes. I am trying to show a dialog to show the progress of this request.
So far I have this:
var progressDialog = Android.App.ProgressDialog.Show(this.Activity, "Please wait...", "Communicating with server...", true);
new Thread(new ThreadStart(delegate {
WCFClient WCF = WCFService();
WCF.TestCompleted+= TestCompleted;
WCF.TestAsync(GetID());
progressDialog.Dismiss();}
)
).Start();
The problem is obvious, the dialog displays and disappears in a flash. I am declaring var progressDialog in the UI thread. How can I reference it again in an event handler?
I would normally use RunOnUIThread but I cannot target "progressDialog" that way as it does not exist in my layout.
It seems like this should be easy - am I missing something in my approach?
There are a few issues with your code. The first is there is a memory leak. You are holding onto the progress dialog in your thread. If the user rotates the device, your Activity will be killed but the garbage collector won't be able to garbage collect the reference because your Thread is holding onto it.
Secondly, you cannot update the UI from another thread, you have to update on the UI thread.
You need to use a Handler, pass a reference to the other threads (in the constructor), then when needed send a message to UIthread to dismiss the progress dialog.
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (progressDialog.isShowing()) progressDialog.dismiss();
}
};
in the thread, do this:
mHandler.obtainMessage().sendToTarget();
Also there is another issue in the code, you don't seem to check if you completed the task, BEFORE dismissing the dialog.
I'm working for an Android app and implementing a ProgressBar by using AsyncTask class.
The problem is that on some devices, it causes "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views." in onPostExecute. On those devices, the problem occurs 100%. On other devices, it works fine.
public final class MyAsyncTask extends AsyncTask<String, Integer, String>
{
private ProgressBar progress;
private ListActivity activity;
public MyAsyncTask(ListActivity activity, ProgressBar progress)
{
this.progress = progress;
this.activity = activity;
}
protected void onPreExecute()
{
this.progress.setVisibility(view.VISIBLE);
}
protected String doInBackground(String[] arg0)
{
// getting xml via httpClient
return string;
}
protected void onPostExecute(String result)
{
this.progress.setVisibility(view.GONE);
}
I don't understand why onPostExecute does not run on the UI thread, on those certain devices.
Next, I tried to call it with runOnUiThread, to make absolutely sure that it runs on the UI thread.
runOnUiThread(new Runnable() {
#Override
public void run() {
ProgressBar progress = (ProgressBar)findViewById(R.id.some_view_progressbar);
MyAsyncTask task = new MyAsyncTask(activity, progress);
task.execute();
}
} );
Even this did not solve the problem. The same exception still occurs.
From Log, I confirmed that Thread.currentThread().getId() is certainly different from the app's main activity's thread inside the handler.
I'm stuck. Any advice will be appreciated.
NOTE:I edited the sample code (not a real code) above to fix the wrong method name and missing "return string".
I will add more information later.
I don't see anything wrong with MyAsyncTask itself, but there are still other things that can go wrong.
Starting the AsyncTask
From the Android Docs
Threading rules
There are a few threading rules that must be followed for this class
to work properly:
The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
You don't show where you normally instantiate, and execute the task, so make sure that you do this in code that's already on the UI/main thread. Note that the first bullet point above might explain why this works for you on some devices, and not on others.
Creating the View Hierarchy
The message tells you
Only the original thread that created a view hierarchy can touch its views.
and you're assuming that this is because your async task is (strangely) trying to modify the UI on a background thread. However, it is possible that you get this error because the async task modifies the UI on the main thread, but the UI (ProgressBar) was not created correctly in the first place.
See this question for an example of how you can erroneously create the view on the wrong thread (anything other than the main thread), and get this same error.
More
I would, however, like to see exactly where you are logging the thread ID, and what value(s) you're getting. If you check out my first two suggestions, and they don't solve your problem, then we may need more information.
You also mention a Handler (?), but don't show how or where you use that. Normally, using AsyncTask removes the need to use Handler, so I'm a little worried about how you might be using that.
Update
Per the discussion in comments below, it looks like the issue here is the one discussed in this question. Some code, probably running on a background thread, is first to cause the AsyncTask class to be loaded. The original (pre-Jelly Bean) implementation of AsyncTask required class loading to occur on the main thread (as mentioned in the Threading Rules above). The simple workaround is to add code on the main thread (e.g. in Application#onCreate()) that forces early, deterministic class loading of AsyncTask:
Class.forName("android.os.AsyncTask");
Make sure you are invoking aysnctask.execute() from the main thread only.
Write a handler in UI thread and call the handler from onPostExecute. It will solve the problem.
Something like this. Have a handler in UI thread (main thread):
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
//run on UI Thread
}
};
and call in onPostExecute() like this:
handler.sendEmptyMessage(MSG);
1) FOA PD (ProgressDialog) can be created only from Activity, doesn't it? Please provide an useful example if it is really not.
2) If a PD should be created in separate thread could it be created and showed if it's thread doesn't do anything at all? I mean something like this (assuming mProgressDialog is a property of the class):
new Thread(){
public void run(){
mProgressDialog = ProgressDialog.show(appContext,
appContext.getResources().getString(R.string.progress_wait),
appContext.getResources().getString(R.string.progress_db_installing),
true);
}.start();
As I understand the thread dies immediately after executing run() cause there is nothing to do and so PD doesn't show. It should have some processing code or at least an empty cycle with some manageable condition
3) if PD should be created in the main thread should it be created only at the end of OnCreate() method or in the body of some method called/caught(by some Listener) started in OnCreate() method?
4) PD by itself doesn't suspend any thread while displaying, does it? So the code continues executing after the show() method . I mean the show() by itself doesn't suspend/pause the thread cause I guessed it does.
1) Not sure how it's relevant unless you can come up with a reason to create a ProgressDialog outside of an Activity context; I think the answer is "no," though.
2) No, you can't create a dialog from a background thread directly. Have you tried your code? It'll die with an exception with a helpful traceback. See any one of a number of SO questions about how to call back to the UI thread to do things like show dialogs.
3) You can create it anywhere in your activity; for example, it's common to do this in onPreExecute() in an AsyncTask, which may be triggered from an onClick callback.
4) No.
Error
05-12 11:56:45.793: ERROR/AndroidRuntime(505): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
what i have done
i have a list view inside my activity and i need to populate the listview by doing the following:
mylistview.setAdapter(new CustomAdapter());
now there is already too much pressure on my UI thread so , thought of calling this method inside a AsynTask.
Another problem
there is a progress dialog that shows in my acitivity when the user clicks the button to populate the listview. when i put everything inside one thread the progress dialog does not show. i had asked a question on stackoverflow about why the progress dialog does not show and i had got a reply saying that i need to put all the extra tasks inside another thread.
i have also read the updating UI in android given on android developer website:
but over there all we do is make a new runnable and post the runnable to the Handler of the UI thread so that when the UI is free, the runnable will be executed.
But how does the above solve my purpose? i mean the UI thread is still executing the instructions.
The only way i can take the load of the UI thread is if i make another thread and put all the work over there... but android does not allow this?
what is wrong with my understanding(if there is anything wrong)? How do i solve this problem
thank you in advance.
Android does allow you to put the extra work in a different thread AND publish the results on the UI thread, using AsyncTask. Add the UI update stage in the onPostExecute() method of the AsyncTask and you should be good-to-go. onPostExecute() is performed on the UI thread, the example in the AsyncTask Documentation is a great one.
Also, if you build your application properly, and don't use graphics a lot, there should not be too much work for the UI thread during the application run. Move everything that doesn't absolutely bound to the UI on a separate thread. AsyncTask is a very convenient way to do it.
AsyncTask is the correct way to solve your problem. Where you are running into difficulty is exactly what to put into the AsyncTask. Call mylistview.setAdapter(); from the onProgressUpdate or onPostExecute methods. So do something like this:
void setProgress(Integer progress){ myprogressbar.setValue(progress); }
void setAdapter(CustomAdapter result){ mylistview.setAdapter(result); }
private class LongRunningTask extends AsyncTask<String, Integer, CustomAdapter> {
protected Long doInBackground(String... urls) {
CustomAdapter res = null;
// do all the work to BUILD the custom adapter, calling publishProgress() as progress gets made
publishProgress(<progress value>);
return res;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress);
}
protected void onPostExecute(CustomAdapter result) {
setAdapter(result);
}
}
That should fix the threading issue and let you set the progress bar.
I am developing my first Androïd application and I'm facing a problem when I want to display a ProgressDialog to indicate that a process is being run.
In my application, the user triggers a time consuming task by pressing a Button. The "OnClick" function of my "OnClickListener" is called when the user presses the Button. In this function, here is what I'm currently doing :
- creation and configuration of an instance of the ProgressDialog class,
- creation of a thread dedicated to the time consuming task,
- attempt to display the ProgressDialog using the "show" method,
- start of the thread,
- main Activity suspended (call of the "wait" function)
- wake up of the main Activity by the thread when it is finished
- removal of the ProgressDialog by calling the "dismiss" function.
Everything works fine (the result of the long task is correct) but the ProgressDialog nevers appears. What am I doing wrong?
Thanks in advance for the time you will spend trying to help me.
You should not call wait() to the Main Activity/UI thread, because this is actually freezing the UI including your ProgressDialog, so it has no time to fade in and will never be shown.
Try to use multithreading correctly: http://developer.android.com/resources/articles/painless-threading.html
final Handler transThreadHandler = new Handler();
public void onClick(View v) {
// show ProgressDialog...
new Thread(){
public void run(){
// your second thread
doLargeStuffHere();
transThreadHandler.post(new Runnable(){public void run(){
// back in UI thread
// close ProgressDialog...
}});
}
}.start();
}
I would suggest using AsyncTask, as it's purpose is exactly to handle this kind of problem. See here for instructions how to use it. Note that the linked page in Floern's answer also recommends the use of AsyncTask.
You would need to do the following:
subclass AsyncTask
override it's onPreExecute() method to create and show a ProgressDialog. (You could hold a reference to it in a member of your subclass)
override it's doInBackground() method to execute your time consuming action.
override it's onPostExecute() method to hide your dialog.
in your activity, create an instance of your subclass and call execute() on it.
If you make your subclass an inner class of your activity, you can even use all of your activity's members.