Background
I have created a game loop to run the UI, but when I push the back button from the game screen (SingleGameActivity) I get to the level-selector menu called (StartLvlActivity). If I try to reenter the game or push the back button from StartLvlActivity I get a keyDispatchingTimedOut error (ANR).
When I wrote Log messages, the game activity (SingleGameActivity) was only paused, not stopped when I pushed the back button. And the game loop was never destroyed when I closed the activity.
08-03 09:56:12.130: I/Process(346): Sending signal. PID: 5763 SIG: 3
08-03 09:56:12.130: I/dalvikvm(5763): threadid=3: reacting to signal 3
08-03 09:56:12.333: I/dalvikvm(5763): Wrote stack traces to '/data/anr/traces.txt'
08-03 09:56:12.341: E/ActivityManager(346): ANR in com.coderogden.pongtennis (com.coderogden.pongtennis/.activities.StartLvlActivity)
08-03 09:56:12.341: E/ActivityManager(346): Reason: keyDispatchingTimedOut
Questions
1. Why isn't the game loop destroyed when I push the back button from the game menu? I mean, why isn't surfaceDestroyed called when onPaused is called (onStop is never called and I don't know why....).
How do I access "/data/anr/traces.txt"
How do I handle the errors?
If you can answer any of these questions I would appreciate it very much!
EDIT
This is the surface view class from where I start and destroy the thread (or apperently I'm just starting it as it won't shut down when I press "back"-button).
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
Log.d(TAG, " surface created");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, " surface destroyed");
}
EDIT 2 (with solution!)
The bug was fixed by changing this piece of code:
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
to this:
thread.setRunning(false);
WHY?
Is it because I need to stop the thread before destroying it?
Related
In my game, I have a thread. This thread is paused in onPause() in the activity and restarted in surfaceCreated in the main game class.
#Override
public void surfaceCreated(SurfaceHolder holder) {
if(gt == null) {
gt = new GameThread(getHolder(), this);
gt.setRunning(true);
gt.start();
}
}
The onPause(called from game):
public void onPause(){
... other stuff
if(gt != null){
try {
gt.setRunning(false);
gt.join();
gt = null;
}catch(Exception e){
e.printStackTrace();
}
}
}
But for some reason, when the screen is locked and unlocked, the thread does not start again. When I open the list of recent apps and reopen the app from there, the thread restarts.
Why is this happening, and how can I solve it?
You should start your thread in onStart(), not in surfaceCreated, because the surface is not recreated in this case (your activity is not destroyed), so the method surfaceCreated is not called when you unlock your phone. However, onStart() is.
So I have an app that uses a surface view that runs a separate thread for the UI. It was taken almost directly from the Lunar Landing sample app. The app also uses Bluetooth service on another thread but I am sure that this is not related to the problem because I can disable bluetooth all together and it still happens.
The problem in my app is that the app when closed and then reopened does not start running the UI thread afterthread.start() except it throws an error. In the Lunar example they have thread.start() in the onSurfaceCreated method. The problem is when I restart my app (it calls onPause then onSurfaceDestroy) the thread is already running and I get an error when I try to start it. My code for onSurfaceCreated, onPause, onResume and onSurfaceDestroyed is all the same as the example. I know I can use if (this.getState() == Thread.State.NEW) { but that seems like it will mask some of my other issues. I want to master the activity life cycle.
My question is how does the Lunar Lander stop the thread? And why is mine not stopping with the same code and running at the onSurfaceCreated method. Obviously I am missing something. As far as I know in the Lunar example the only thing that is called on the thread on a destroy is thread.join().
Edit 3: Here is the Lunar Lander Example Code if needed.
So these are the three override methods in my surfaceview...
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "surfaceCreated");
// start the bluetooth service
thread.startBluetoothService();
// start the game
//if (this.getState() == Thread.State.NEW) {
Log.d(TAG, "thread start");
// start running the thread
this.start();
//}
Log.d(TAG, "running to true");
// release the thread lock
setRunning(true);
}
// surfaceChanged is called at least once after surfaceCreated
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged");
// reset the surface size
thread.setSurfaceSize(width, height);
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "surfaceDestroyed");
// make sure to shut down the thread cleanly
boolean retry = true;
// stop the running thread
thread.setRunning(false);
// continuously try to shut down the thread
while (retry){
try{
// blocks calling thread until termination
thread.join();
// stop the bluetooth service
//thread.stopBluetoothService();
retry = false;
}catch(InterruptedException e){
//try to shut it down again
}
}
}
I am really pretty lost with all of this. Any help would be very appreciated, thanks!
Edit:
So I did a little more testing. When the user hits home(which exits the app completely) onPause, then onSurfaceDestroy like I said before. Then when it restarts I get onResume followed by onSurfaceCreated. I think my issue is that it is not calling onCreate when you reenter the app.
Some more questions...
What distinguishes the difference between a onPause and a onDestroy? I think my problem is that since onCreate is not being called I don't have a newly created UI thread which seems like it is still running.
Should the thread be stopped even on a onPause? Because then I am not garenteed to run onCreate which re instantiates the thread. Here is the onCreate code...
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wobble);
// get view and thread
wobbleView = (WobbleView) findViewById(R.id.wobble);
wobbleThread = wobbleView.getThread();
// Get local Bluetooth adapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// If the adapter is null, then Bluetooth is not supported
if (mBluetoothAdapter == null) {
// alert the user of bluetooth failure
Toast.makeText(this, "Bluetooth is not available, using internal devices sensors", Toast.LENGTH_LONG).show();
// set the data source to internal sensors - so we'll just use the devices accel
wobbleThread.setDataSource(WobbleThread.INTERNAL_SENSORS);
// bluetooth is supported so make sure its enabled and
}else{
// make sure bluetooth is enabled on the device
if (!mBluetoothAdapter.isEnabled()) {
Log.d(TAG, "starting request to enable bluetooth");
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
}
// all is well with bluetooth - use bluetooth
Log.d(TAG, "setting bluetooth to bluetooth");
wobbleThread.setDataSource(WobbleThread.BLUETOOTH);
}
// give the LunarView a handle to the TextView used for messages
wobbleView.setTextView(
(TextView) findViewById(R.id.text_accel),
(TextView) findViewById(R.id.game_msg),
(TextView) findViewById(R.id.text_score),
(TextView) findViewById(R.id.bluetooth_status)
);
if (savedInstanceState == null) {
// we were just launched: set up a new game
//wobbleThread.setState(wobbleThread.STATE_READY);
} else {
//wobbleThread.setRunning(true);
// we are being restored: resume a previous game
//wobbleThread.restoreState(savedInstanceState);
}
}
Edit 2:
Some logcat output
So this is what I get when the thread.start() is called after reopening the app.
11-18 22:50:44.104 4868-4868/com.bme.shawn.wobble E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.IllegalThreadStateException: Thread already started
at java.lang.Thread.checkNotStarted(Thread.java:871)
at java.lang.Thread.start(Thread.java:1025)
at com.bme.shawn.wobble.WobbleThread.startGame(WobbleThread.java:213)
at com.bme.shawn.wobble.WobbleView.surfaceCreated(WobbleView.java:94)
at android.view.SurfaceView.updateWindow(SurfaceView.java:580)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:240)
at android.view.View.dispatchWindowVisibilityChanged(View.java:7903)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1071)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1071)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1071)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1071)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1289)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1050)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5750)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
at android.view.Choreographer.doCallbacks(Choreographer.java:591)
at android.view.Choreographer.doFrame(Choreographer.java:561)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5406)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
And if I use the thread.getState check and bypass the error I get this when I close then reopen the app. (logs in basically everything) In this case when the app reopens the thread is not drawing at all. Which is very weird since without the check I get an error saying that the thread is already running.
11-18 22:59:13.444 5345-5345/com.bme.shawn.wobble D/WobbleActivity﹕ onPause
11-18 22:59:13.584 5345-5345/com.bme.shawn.wobble D/WobbleView﹕ surfaceDestroyed
11-18 22:59:17.794 5345-5345/com.bme.shawn.wobble D/WobbleActivity﹕ onResume
11-18 22:59:17.804 5345-5345/com.bme.shawn.wobble D/WobbleView﹕ surfaceCreated
11-18 22:59:17.804 5345-5345/com.bme.shawn.wobble D/WobbleThread﹕ running to true
11-18 22:59:17.804 5345-5345/com.bme.shawn.wobble D/WobbleView﹕ surfaceChanged
11-18 22:59:17.804 5345-5345/com.bme.shawn.wobble D/WobbleThread﹕ setting surface sizes
11-18 22:59:17.824 5345-5345/com.bme.shawn.wobble D/dalvikvm﹕ GC_FOR_ALLOC freed 3343K, 2% free 6585K/6668K, paused 13ms, total 13ms
11-18 22:59:17.844 5345-5345/com.bme.shawn.wobble E/IMGSRV﹕ :0: PVRDRMOpen: TP3, ret = 44
11-18 22:59:17.854 5345-5345/com.bme.shawn.wobble E/IMGSRV﹕ :0: PVRDRMOpen: TP3, ret = 50
Create a new instance of the thread in surfaceCreated() and start it. And call thread.join() in surfaceDestroyed()to destroy it.
I'm trying to build a custom player for a radio station, and they want to move from mpeg to aac to lower the bitrate (mostly for mobile users) however when i started the project i was testing it against the mpeg stream, When i change it to the new aac stream my player seems to lock for a period of time, then the UI thread is killed by android while the player is attempting to load the stream in the background. I do get some initial errors about it being unable to play, but maybe 30 seconds after the app is killed i get an orphaned async player re-spawning in the background with it's sticky notification and the stream starts playing?
the logcat output shows these lines around when it crashes
I/dalvikvm﹕ threadid=3: reacting to signal
I/dalvikvm﹕ Wrote stack traces to '/data/anr/traces.txt'A/libc﹕ Fatal signal 6 (SIGABRT) at 0x000008eb (code=0), thread 32258
I see the sticky for the orphaned async task and it's playing audio when i click it (to regain audio focus) but the main app/ui thread is obviously completely reset
The code that spawns the async task is as follows:
private class PlayAsync extends AsyncTask<String, Void, Boolean> {
private String url_to_play = "";
#Override
protected void onPreExecute() {
super.onPreExecute();
textView_info.setText("Buffering...");
}
#Override
protected Boolean doInBackground(String... params) {
url_to_play = params[0];
Bundle b = new Bundle();
b.putString("url", url_to_play);
Intent i = new Intent(MainActivity.this, MusicService.class);
i.putExtras(b);
try {
stopService(new Intent(MainActivity.this, MusicService.class));
} catch (Exception ex) {
//
}
startService(i);
return true;
}
#Override
protected void onPostExecute(Boolean result) {
isMuted = isMuted();
if (isMuted) {
textView_info.setText("Playing Muted!");
} else
textView_info.setText("Playing");
isBuffering = false;
}
#Override
protected void onProgressUpdate(Void... values) {
}
protected void onError(MusicService mp, int what, int extra) {
// The MediaPlayer has moved to the Error state, must be reset!
button_stop.performClick();
}
}
The problem I am having at the moment is that my pause is not always working.
What I have is an MediaPlayer in main activity that is operated via ActionBarSherlock and onClick listeners. MediaPlayer is using ArrayList with URLs of MP3 files(some of them 1sec long).
Pause code:
if (player.isPlaying()) {
if (player != null) {
player.pause();
swapPlayIcon(1);
isPaused = true;
pauseMenuButt.setVisible(false);
playMenuButt.setVisible(true);
}
}
swapPlayIcon(int) handles only visibility and drawable swaps.
Start code:
Iterator<Uri> iterUri = tracks.iterator();
while (iterUri.hasNext()) {
Uri tmpUri = iterUri.next();
try {
player.reset();
player.setDataSource(String.valueOf(tmpUri));
player.prepare();
player.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
EDIT
After more testing I have found out that the problem appear in "in between" state.
What I mean is that when my MP3 file is 2sec long and I click pause its not stopping becouse it just have ended reading one file and now moved on to the next one.
I have added:
} else {
pauseLocked = true;
}
to pause if statment and it does not land in it at all while testing.
So im not sure about the "in between" problem that I have found out previously.
Your pause button is not working because there's no way for its message to ever reach the MediaPlayer until you've started the last URL. You are entirely blocking the thread your MediaPlayer is running on (I'm assuming it's the UI thread since the MediaPlayer is in your main activity). If you're creating your MediaPlayer on the same thread as your UI, you should use the asynchronous version of prepare: prepareAsync. You need to respond once you receive the onMediaPrepared callback and then start the media. Once that happens you must do nothing and wait for the media to finish, only then should you load another url.
When testing several games with a standard game thread in Android 4.0 it works well until the Activity is shut down (pressing the home button etc), leaving the activity the application crashes with a nullPointer.
This even happens in the sample LunarLander that Google has programmed.
The problem is that Canvas becomes null when leaving the activity and that makes the application crash.
The error message from the LogCat is just below.
02-27 18:07:58.974: V/MainThread(2667): CANVAS android.view.Surface$CompatibleCanvas#4102bcf0
02-27 18:07:59.164: V/MainThread(2667): CANVAS null
02-27 18:07:59.164: W/dalvikvm(2667): threadid=14: thread exiting with uncaught exception (group=0x409c01f8)
02-27 18:07:59.174: E/AndroidRuntime(2667): FATAL EXCEPTION: Thread-108
02-27 18:07:59.174: E/AndroidRuntime(2667): java.lang.NullPointerException
02-27 18:07:59.174: E/AndroidRuntime(2667): at com.joakimengstrom.pong.MainThread.run(MainThread.java:49)
This is the code when starting a thread, with the Log.v you see above.
while(this.running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
Log.v(TAG, "CANVAS " + canvas);
canvas.drawColor(Color.BLACK);
...
And below is when creating the thread and shutting it down when the surface is destroyed.
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new MainThread(getHolder());
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
How do I exit the thread in a safe way without making canvas null?
In Android 4.0, SurfaceHolder.lockCanvas can return null when the surface is destroyed. So here:
try {
canvas = this.surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
Log.v(TAG, "CANVAS " + canvas);
canvas.drawColor(Color.BLACK);
Just surround your synchronized (surfaceHolder) block with if (canvas != null). This should not cause any issues with the behaviour of your application as it occurs when drawing is not needed.