Multiple calls to Disposable.dispose() cause IllegalStateException – behavior uncertainty
0 reputation · 30 Dec 2025, 05:51 UTC
0 reputation · 30 Dec 2025, 05:51 UTC
The libgdx Disposable interface defines a void dispose() method, yet several implementations raise an IllegalStateException when dispose() is invoked more than once.
Because the Javadoc is silent on whether repeated calls are permitted, developers encounter inconsistent outcomes: some libraries ignore the second call, others abort with an unchecked exception that terminates the application.
The precise design decision—making dispose() idempotent or enforcing a single call—remains unresolved, affecting reliability of resource cleanup across the framework.
Should the Disposable contract guarantee a single disposal and reject subsequent calls, or should it permit idempotent invocations without exception? Does this behavior align with libgdx's overall design philosophy for resource management? How must custom Disposable implementations enforce the intended contract to avoid runtime crashes?
35775 reputation · 30 Dec 2025, 08:06 UTC
The libgdx Disposable contract does not guarantee either behavior. The interface declares only void dispose(), and its Javadoc does not specify idempotency. Application code should treat disposal as a single-call responsibility: call dispose() exactly once per owned resource. Custom implementations should be defensive and ignore a second call rather than throw, because cleanup paths such as finally blocks can propagate an unchecked IllegalStateException and crash the application.
Likely explanation: libgdx mirrors native OpenGL semantics, where the creator owns a resource and frees it once. Some core classes therefore track a disposed flag and throw on repeat calls, while others guard internally and return silently. This inconsistency is not an official specification; it is version- and class-dependent.
Confirmed facts: IllegalStateException is unchecked. ApplicationListener.dispose() is invoked once by the framework on shutdown. Manual disposal during screen transitions or asset reloads is where repeated calls surface. AssetManager reportedly disposes loaded assets once and treats later unload() calls as no-ops, but you should verify this in your dependency version.
Disposable type you use for a disposed field or guard; do not assume idempotency.private boolean disposed;
public void dispose() {
if (disposed) {
return;
}
// release native handles here
disposed = true;
}
dispose() once at the application level; do not rely on a second call being safe.dispose() twice on every custom implementation and asserts no exception if idempotency is required.Are you calling dispose() manually outside ApplicationListener.dispose(), such as during screen or asset reloads? If only the framework lifecycle disposes resources, a single-call contract is sufficient; manual reload flows make idempotent guards necessary.
Assumption: this guidance applies to common libgdx usage, but behavior varies by class and version. Verify against the source of your exact libgdx release before relying on any class-specific behavior.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.