Managing Asynchronous Resource Loading with libGDX AssetManager
Learn how to use the libGDX AssetManager to implement asynchronous loading, prevent ANR errors on mobile, and manage native memory through reference counting.
03 Jun 2026, 01:07 UTC

The Problem: Frame Stutters and ANR Errors
Loading high-resolution textures or audio files directly on the main thread causes the application to freeze. In mobile environments, this often triggers an "Application Not Responding" (ANR) error because the rendering loop is blocked while the CPU waits for disk I/O. To maintain a smooth user experience—such as an animated loading screen—you must offload resource loading to a background process.
The AssetManager in libGDX solves this by implementing a queue-based asynchronous loader. It handles the lifecycle of assets through reference counting, ensuring that a texture used by three different game screens is only loaded into memory once and only disposed of when all three screens release it.
Prerequisites
- A libGDX project configured for at least version 1.12.0.
- Assets (images, sounds, shaders) located in the
assetsfolder. - A basic
Screenimplementation where the loading logic will reside.
Implementing the Loading Pipeline
The AssetManager does not load assets instantly. Instead, it queues them and processes them incrementally during the render() loop.
1. Initialize and Queue Assets
Create a single instance of AssetManager. For most games, this should be managed by a global Game class to persist across screens.
// In your Game or LoadingScreen class
AssetManager manager = new AssetManager();
// Queue assets for loading (non-blocking)
manager.load("images/background.png", Texture.class);
manager.load("sounds/explosion.wav", Sound.class);
manager.load("shaders/outline.frag", ShaderProgram.class);
2. The Update Loop
To actually move assets from the disk to memory, you must call update() inside your render method. This method processes a small chunk of the queue each frame, allowing your loading animation to keep running.
@Override
public void render(float delta) {
// update() returns true if there are still assets loading
if (manager.update()) {
float progress = manager.getProgress();
drawLoadingBar(progress);
} else {
// All assets are loaded; transition to the next screen
goToGameScreen();
}
}
3. Retrieving Loaded Assets
Once update() returns false, you can safely retrieve the assets using the get() method. Risk: Calling get() before the asset is fully loaded will result in a NullPointerException or a crash.
Texture bg = manager.get("images/background.png", Texture.class);
Resource Lifecycle and Memory Management
To prevent native memory leaks, you must manage how assets are removed from the GPU and RAM.
| Method | Behavior | Use Case |
|---|---|---|
unload("path") |
Decrements reference count; removes if count reaches 0. | Leaving a specific level/map. |
unload() |
Clears all managed assets from memory. | Returning to the main menu from a game session. |
dispose() |
Destroys the manager and all associated native resources. | Closing the application. |
Verification and Diagnostics
To verify the implementation is working asynchronously, follow these checks:
- Visual Check: Load a large asset (e.g., a 4K texture). If the loading bar animation stutters or freezes,
update()is likely being called in a blocking manner or the asset is too large for a single frame's budget. - Logic Check: Print
manager.getProgress()to the console. It should increment from 0.0 to 1.0 over several frames. - Memory Check: Use a profiler to monitor native memory. Load and unload a large set of textures repeatedly; if memory usage climbs without dropping, ensure
unload()is being called correctly.
Rollback and Recovery
If the AssetManager introduces unexpected overhead for a small number of assets, you can revert to direct loading:
Replace manager.get("path", Texture.class) with new Texture(Gdx.files.internal("path")). Note that if you do this, you are now responsible for calling .dispose() on every individual asset manually, as you lose the centralized reference counting provided by the manager.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.