| name | libgdx-application-lifecycle |
| description | Use when writing a libGDX ApplicationListener or ApplicationAdapter, structuring a game's main class, or debugging lifecycle issues like missing disposal, initialization crashes, or incorrect pause/resume behavior. Use when asking where to put initialization code, how to get delta time, or which types need dispose(). |
libGDX Application Lifecycle
Reference for ApplicationListener, ApplicationAdapter, lifecycle method order, frame-independent updates, resource disposal, and platform-specific pause/resume behavior.
ApplicationListener Interface
public interface ApplicationListener {
void create();
void resize(int w, int h);
void render();
void pause();
void resume();
void dispose();
}
There is no update() method. All game logic and drawing happen in render().
ApplicationAdapter (Use This)
ApplicationAdapter implements ApplicationListener with empty defaults. Extend it and override only what you need — avoids boilerplate empty methods.
public class MyGame extends ApplicationAdapter { ... }
public class MyGame implements ApplicationListener { ... }
Lifecycle Order
create() → resize(w,h) → [render() loop] → pause() → dispose()
↕
pause() ↔ resume()
Startup: create() → resize(w, h) → render() ...
Exit: pause() → dispose()
On Android, dispose() may not be called if the OS kills the process while backgrounded. Save critical state in pause(), not dispose().
Initialization: create(), Not the Constructor
All GPU/audio/file initialization must happen inside create(). The OpenGL context and libGDX subsystems (Gdx.graphics, Gdx.audio, Gdx.files, etc.) do not exist until create() is called. Constructing a Texture, SpriteBatch, or any GL-dependent object as a field initializer or in the constructor will crash.
public class MyGame extends ApplicationAdapter {
SpriteBatch batch = new SpriteBatch();
Texture img = new Texture("pic.png");
}
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
@Override
public void create() {
batch = new SpriteBatch();
img = new Texture("pic.png");
}
}
render() = Update + Draw
There is no separate update() callback. Combine logic and rendering in render():
@Override
public void render() {
float dt = Gdx.graphics.getDeltaTime();
x += 100 * dt;
ScreenUtils.clear(0, 0, 0, 1);
batch.begin();
batch.draw(img, x, 0);
batch.end();
}
getDeltaTime() is on Gdx.graphics, not Gdx.app.
pause() / resume() — Platform Differences
| Event | Desktop (LWJGL3) | Android | iOS (RoboVM) |
|---|
| Window minimized / Home button | pause() fires | pause() fires | pause() fires |
| Window loses focus (Alt+Tab) | Nothing — render continues | — | — |
| Incoming call / screen off | — | pause() fires | pause() fires |
| render() during pause | Continues running | Stops entirely | Stops entirely |
| GL context on pause | Retained | Destroyed — managed resources auto-reloaded | Retained |
| dispose() on kill | Called | May not be called | Called |
Desktop: pause() only fires on iconification, not on focus loss. render() keeps running even when iconified — if you have network timers or other continuous work in render(), it won't stop on desktop. Check the pause state explicitly if needed.
Android: The render loop stops entirely during pause. Any work in render() (heartbeats, timers, network pings) ceases. Use a background thread or Android service for work that must continue. The GL context is destroyed during pause; libGDX automatically reloads managed resources on resume. Only raw, unmanaged GL handles need manual recreation.
iOS (RoboVM): Behaves like Android for pause triggers (home button, incoming call, app switcher), but the GL context is retained like desktop. render() stops during pause.
resize() on Android — configChanges Gotcha
On Android, device rotation triggers resize() only if android:configChanges includes orientation|screenSize in the manifest. Without it, Android destroys and recreates the Activity — a full dispose() → create() cycle, not a resize(). The Android backend skill covers manifest setup.
Gdx.app.exit()
Gdx.app.exit() schedules a graceful shutdown — it is NOT immediate. Code after exit() still executes in the current frame. The shutdown triggers pause() → dispose(). On Android, it calls finish() on the Activity.
if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
Gdx.app.exit();
return;
}
Disposal: Types That Need dispose()
These types allocate native memory or GPU resources outside the JVM heap. The garbage collector cannot free them — you must call dispose() explicitly, typically in your ApplicationAdapter.dispose().
| Type | Resource held |
|---|
Texture | GPU texture |
SpriteBatch | Mesh + shader |
BitmapFont | Texture(s) |
ShaderProgram | GL shader program |
FrameBuffer | GL framebuffer + texture |
Sound | Native audio buffer |
Music | Native audio stream |
Pixmap | Native pixel buffer |
TextureAtlas | Texture(s) |
Skin | Textures + fonts |
Stage | SpriteBatch (if owns it) |
Rule of thumb: if you created it, dispose it. Dispose in reverse order of creation when possible. In non-trivial projects, AssetManager handles disposal via unload() and clear() — prefer it over manual tracking.
Lifecycle Debugging with Gdx.app.log()
Gdx.app.setLogLevel(Application.LOG_DEBUG);
Gdx.app.log("Game", "info message");
Gdx.app.debug("Game", "debug message");
Gdx.app.error("Game", "error message");
Log level constants (com.badlogic.gdx.Application):
| Constant | Value | Shows |
|---|
LOG_NONE | 0 | Nothing |
LOG_ERROR | 1 | error() only |
LOG_INFO | 2 | error() + log() |
LOG_DEBUG | 3 | All messages |
Default level is LOG_INFO. Output goes to stdout (desktop), LogCat (Android), or NSLog (iOS).
Minimal Working Example
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.ScreenUtils;
public class MyGame extends ApplicationAdapter {
@Override
public void create() {
Gdx.app.log("MyGame", "create()");
}
@Override
public void render() {
ScreenUtils.clear(Color.CORAL);
}
@Override
public void dispose() {
Gdx.app.log("MyGame", "dispose()");
}
}
Common Mistakes
- Initializing GL objects in constructor or as field initializers — No OpenGL context exists yet. Move to
create().
- Looking for an
update() method — It doesn't exist. Put logic in render().
- Using
Gdx.app.getDeltaTime() — Wrong location. It's Gdx.graphics.getDeltaTime().
- Forgetting to dispose native resources — Texture, SpriteBatch, BitmapFont, Sound, Music, etc. all leak if not disposed. Use
AssetManager for non-trivial projects.
- Implementing
ApplicationListener directly — Use ApplicationAdapter to avoid empty method stubs.
- Assuming desktop pause() fires on focus loss — On LWJGL3, only iconification (minimize) triggers it. Focus loss alone does nothing.
- Manually reloading textures on Android resume — libGDX reloads managed resources automatically. Only raw GL handles need manual recreation.
- Assuming render() keeps running on Android during pause — The render loop stops entirely. Use a background thread or Android service for work that must continue.
- Treating Gdx.app.exit() as immediate — It schedules shutdown. Code after
exit() still executes in the current frame.
- Missing configChanges in Android manifest — Without
orientation|screenSize in android:configChanges, rotation destroys and recreates the Activity instead of calling resize().