question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

AssetManager: NullPointerException vs isLoaded => true ~ unload() called twice

See original GitHub issue

Please 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:

  1. assetManager.load(any TextureAtlas);
  2. ask for isLoaded(textureAtlasName) => true;
  3. so unload(TextureAtlasName); => OK
  4. ask again for isLoaded(textureAtlasName) => false; <<< OK
  5. so unload(TextureAtlasName); => GdxRuntimeException("Asset not loaded: " + fileName); <<< OK

v1.9.11-SNAPSHOT ~ 2020-03-01

  1. assetManager.load(any TextureAtlas);
  2. ask for isLoaded(textureAtlasName) => true;
  3. so unload(TextureAtlasName); => OK
  4. ask again for isLoaded(textureAtlasName) => true; <<< BUG
  5. 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:closed
  • Created 4 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
tommyettingercommented, Mar 4, 2020

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.

0reactions
j3ndacommented, Mar 6, 2020

confirm #5932 fixes this problem

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found