Open topic with navigation
Technically, game data other than OpenGL ES resources can be loaded at any time from
onCreate beyond. Actually, application copies of OpenGL ES resources could be loaded in
onCreate as well, but they cannot be loaded to OpenGL ES until it is initialized. So it may or may not make sense to load the rendering resources so early.
|Note: Depending on your manifest settings, the application may be brought up and shut down several times during initial launch (see the later section Surprising Application Lifecycle Events and How to Handle Them). If you choose not to use the settings to avoid this, you may wish to delay the loading of any data until you know that the game is fully launched. Doing so can avoid doubling or tripling the load time if the system launches and re-launches the app several times.|
Technically, the following steps can be done at any time between
onDestroy, as they do not require any specific resources from the app:
EGLContextfor OpenGL ES.
However, the context cannot be bound until there is an
EGLSurface that can be bound at the same time. The
EGLSurface in turn cannot be created until there is an Android surface available to attach the
EGLSurface. Such a surface is provided by the
surfaceCreated callback. As a result, in order to fully set up OpenGL ES, we must be between
Indeed, since an
EGLContext must be bound in order to load OpenGL ES resources (which in turn requires an
EGLSurface), this means that we cannot load our 3D resources for the application until we receive
surfaceCreated. We should note further that it is best to wait for
surfaceChanged to indicate that the surface has positive width and height as well; some cases can generate temporary 0x0 surfaces.
When we receive a
surfaceDestroyed callback, we must immediately unbind the
display, NULL, NULL) and must stop rendering. However, we do not need to destroy the
EGLContext at this time. It can be kept resident even when it is not bound. When a new surface is available we can rebind the existing context and we will not have lost any of our rendering resources. This can avoid an expensive asset reloading process in some lifecycle cases like suspend/resume.
We should still consider deleting the
EGLContext (and thus freeing resources) in
onStop, even if the context is not bound. If the context is bound, it may be best to release the GLES resources manually using GLES calls. However, if there is no surface and thus no way to bind the context at the point at which we want to delete the GLES resources, the best option is to destroy the
EGLContext manually using
One common way to handle initialization by making a function that returns "true" if we are completely ready to load resources, allocating/initializing/failing as needed/possible. This can handle partial initializations; for example, it can handle the case where we had a surface, lost it due to
surfaceDestroyed and now have a new Android surface (and can thus create and bind a new
EGLSurface). The pseudo-code might be as follows:
bool EGLReadytoRender ()
// If we have a bound context and surface, then EGL is ready
if ((Is EGL Context Bound?) == false)
// If we have not bound the context and surface,
// do we even _have_ a surface?
if ((Is EGL Surface Created?) == false)
// No EGL surface; is EGL is set up at all?
if ((Are EGL Context/Config Initialized?) == false)
// Init EGL, the Config and Context here…
// Mark that we have a new context – this will
// be important later, as a new context bound
// means we can/must load content into it
if ((Is there a valid Android surface?) == false)
// Create the EGLSurface surface here…
} // We have a surface and context, so bind them // eglMakeCurrent with the surface and context here… if (failed) return false; } // One way or another (free or requiring some amount of setup) // We have a fully initialized set of EGL objects… Off we go return true; }
In general, an app should only render when it is fully visible and interact-able. In Android lifecycle terms, this tends to mean:
surfaceDestroyedand have a bound surface and context).
There are cases where we might render outside of being the focused window and in rare cases when
onPause’d, but those tend to be:
In practice, while there are several callbacks that can indicate that a screen resize has occurred (e.g.
onConfigurationChanged), we have found in practice that these should be used only to trigger a forced-redraw of the application. Resizing of the window and surface can be delayed in some cases, especially with
native_app_glue. As a safety net, we recommend that at the start of each rendered frame, the application check the size of the window or surface and update their aspect ratio, size, and viewport information. In this way, any problems from a rogue size change that is not signaled with a callback or is out of sequence can be minimized.
A very important case to consider is the previously-discussed suspend/resume.
Note the sequence: when the device is suspended, the app receives
onPause and focus loss, the signal to stop rendering and playing music. When the device is resumed,
onResume is given. But the app is NOT visible. It must not render and must not play sounds yet. The lock screen covers it. Users often power up to the lock screen in quiet areas to look at their clock and messages. If the app began blasting music, it could be a major usability issue. It is not until the user unlocks the device and the lock screen goes away that the focus regained callback is given. Only when both focus and
onResume have both been given should rendering and sound begin again. However, the application should not resume to active gameplay on unlock – see the next section for more discussion of this.
|Note: The application should not resume to active gameplay on unlock. See the next section for more discussion of this.|
In general, gameplay should be paused to some auto-pause screen whenever the user would have lost the ability to control the game for any period of time. Commonly, this is the case when you are regaining window focus from any form of loss. This screen should be kept active even once focus is regained. Do not restart active gameplay just because focus is regained.
If you handle window focus regain by simply enabling the gameplay again, the user may be caught off guard and left to catch up. When receiving an o
nWindowFocusChanged(TRUE) after a
onWindowFocusChanged(FALSE), it is best to show an autopause screen to allow the user to resume gameplay on their own time.
In general, most applications will want to handle the BACK button explicitly and “eat” the event in order to avoid having Android kill their activity with no further user interaction. Generally, for games, this involves handling BACK as a “back step” through their UI menus, and then when the start screen is reached, having the BACK button trigger a “do you really want to quit” dialog. In these cases, the application must signal to Android that it has completely handled the BACK button, and Android should do no further processing.
Depending on the application’s architecture, this will be one of the following:
onKeyUpby not calling the superclass implementation and by returning “true."
AInputQueue_finishEventa handled parameter of “1.”
native_app_glue, return “1” from the
The application can always request quit on its own schedule via Java finish or
Native executables on most platforms signal exit by returning from their main function. However, since native code in Android is merely one piece of the application, this will not work for native Android applications.
Technically, a native Android application should not return from its main function until the framework (
native_app_glue) has requested it. If an application wishes to proactively exit, they must invoke the Java finish method. This can be done:
NativeActivity, via a native call to
Exiting the native entry-point (e.g.,
android_main) does not necessarily cause the top-level Java application to exit. Applications should keep their main loops running until requested to exit the thread by
Game progress will be lost if it is not saved prior to the application being killed. Since an application is killable in all Android variants after returning from
onStop, and is killable on pre-Honeycomb after returning from
onPause, these are the obvious callbacks to use to save game state to persistent storage.
In addition, it may make sense to save a flag in
onStop that indicates that the current save game is an auto-save, and not a user-initiated save. Then, if the user exits out of gameplay or entirely out of the game with explicit decisions, clear this "autosaved" flag.
When your application is launched, you can check if this is a "fresh" launch initiated by a user or simply your app being relaunched because it was killed silently by Android. In the latter case, you may want to resume directly to auto-pause in the saved game, since that is presumably where the user last sat before the game was interrupted. This can make the game appear seamless with respect to getting auto-killed.
NVIDIA® GameWorks™ Documentation Rev. 1.0.211026 ©2014-2021. NVIDIA Corporation and affiliates. All Rights Reserved.