Get the FREE Ultimate OpenClaw Setup Guide →

libgdx-application-lifecycle

npx machina-cli add skill kyu-n/gdx-claude-skills/libgdx-application-lifecycle --openclaw
Files (1)
SKILL.md
9.1 KB

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();             // App created — OpenGL context now available
    void resize(int w, int h); // Window/viewport resized
    void render();             // Called every frame (update + draw combined)
    void pause();              // App losing focus / going to background
    void resume();             // App regaining focus / returning to foreground
    void dispose();            // App shutting down — release all resources
}

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.

// YES — extend ApplicationAdapter
public class MyGame extends ApplicationAdapter { ... }

// NO — don't implement ApplicationListener directly unless you need all 6 methods
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.

// WRONG — crashes: no GL context at field-init time
public class MyGame extends ApplicationAdapter {
    SpriteBatch batch = new SpriteBatch();   // CRASH
    Texture img = new Texture("pic.png");    // CRASH
}

// RIGHT — create() has a valid GL context
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() {
    // 1. Delta time for frame-independent movement
    float dt = Gdx.graphics.getDeltaTime();  // seconds since last frame

    // 2. Update
    x += 100 * dt;  // 100 pixels/second regardless of frame rate

    // 3. Draw — ScreenUtils.clear() is the modern shorthand (since 1.10.0)
    //    replacing the verbose Gdx.gl.glClearColor() + Gdx.gl.glClear() pattern
    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

EventDesktop (LWJGL3)AndroidiOS (RoboVM)
Window minimized / Home buttonpause() firespause() firespause() fires
Window loses focus (Alt+Tab)Nothing — render continues
Incoming call / screen offpause() firespause() fires
render() during pauseContinues runningStops entirelyStops entirely
GL context on pauseRetainedDestroyed — managed resources auto-reloadedRetained
dispose() on killCalledMay not be calledCalled

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;  // necessary — exit() is non-blocking
}

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().

TypeResource held
TextureGPU texture
SpriteBatchMesh + shader
BitmapFontTexture(s)
ShaderProgramGL shader program
FrameBufferGL framebuffer + texture
SoundNative audio buffer
MusicNative audio stream
PixmapNative pixel buffer
TextureAtlasTexture(s)
SkinTextures + fonts
StageSpriteBatch (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); // in create()

Gdx.app.log("Game", "info message");    // LOG_INFO level
Gdx.app.debug("Game", "debug message"); // LOG_DEBUG level
Gdx.app.error("Game", "error message"); // LOG_ERROR level

Log level constants (com.badlogic.gdx.Application):

ConstantValueShows
LOG_NONE0Nothing
LOG_ERROR1error() only
LOG_INFO2error() + log()
LOG_DEBUG3All 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() replaces the old glClearColor()+glClear() pattern (since 1.10.0)
        ScreenUtils.clear(Color.CORAL);
    }

    @Override
    public void dispose() {
        Gdx.app.log("MyGame", "dispose()");
    }
}

Common Mistakes

  1. Initializing GL objects in constructor or as field initializers — No OpenGL context exists yet. Move to create().
  2. Looking for an update() method — It doesn't exist. Put logic in render().
  3. Using Gdx.app.getDeltaTime() — Wrong location. It's Gdx.graphics.getDeltaTime().
  4. Forgetting to dispose native resources — Texture, SpriteBatch, BitmapFont, Sound, Music, etc. all leak if not disposed. Use AssetManager for non-trivial projects.
  5. Implementing ApplicationListener directly — Use ApplicationAdapter to avoid empty method stubs.
  6. Assuming desktop pause() fires on focus loss — On LWJGL3, only iconification (minimize) triggers it. Focus loss alone does nothing.
  7. Manually reloading textures on Android resume — libGDX reloads managed resources automatically. Only raw GL handles need manual recreation.
  8. 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.
  9. Treating Gdx.app.exit() as immediate — It schedules shutdown. Code after exit() still executes in the current frame.
  10. Missing configChanges in Android manifest — Without orientation|screenSize in android:configChanges, rotation destroys and recreates the Activity instead of calling resize().

Source

git clone https://github.com/kyu-n/gdx-claude-skills/blob/master/skills/libgdx-application-lifecycle/SKILL.mdView on GitHub

Overview

Describes the lifecycle methods of ApplicationListener and ApplicationAdapter, when to initialize resources, and how disposal and pause/resume behave. It helps you structure a game's main class and troubleshoot initialization or lifecycle issues.

How This Skill Works

The framework calls create() once the GL context is available, followed by resize() and a render() loop. Initialize GPU/asset objects inside create(), perform updates and drawing inside render(), and dispose of resources in dispose(). Use Gdx.graphics.getDeltaTime() for frame timing, and beware platform-specific pause/resume behavior.

When to Use It

  • Structuring a game's main class with ApplicationAdapter to avoid boilerplate
  • Debugging missing disposal or startup crashes
  • Deciding where to place initialization code to ensure GL context exists
  • Retrieving delta time for frame-independent updates with Gdx.graphics.getDeltaTime()
  • Identifying which objects need dispose() and when to call it

Quick Start

  1. Step 1: Extend ApplicationAdapter (or implement ApplicationListener) and override only needed methods
  2. Step 2: Initialize GL-dependent resources inside create()
  3. Step 3: Implement render() to update with delta time, draw, and dispose() resources when closing

Best Practices

  • Initialize GPU/audio/file resources inside create() (GL context must exist)
  • Use ApplicationAdapter to override only the needed methods
  • Avoid creating GL-dependent objects as field initializers or in the constructor
  • Dispose resources in dispose() to release all resources cleanly
  • On Android, save critical state in pause() since dispose() may not be called

Example Use Cases

  • Move Texture and SpriteBatch initialization from field initializers into create()
  • Compute delta time in render() with Gdx.graphics.getDeltaTime() and apply it to movement
  • Use ScreenUtils.clear() at the start of render() instead of manual GL clear calls
  • Override only the necessary methods in ApplicationAdapter to reduce boilerplate
  • Handle pause() to preserve state on Android where dispose() might not run

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers