Showing Dialog in SurfaceView - android

My game is drawn onto a SurfaceView. I am using a Dialog as a level completion screen, but cannot get it to show (Dialog.show()).
I keep getting the following error:
01-30 16:45:34.425: E/AndroidRuntime(3415): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I have a Game class which extends Activity and holds the SurfaceView. I think runOnUiThread() may be the solution, but after tireless searching have no idea how to implement it inside my SurfaceView.
Any help is appreciated and I will post my code if requested (just not sure which bits are actually relevant at the moment)

You're attempting to modify the UI thread from a worker thread which will give these errors. To prevent this try making a call to the runOnUiThread() method...
Game.this.runOnUiThread(new Runnable() {
public void run() {
customDialogObject.show();
}
});

Related

Android: runOnUiThread() Runnable Sequencing

I have two JNI native methods that callback Java methods in my UI.
1) Display progress..
2) Dismiss progress
Both of the above calls are definitely in sequence. They both call Java methods that create new runnables as follows:
m_Activity.runOnUiThread( new Runnable()
{
#Override
public void run()
{
DisplayProgressUpdate( m_ProgressPercent );
}
} );
--
m_Activity.runOnUiThread( new Runnable()
{
#Override
public void run()
{
m_Progress.dismiss();
}
} );
What I am seeing is that the dismiss runnable is happening before the progress update runnable completes. I would have thought that because they were called in sequence and because they are both being requested on the same (UI) thread that they would occur in sequence as well. Is this not the case?
Is this why I should be using something such as a Handler to synchronise/sequence these calls?
EDIT: OK, I implemented a Handler and still observed the same behaviour. It was actually my debug that confused me. It looked as though the Dismiss Java code was happening before the progress update had completed, but what it was in fact was the Java debug printing as soon as JNI called the Java method which did the posting to the handler - not the actual runnable thread itself. So.. tajonn07 was right in a way - the dialog box was closing before I had a chance to see it and my debug lead me astray. Thanks for helping guys.
I suspect that what you're seeing isn't the dismiss being executed first, but instead it's being executed so quickly after the display that it doesn't even show.
I would suggest using a handler. But even with a handler, if it's in your UI thread, it will freeze your screen.
It's a bit messy, but you could drop both those blocks of code inside another thread with a handler. It's not the cleanest solution but it should do the trick!
runOnUiThread is not added on queue in android , this is called immediately when it invoke.
If you want queue / sequence(ie one after another), you have to use Handler.

Android runOnUiThread/AsyncTask cannot resolve CalledFromWrongThreadException

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);

Handler Does Not Execute Run

This feels like it should be a very simple task but I am having a lot of problems with it. In my program I have extended the WebView class for my own and am trying to add it to a layout programatically. Here is my code:
Looper.prepare();
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
MyWebView webView = new MyWebView(context,1,2,3,4);
appState.projectWebView.add(webView);
addView(webView);
}
});
When I run this code it does not execute. I have no idea why. Thanks for you time.
You cannot create a Handler outside of an UI thread. Well, you can, but you will have to turn that thread into a message queue with much more than just Looper.prepare().
What you need to do is pass an Activity to the class that contains the code in your sample, and call runOnUiThread() on it. Alternatively, you can pass a Handler created on a UI thread, for instance create it at the thread that runs your Activity UI, and then call post on that handler.
Note that this is awful advice, you seem to be trying to do things against the Android framework. But, without further information of what you are actually trying to do, there is no much that can be said.

Android Fragments and Separate Threads

I am using the android ViewPager : http://developer.android.com/reference/android/support/v4/view/ViewPager.html
This class allows you to basically slap a bunch of Views or Fragments into a group and page through them easily. My problem occurs when I try to create a separate thread to randomly shuffle through the views.
I extended a thread which would call setCurrentItem on my ViewPager which I passed in through an argument. When I did that I received this:
java.lang.IllegalStateException: Must be called from main thread of process
I figured all I would have to do to fix that would be to call a method from my activityfragment so I created changePageFromActivity to do the dirty work and called that by passing my activity to my thread. But that didn't work either. Below is the full stack trace:
FATAL EXCEPTION: Thread-11
java.lang.IllegalStateException: Must be called from main thread of process
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1392)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:431)
at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:160)
at android.support.v4.view.ViewPager.populate(ViewPager.java:804)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:433)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:405)
at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:397)
at com.lyansoft.music_visualizer.MusicVisualizerActivity.changePageFromActivity(MusicVisualizerActivity.java:144)
at com.lyansoft.music_visualizer.ShuffleThread.run(ShuffleThread.java:38)
After doing some research I gathered that Fragments are easily destroyed and result in a different thread so to prevent any problems ViewPager just made sure that the method I wanted had to be called from the main activity.
So my question is:
Is it possible to inject something along the lines of
run() { while(condition) { methodIWantHere(); sleep(timeout); } }
inside my main activity's thread without disrupting it?
OR
What is a better design pattern to achieve the effect I would like? (which is to change the view at consistent specified intervals)
A Handler would be an obvious choice.
final Runnable changeView = new Runnable()
{
public void run()
{
methodIWantHere();
handler.postDelayed(this, timeout);
}
};
handler.postDelayed(changeView, timeout);
Try to execute your piece of code by using
runOnUiThread(Runnable action).

MapView and AsyncTask

I am trying to use an AsyncTask class that I created to update a MapView. The problem is I am getting this error when I make my call to execute the AsyncTask:
"Can't create a Handler inside a thread that has not called Looper.prepare()"
I have tried running the Task on the UI Thread using
Handler hand = new Handler(Looper.getMainLooper());
hand.post(new Runnable() {
public void run() {
new RxThread().execute();
}
});
But that just gives me the same error. I realize that my MapView doesn't call looper prepare, and that I'm having troubles getting this to work since I'm running the MapView on a seperate activity rather than the Main Activity. Does anyone have a good solution to this?
IIRC, your error is because you are first referencing AsyncTask on a background thread. You can only create and execute() an AsyncTask on the main application thread.

Categories

Resources