My game is working correctly except in the case where I press the HOME button then resume. What needs to be done to use the textures again? I have tried calling onPause and onResume on the GLSurfaceView (when the activity's onPause and onResume are called).
Any ideas what I could be doing wrong?
If all else fails, reload the textures:
Pseudocode
for tex in textures:
if glIsTexture(tex.opengl_name) == false:
glGenTextures(1, &tex.opengl_name)
glBindTexture(tex.texture_target);
glTexImage(..., texture.image);
Even if you fixed your problem, just to give a bit of explanation that might help others.
Android does not guaranty to keep the OpenGL context alive when the activity is paused.
You have to recreate every OpenGL-resources on resume (texture in you case, but also VBOs etc etc).
Since API 11, you can ask kindly Android to keep the context, but there is no guaranty it would.
After trying:
do not call GLSurfaceView#onPause/onResume in Activity's onPause/onResume
call GLSurfaceView#onPause/onResume, but also set GLSurfaceView#setPreserveEGLContextOnPause(true)
Both of cases fix the HOME-resume-black-texture issue.
Guess Android implementation failed to re-create the EGL context when resume. Since onPause/onResume are required to call, should always set setPreserveEGLContextOnPause to true.
Related
I'm successfully drawing simple shapes on my GLSurfaceView using OpenGL ES 2.0. So far i'm not using any textures. The problem is that when i re-run for the second time the activity with GLSurfaceView the screen is empty (black), shapes are not being drawn again. I have to reinstall the whole app to make it work again.
I don't have any special methods onPause & onResume so i assume the new instance of GLSurfaceView should redraw the screen each time i re-run the application.
Does anyone have any clue what am i missing here?
I don't have any special methods onPause & onResume
That's likely your problem. You need to follow the documentation of GLSurfaceView, specifically what it says under "Activity Life-cycle":
A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients are required to call onPause() when the activity pauses and onResume() when the activity resumes. These calls allow GLSurfaceView to pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate the OpenGL display.
I have a stock Nexus 5 running 4.4.2 (using ART if it matters) and I've found an interesting scenario. I have this as my onDestroy():
#Override
protected void onDestroy() {
super.onDestroy();
t.setText("onDestroy");
t.show();
}
It's a GPS oriented app so I'm up and walking around. I am using the technique mentioned in this question to show a lot of debug toast messages.
Anyway, when I rotate my app, the toast appears. I understand that the activity is destroyed and recreated for the new orientation, but how can I know what's really going on? How can I tell when my app is REALLY getting destroyed and not just being rotated? Similar to this question, I want to log out when a particular activity is destroyed.
Since Honeycomb, the isChangingConfigurations() method can be queried to check whether the Activity is being recreated due to configuration changes. Alternatively, the isFinishing() method can be queried on any API level to check whether the Activity is actually being finished, or is only being destroyed temporarily by the system.
As far as I can determine, the two methods should always return mutually consistent results in practice. The only point where they might have diverged is when the system kills the process to clear memory, but there are no callbacks or interaction with the app at that point.
The documentation of the onDestroy() method mentions the use of the isFinishing() method:
Perform any final cleanup before an activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
You can put it in a fragment with setRetainInstanceState(true) set. Place your code in the onDestroy() method of the fragment. Then, the fragment will not be destroyed on orientation changes.
First of all, you should not use onDestroy() to do anything because its not guaranteed to be called. I would put things on the onPause() method; I wouldn't even put things in onStop().
Also, Im not sure why you want to log out a user when they navigate away from the app. I would rather implement some kind of timer on the app or server to log out after x time.
Now, the answer lies in the documentation: http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
You might want to override onConfigurationChanged so that your activity is not restarted.
I found a couple of solutions which are really just patterns to detect when the screen rotates. Alternatively, you can determine that the device was actually destroyed by checking some static data member to see if it was initialized or not.
Configuration changed solutions:
The first one involves handling all of the configuration changes in the onConfigurationChanged callback.
"Note that this will only be called if you have selected
configurations you would like to handle with the configChanges
attribute in your manifest."
The second involves listening for Display.getRotation() which returns a Surface.ROTATION_* object. Which is the new orientation of your screen relative to the natural state of the device orientation.
Again, you can use the configuration changes along with the static member.
Add ConfigChanges.UiMode flag to the ConfigurationChanges attribute for your MainActivity class, and this solves the problem.
More details: Android Launcher "OnDestroy" gets called twice
I have a GLSurfaceView renderer and method onSurfaceCreated is called only once (basically only when activity is created or re-created).
I need to test behavior when EGL context is lost and surface is recreated during normal activity life cycle, however I am unable to put app into such situation no matter what I do.
Is there anything that I can do to simulate situation in which is onSurfaceCreated called again? How to make my app lose EGL context?
Edit: After a few days I noticed that the problem was gone and the app started to behave properly as said in specs:
public abstract void onSurfaceCreated (GL10 gl, EGLConfig config)
Called when the surface is created or recreated.
Called when the rendering thread starts and whenever the EGL context is lost.
The EGL context will typically be lost when the Android device awakes
after going to sleep
Before that, every time the device went to sleep, the activity was destroyed and recreated on awake which was preventing me to test if the code in OnSurfaceCreated worked well.
Now it behaves much better when sleep/awake occurs - it usually does not destroy the activity and just calls OnSurfaceCreated method which is the situation I was aiming for.
I'm not sure but the only thing that i can think of is to switch the GLSUrfaceView with a new one using the same GLSurfaceView.Renderer.
That should create a new EGL context and call the onSurfaceCreated() a second time.
According to the documentation, GLSurfaceView.Renderer.onSurfaceCreated is called when the surface is created or recreated.
Called when the rendering thread starts and whenever the EGL context is lost. The EGL context will typically be lost when the Android device awakes after going to sleep.
Have you tried locking your device's screen and unlocking it?
Maybe you can look at onScreenStateChanged method but it's available for API 16 or higher. If you mean "pausing and resuming the activity" by "normal activity life ciycle" maybe you can use onPause or onResume to track this.
What exactly do you want to do with onSurfaceCreated() ? You can force the rendering by requestRender() method of SurfaceView and do the necessary calculations inside onDrawFrame() of GLSurfaceView.Renderer
If your view is in a fragment, you can detach and re-attach the fragment.
I've got an app that is complete, apart from the lifecycle that, guiltily, I left as last.
Things seem to work good in all cases, but one:
When I pause the app by pressing the power button, it correctly pauses and restarts when resuming.
When I rotate the screen it correctly resizes.
When I pause AND THEN rotate the screen, its behaviour is different and the app resets.
Now, obviously it's a problem of my internal code, but to help me in the debugging, I would like to understand what's the difference between the three lifecycles. Or better, if the THIRD example is some how different from the first two.
With debugging I saw that all of three do this (I resemble by mind, I hope to be right):
onSaveInstanceState()
onPause()
onStop()
onStart()
onResume()
Is it right? Or do I miss some lifecycle steps in the third example, above?
Thank you.
Please specify what you mean by the app resets.
When you press the power button, most likely your app stops. That's why you see onStart() being called after stopping (not: pausing) your app.
Given that, is it really the case that the third scenario you describe is a combination of the first two? In other words, how do you pause your app in the third scenario?
Update Regarding your comment: There is a difference between your Activity being stopped and your Activity being destroyed.
Rotation is a configuration change. In this scenario, your Activity is going to be destroyed and re-created. You can handle this case separately if you must (it depends) through the onRetainNonConfigurationInstance() callback and the getLastNonConfigurationInstance() method. Android will call onPause(), onStop(), onRetainNonConfigurationInstance() and onDestroy() in this order, and then continue with onCreate() etc and it will not handle any events in the meantime so that they won't get lost.
Most likely, some of your code is located in the wrong callback, such that in the third scenario, something does not get initialized/attached/... This is really hard to tell without the code and the exception (if it is an exception).
I suggest you proceed as follows.
Complete your understanding of the lifecycle and be precise (creation, start, restart, resume, pause, stop, destroy)
Complete your code to override the other lifecycle callbacks as well with log messages (don't forget to call the super class' callback) and analyze pause, stop, rotation and combinations
Identify the point of the crash
Analyze the life cycle of the objects involvedd in the crash in conjunction with the Activity lifecycle
Or you can post some code and the exception here, of course.
Update 2 I have made a test on an AVD. I have observed the following:
When the AVD is in landscape orientation, and I press the power button, my app receives a configuration change to portrait orientation (presumably because the portrait-orientation-only lock screen takes over). Pressing power again and unlocking the AVD, the app receives a configuration change again back to landscape orientation.
While not solving your issue, it goes to show that code must be carefully placed in the respective callbacks, because in the case above, while being re-created, the Activity will still not be shown.
Drop me a comment if you update your question.
Hi you can check the documentation regarding Managing the Activity life cycles in android .
When orientation changes Activity is re-created.
Add android:configChanges="keyboardHidden|orientation" to your activity in the manifest.
On android, the GLSurfaceView documentation says this:
A GLSurfaceView must be notified when the activity is paused and
resumed. GLSurfaceView clients are required to call onPause() when the
activity pauses and onResume() when the activity resumes. These calls
allow GLSurfaceView to pause and resume the rendering thread, and also
allow GLSurfaceView to release and recreate the OpenGL display.
So I'm supposed to do something like this in my activity:
public void onPause() {
myGlSurfaceView.onPause();
}
public void onResume() {
myGlSurfaceView.onResume();
}
I'm observing in my code that if I don't call onPause() and onResume() then the context is not lost when I press the home button, so I can switch between applications and then go back to my game and everything is working. What I see is that if I close the game using the back button then the screen is black when I open it again, but I can change the back button behaviour to totally close the game and avoid this problem.
So my question is: when is the OpenGL context destroyed? If I don't call onPause() and onResume() can I assume that it will never be destroyed?
EDIT:
I'm targeting Android 2.2, so setPreserveEGLContextOnPause() is not an option to me.
The OpenGL might be lost only after Actvity::onPause() is called, and only in this case. See the setPreserveEGLContextOnPause documentation :
Whether the EGL context is actually preserved or not depends upon whether the Android device that the program is running on can support an arbitrary number of EGL contexts or not. Devices that can only support a limited number of EGL contexts must release the EGL context in order to allow multiple applications to share the GPU.
[...] the EGL context [can be] released when the GLSurfaceView is paused, and recreated when the GLSurfaceView is resumed.
EDIT : The situation described in the documentation is valid on all Android version. Not matter you have access to setPreserveEGLContextOnPause
In my opinion, this is one major drawback is Android OGLES implementation : you can't be certain.
The documentation itself is vague (EGL Context Lost note) :
There are situations where the EGL rendering context will be lost. This typically happens when device wakes up after going to sleep
I noticed the same behavior as you about the Home and Back button. Calls are not exactly the sames (but can't remember them precisely).
The only way to be sure that the OpenGL context is available is to create all OpenGL resources in onSurfaceCreated
Note about setPreserveEGLContextOnPause. Once again, this documentation comment demonstrates the "random" behavior of context destruction :
If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. [...]
setPreserveEGLContextOnPause is an option for you, you just have to implement the GlSurfaceView yourself.
See my answer here to a similar question:
Prevent onPause from trashing OpenGL Context