I have a game using OpenGL. I've built off of the examples, for the most part, having the Main Thread, Renderer Thread (GLSurfaceView.Renderer) and added a GameLogic thread as well. When the game is executed, everything seems to run through perfectly. When back is pressed and onPause() is fired, I'm also firing the GLSurfaceView()'s onPause, but I'm having a "crash" at that point. Here's the MainActivity's onPause:
#Override
protected void onPause() {
Log.d("Main", "Pre- Super onPause");
super.onPause();
Log.d("Main", "Post- Super onPause");
mGSGLView.onPause();
Log.d("Main", "Post- GL onPause");
}
Each log point is reached except the last. In logcat, immediately following the "Post Super onPause" line, I get an Activity pause timeout.
I am not overriding onPause in the GLSurfaceView class... and as far as I know this had been working for me for some time, but recently started occurring when I started getting a completely black screen on the second time I tried to run my game which sits until finally getting an ANR. 95% of my game runs natively. Similar to the San Angeles example, the Renderer calls to onDraw, for instance call a NativeDraw function instead of java. The same is true for the onSurfaceCreated, onSurfaceChanged, and I also call a native GameLogic method in the logic thread (basically all that is called there is a thread sleep and that logic method.)
I hope I've given enough information, please let me know if there is anything else I should be providing.
EDIT - Well... I've actually narrowed down the issue to a native function call where I'm freeing certain pointers that had previously had memory allocated for them with malloc(). My code there looks okay, but if I omit the call, everything works okay, so my free() calls must be corrupting something...
Okay, I'm only answering this myself because the solution was so specific to my own code that no one would have been able to.
If you have memory allocated on the native side, take care not to free it until after you are sure no other code will be trying to access it.
Related
Is it possible that activity lifecycle callbacks get interrupted before executting all its code? can a callback interrupt another?
https://developer.android.com/guide/components/activities/activity-lifecycle
In this documentation it recommends not implementing heavy work in onPause for example as it has a "very brief execution", who controls that? how? does the system calls the next lifecycle callback even if onPause did not finish execution yet?
Docs suggest that onResume()/onPause() execute rapidly for better user experience, because these methods are called frequently. Android activity callbacks are always executed in a sequence. They may be interrupted only by an uncaught exception which will terminate the whole app.
Those calls are what will influence UI mostly (along with onCreate, onCreateView) and if they take a long time to complete their code the user will most certainly know about it.
Say you have this in your onResume() method
OnResume(){ super.onResume; updateView(); }
Something standard, this is what normally happens before the fragment or activity gets “resumed”.
If your updateView(); required 2seconds to complete, weather it’s loading data or creating objects, that’s 2 seconds that the UI is “frozen” and the user cannot use the app, or could mistake as “is broken”
If you have the theoretical same scenario for onPause() then that too is 2 seconds of “frozen” time in the app
It is preferred to have the “updateView()” (essentially your onResume and onPause methods) complete as quickly as possible so that the user does not notice any “lag” in the application.
I don't know/understand something about Android callback methods and their implementation.
I have a simple Activity with implemented callbacks like:
#Override
onResume() {
Log.i(TAG, "onPause()");
}
#Override
onDestroy() {
Log.i(TAG, "onDestroy()");
}
onButtonPressed() {
while(true) {
Log.i(TAG, "onButtonPressed()");
SystemClock.sleep(1000);
}
}
When I start my App and change screen orientation I can see onPause() and onDestroy() logs in console.
But, when I press test button (onButtonPressed()) and a long lasting procedure starts (while true) I can see in console only onButtonPressed() logs. I can rotate my phone, and I see that activity is turned according to orientation, but no calls to callbacks onDestroy() and onPause() (no logs in console).
Is my Activity still recreated during orientation change? Why callbacks are not called? Are they called on some other thread?
Can anyone clarify for me this situation and explain how exactly Android performs calls to callback methods?
Tested on AndroidN.
Thank You.
UPDATE:
Looks like it was a mistake from my side.
When I was rotating my phone, I saw that screen is rotating, but haven't understood that activity isn't... It was just placed on one side of screen (sought it was a developer preview bug on Android N). And that confused me :D
Thanks for answer and help.
Unless specified otherwise in documentation all method calls and callbacks are typically handled on the main thread (UI thread).
This goes for all the life cycle methods such as onResume() and onDestroy() in your example as well as UI callbacks such as onButtonPressed().
In the second example where you enter the infinite while loop, you completely tie up the UI thread so none of the life cycle methods can run anymore and the user can no longer interact with the application. This is why when you rotate the device you do not see the log statements being printed. The current Activity is never destroyed and a new Activity cannot be created because you have the thread responsible for doing so in an infinite loop.
This is also why Android documentation recommends moving all long running operations off the UI thread so that you do not negatively impact the user's experience.
I am using this for some signal processing. It all works fine except when the screen goes off and the display was rotated at the time. Normally screen off just produces an Activity onPause(), but when the screen is rotated it goes: onPause(), onStop(), onDestroy(), onCreate(), onStart(), onResume(), onPause(). Ie it appears that Android 'unrotates' the screen first.
Unfortunately this seems to make AudioRecord crash or freeze in some unknown way.
The basic way I have it all operating is that in the onResume() a new thread is started and this instantiates an AudioRecord instance, sets it up, starts recording and then sits in a loop:
keepProcessing = true;
while (keepProcessing)
// read a block of data and process
In the onPause() in the UI thread keepProcessing (a volatile variable) is cleared, and it then waits for the worker thread to stop.
if (thread.isAlive()) {
keepProcessing = false;
thread.join();
}
thread = null;
When the other thread drops out of the keepProcessing loop it stops recording, releases the AudioRecord resources, drops the AudioRecord instance and terminates.
As a general start / stop mechanism this all works fine. Checking with breakpoints and adb everything seems to happen in the right order. It just doesn't work in this screen rotated scenario. I can only think it is because of the very rapid new Activity onCreate() etc after everything stops in the old Activity. Ie there is something in the AudioRecord that is still busy. What happens is everything freezes up when one tries to turn the screen on again.
If I dummy the AudioRecord code so that it doesn't actually start recording, the loop just sits and sleeps, and then doesn't stop recording again, it is all OK. It only goes wrong once the recorder has gone into recording mode.
Any advice?
When you rotate the device, your activity is actually stopped and destroyed. If you have background threads doing work, this totally screws you over. The best way to fix it is to tell Android not to do that, by adding android:configChange="orientation" to your manifest for that activity. Yeah, it's a bad architectural decision by Google and annoying to the point where I almost suggest adding that for all activities.
Yes, I can see that, and I've just found what was happening, which was sort of that.
Owing to an error where the keepProcessing flag was initialised, there was a race condition that could leave the background thread running when the Activity stopped.
"If you have background threads doing work [when the activity stops], this totally screws you over"
You are dead right. It does.
I have repeatedly read that onPause() and onResume() must be called for the GLSurfaceview during the onPause() and onResume() methods of the enclosing activity. I gather that this is to stop and restart the rendering thread to save resources. However, when I log each iteration of the onDraw method of the rendering thread, I see that onDraw stops being called when I press the home or back buttons, and when I return to the app, onSurfaceCreated and onSurfaceChanged get called, and then the onDraw call resume - even after I remove the calls to the GLSurfaceview's onPause() and onResume() methods in the corresponding activity methods. If the rendering thread is not being stopped, how am I seeing this behavior? Is the rendering thread still somehow alive even though onDraw is no longer being called? And if the rendering thread is being shut down automatically when the activity loses focus, then why is it necessary to call the GLSurfaceview versions of onPause() and onResume()?
It seems like a bug in GLSurfaceView where it tries to be clever by tolerating you forgetting to call onResume() and onPause(). I'm not sure if this is deliberate or not.
Leaving the calls out works almost always, but it can cause odd behaviour and crashes in some situations. I'm not exactly sure what those situations are, but I have traced a few weird crashes back to missing onResume()/onPause() calls. Something gets slightly messed up with the state machine in GLSurfaceView when you don't call them - it looks like the fallback logic to 'automatically' resume and pause doesn't get triggered in 100% of cases.
I am experiencing a very strange phenomenon (test device: HTC Desire HD, Android 2.3.5). I know that System.gc() is needless and discouraged, and I don't try to suggest otherwise, but the point is that it shouldn't cause issues either (i.e. it should be useless at most).
I have an application which contains a GLSurfaceView in its view hierarchy. The GLSurfaceView is instantiated and added in the Activity.onCreate(). Normally, the application works like this:
User starts the app and goes to mainmenu
User chooses a mainmenu item which sets the GLSurfaceView to View.VISIBLE
User plays with the in-built game on GLSurfaceView
User goes to mainmenu and exits the activity (=> Activity.finish() is called)
My Activity.onPause() looks like this:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
So far so good, everything works fine. However, issues appear after I add the following code to onPause() (for the case when the user exits the game from the mainmenu):
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
System.gc();
}
In details: if the Activity is started for the first time (= i.e. the app process didn't exist before), everything works fine. However, starting from the 2nd start of the activity (= after the first exit from the mainmenu, i.e. after the first Activity.finish()), the framerate of GLSurfaceView is reduced by 40-50%, the in-built game becomes slow.
If I remove the System.gc() call, the problem disappears. Moreover, if I do the following, it also gets rid of the problem:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
// 1. get layout root of View hierarchy
// 2. recursively remove (detach) all Views
// 3. call GC
System.gc();
}
I didn't add concrete code because it's complex, so I used comments. If I just detach the GLSurfaceView via removeView(), it is not enough. The entire view hierarchy needs to be cleared.
Note that I couldn't find any memory leaks (no Activity leak via drawables/statics etc.). Moreover, of course, the gameThread properly exits when the app is closed (I just didn't include its source code).
Any ideas, guesses? Apparently, System.gc() seems to cause some issues for the Activity/layout destroying mechanism of Android. Again, as I said, if I remove System.gc(), the problem disappears.
I have experience of Android Game Programming. I used to clear all the view in hierarchy because when running threads if you call System.gc() sometimes it happens that your thread has a reference to some of your view, even if you call system.gc() this view won't get removed and if you keep playing again and again this game you will notice that your heap memory is started growing.
It depends upon the memory leak, if you are leaking some KB memory it will take more time to crash your game. The best way it to use Eclipse Memory Anlyser (Eclipse MAT) and compare your stacks.
Step1:
take memory snap shot when you start your game for first time
Step2:
take memory snap shot when you start your game second time
Step3:
Now compare your both stacks of snapshots it will tell you the difference.
It is a very useful tool. I was having huge memory issues in my game Apache Attack. I fixed them using this awesome tool.
Follow this ECLIPSE MAT TUTORIAL