AssetManager: NullPointerException vs isLoaded => true ~ unload() called twice
See original GitHub issuePlease ensure you have given all the following requested information in your report.
Issue details
when AssetManager try to unload same resource twice it will fail on NullPointerException, here:
// get the asset and its type
Class type = assetTypes.get(fileName);
if (type == null) throw new GdxRuntimeException("Asset not loaded: " + fileName);
RefCountedContainer assetRef = assets.get(type).get(fileName); // <<< NullPointerException
// if it is reference counted, decrement ref count and check if we can really get rid of it.
assetRef.decRefCount();
- on 1st attempt, when resource is loaded: assetManager.isLoaded(resourceName) will return true; and then assetManager.unload(resourceName); is OK!
but
- on 2nd attemt, resource is already unloaded and: somehow assetManager.isLoaded(resourceName) returns TRUE instead of false! so mine code will call again: assetManager.unload(resourceName) ~ because it says its loaded, so: next pass is failing on NullPointerException: “RefCountedContainer assetRef = assets.get(type).get(fileName);” where first get(type) is positive - but second one get(fileName) will return null!
Reproduction steps/code
v1.9.10:
- assetManager.load(any TextureAtlas);
- ask for isLoaded(textureAtlasName) => true;
- so unload(TextureAtlasName); => OK
- ask again for isLoaded(textureAtlasName) => false; <<< OK
- so unload(TextureAtlasName); => GdxRuntimeException("Asset not loaded: " + fileName); <<< OK
v1.9.11-SNAPSHOT ~ 2020-03-01
- assetManager.load(any TextureAtlas);
- ask for isLoaded(textureAtlasName) => true;
- so unload(TextureAtlasName); => OK
- ask again for isLoaded(textureAtlasName) => true; <<< BUG
- so unload(TextureAtlasName); => NullPointerException <<< BUG
Version of LibGDX and/or relevant dependencies
1.9.11-SNAPSHOT (v.1.9.10 is ok)
Stacktrace
| 2020/03/01 21:09:46.103 | ERROR | Current screen: com.sjet.gula.graphics.screens.singleplayer.SingleplayerGameScreen [ WorldOfClonesGame.java:343 ]
| 2020/03/01 21:09:46.104 | ERROR | Application detected fatal exception! [ WorldOfClonesGame.java:344 ]
Exception:java.lang.NullPointerException
com.badlogic.gdx.assets.AssetManager.unload(AssetManager.java:237)
com.sjet.gula.resources.dynamic.DynamicResourcesManager.unloadGroupTextures(DynamicResourcesManager.java:579)
com.sjet.gula.resources.dynamic.DynamicResourcesManager.unloadGroup(DynamicResourcesManager.java:553)
com.sjet.gula.resources.ResourcesManager.unloadGroup(ResourcesManager.java:361)
com.sjet.gula.graphics.screens.ScreenBase.unloadScreenResources(ScreenBase.java:252)
com.sjet.gula.graphics.screens.ScreenBase.hide(ScreenBase.java:168)
com.sjet.gula.graphics.screens.GameScreenBase.hide(GameScreenBase.java:686)
com.badlogic.gdx.Game.setScreen(Game.java:58)
com.sjet.gula.WorldOfClonesGame.setScreen(WorldOfClonesGame.java:389)
com.sjet.gula.graphics.screens.GameScreenBase.redirectToNextScreen(GameScreenBase.java:458)
com.sjet.gula.graphics.screens.GameScreenBase.onUserAnswered(GameScreenBase.java:640)
com.sjet.gula.graphics.ui.items.dialogs.YesOrNoDialog.fireUserAnswered(YesOrNoDialog.java:59)
com.sjet.gula.graphics.ui.items.dialogs.YesOrNoDialog$1.changed(YesOrNoDialog.java:156)
com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.handle(ChangeListener.java:28)
com.badlogic.gdx.scenes.scene2d.Actor.notify(Actor.java:189)
com.badlogic.gdx.scenes.scene2d.Actor.fire(Actor.java:153)
com.badlogic.gdx.scenes.scene2d.ui.Button.setChecked(Button.java:132)
com.badlogic.gdx.scenes.scene2d.ui.Button$1.clicked(Button.java:95)
com.badlogic.gdx.scenes.scene2d.utils.ClickListener.touchUp(ClickListener.java:89)
com.badlogic.gdx.scenes.scene2d.InputListener.handle(InputListener.java:60)
com.badlogic.gdx.scenes.scene2d.Stage.touchUp(Stage.java:353)
com.badlogic.gdx.InputMultiplexer.touchUp(InputMultiplexer.java:124)
com.badlogic.gdx.backends.lwjgl.LwjglInput.processEvents(LwjglInput.java:360)
com.badlogic.gdx.backends.lwjgl.GulaDesktopLWJGLApp.mainLoop(GulaDesktopLWJGLApp.java:121)
com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:128)
[__RETURN_CODE__] 2
Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: Asset not loaded: graphics/units/bellis/attack.png
at com.badlogic.gdx.assets.AssetManager.unload(AssetManager.java:232)
at com.badlogic.gdx.assets.AssetManager.clear(AssetManager.java:725)
at com.badlogic.gdx.assets.AssetManager.dispose(AssetManager.java:693)
at com.sjet.gula.resources.ResourcesManager.dispose(ResourcesManager.java:179)
at com.sjet.gula.WorldOfClonesGame.dispose(WorldOfClonesGame.java:411)
at com.badlogic.gdx.backends.lwjgl.GulaDesktopLWJGLApp.mainLoop(GulaDesktopLWJGLApp.java:162)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:128)
Please select the affected platforms
- Android
- iOS (robovm)
- iOS (MOE)
- HTML/GWT
- Windows
- Linux
- MacOS
EXAMPLE ~ which fails (on 1.9.11-SNAPSHOT) and 1.9.10 is ok
(focus on part around: “if (Version.isLower(1, 9, 11))” else …
private void unloadGroupAnimations(Array<String> resources)
{
if (resources == null)
{
return;
}
for (String resource : resources)
{
AnimationConfiguration animationConfig = configLoader.getAnimationConfiguration(resource);
boolean isFreezed = freezeToUnloadResources.contains(animationConfig.getTextureAtlas());
boolean isLoaded = assetManager.isLoaded(animationConfig.getTextureAtlas());
logger.debug(
"unload(animation)["
+ (isFreezed ? "F" : "-")
+ (isLoaded ? "L" : "-")
+ "]:"
+ resource
);
isFreezed = false;
if (!isFreezed && isLoaded)
{
if (animationConfig.getTextureAtlas() != null)
{
if (Version.isLower(1, 9, 11))
{
assetManager.unload(animationConfig.getTextureAtlas());
}
else
{
// because v1.9.10 is ok (~but contains AnimatedTile bug) and 1.9.11-SNAPSHOT has this
// weird behavior...
try
{
assetManager.unload(animationConfig.getTextureAtlas());
}
catch(Exception e)
{
// im just ignoring it...
// it's because unload is async! and there isnt any callback, when its finished!
// eg: unload:
// - graphics/units/gribs/run_left.json
// - graphics/units/gribs/run_right.json
//
// its using same TextureAtlas ~ "graphics/units/gribs/run.atlas" and
// 1st unload() will create async task to unload it. and do it!
// 2nd unload() will report, that resource is loaded(true) ~ so this will fail!
//
// its happening inside: AssetManager.unload(), at:
// RefCountedContainer assetRef = assets.get(type).get(fileName);
System.out.println(e);
}
}
}
onUnloadListeners.onUnloadResourceAnimation(animationConfig);
}
}
}
| 2020/03/01 21:30:25.568 | DEBUG | unload(animation)[-L]:graphics/units/gribs/die_left.json [ Logger.java:142 ]
| 2020/03/01 21:30:25.568 | DEBUG | unload(animation)[-L]:graphics/units/gribs/die_right.json [ Logger.java:142 ]
| 2020/03/01 21:30:25.569 | DEBUG | unload(animation)[-L]:graphics/units/gribs/run_left.json [ Logger.java:142 ]
java.lang.NullPointerException
| 2020/03/01 21:32:22.823 | DEBUG | unload(animation)[-L]:graphics/units/gribs/run_right.json [ Logger.java:142 ]
java.lang.NullPointerException
| 2020/03/01 21:32:33.615 | DEBUG | unload(animation)[-L]:graphics/units/kingofanimals/attack_left.json [ Logger.java:142 ]
| 2020/03/01 21:32:33.616 | DEBUG | unload(animation)[-L]:graphics/units/kingofanimals/attack_right.json [ Logger.java:142 ]
| 2020/03/01 21:32:33.618 | DEBUG | unload(animation)[-L]:graphics/units/kingofanimals/die_left.json [ Logger.java:142 ]
| 2020/03/01 21:32:33.619 | DEBUG | unload(animation)[-L]:graphics/units/kingofanimals/die_right.json [ Logger.java:142 ]
| 2020/03/01 21:32:33.621 | DEBUG | unload(animation)[-L]:graphics/units/kingofanimals/run_left.json
Issue Analytics
- State:
- Created 4 years ago
- Comments:10 (5 by maintainers)
Top Results From Across the Web
AssetManager null pointer exception - java - Stack Overflow
I've called getAssets() by itself, from the context and from the resources but still I'm getting null. Can anyone help me out here?...
Read more >Diff - platform/frameworks/base - Google Git
closeQuietly(assets); + // TODO(b/72056911): Implement and call close() on ApkAssets. } } ... Add multiple sets of assets to the asset manager at...
Read more >SoundPool | Android Developers
Unload a sound from a sound ID. Unloads the sound specified by the soundID. This is the value returned by the load() function....
Read more >Broadcast starting app immediately after apk ... - Issue Tracker
3) The app code is loaded from the old APK, but eventually loading resources will fail. * Happens with both alarm broadcasts and...
Read more >Java. This vulnerability - CVE - Search Results
By default it is allowed to call any static method of any Java class in the ... to exploit and spoof the local...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I submitted a PR that might fix this, #5932 , above. The PR is meant to addresses a case where put() and remove() are called in a mixed order; that is probably what is happening here and would explain unexpected null results. If #5932 doesn’t fix this issue, it’s going to take more in-depth stepping through AssetManager to find where things go wrong.
confirm #5932 fixes this problem