7
\$\begingroup\$

I'm working on a game for Android using Android Studio with LibGDX in Java. I recently found a weird problem when closing and reopening the game. When I first open the game, it works fine. If I click the QUIT button and close the app, the asset manager disposes. When I reopen the game, some of the atlas regions are shown as black rectangles in the same size of the region. I noticed that it happens only when I call assets manager's dispose method. If I remove it, the problem doesn't appear. It's funny, but the game seems to work fine when not calling that method but I read that it is recommended to dispose it. This is my AssetsManager class:

public final class AssetManagerWrapper extends AssetManager {
    ArrayMap<String, FileHandle> configs = new ArrayMap<String, FileHandle>();
    public Texture lightTexture;
    public Texture alarmTexture;
    public TextureAtlas particlesAtlas;

    public final void loadGFX() {
        FileHandle dirHandle;
        dirHandle = Gdx.files.internal(Rules.System.GFX.GFX_FOLDER_NAME);
        FileHandle subDirHandle;

        for (FileHandle dir : dirHandle.list()) {
            subDirHandle = Gdx.files.internal(dir.path());
            if (dir.name().equalsIgnoreCase(AssetsPaths.Gfx.Sheets.SHEETS_FOLDER_NAME)) {
                for (FileHandle file : subDirHandle.list()) {
                    if (file.extension().equalsIgnoreCase(Rules.System.GFX.SHEETS_DATA_EXTENSION)) {
                        load(file.path(), TextureAtlas.class);
                    }
                }
            } else {
                for (FileHandle file : subDirHandle.list()) {
                    load(file.path(), Texture.class);
                }
            }
        }
    }

    public final void loadSounds() {
        FileHandle dirHandle;
        dirHandle = Gdx.files.internal(Rules.System.SFX.SFX_FOLDER_NAME);
        FileHandle subDirHandle;

        for (FileHandle dir : dirHandle.list()) {
            subDirHandle = Gdx.files.internal(dir.path());
            if (dir.name().equalsIgnoreCase(Rules.System.SFX.SOUNDS_FOLDER_NAME)) {
                for (FileHandle file : subDirHandle.list()) {
                    if (file.extension().equalsIgnoreCase(Rules.System.SFX.SOUNDS_DATA_EXTENSION)) {
                        load(file.path(), Sound.class);
                    }
                }
            }
        }

    }

    public void loadParticlesConfigs() {
        FileHandle dirHandle;
        dirHandle = Gdx.files.internal(AssetsPaths.Configs.CONFIGS_FOLDER_NAME + '/' + AssetsPaths.Configs.PARTICLE_CONFIGS_FOLDER_NAME);

        for (FileHandle file : dirHandle.list()) {
            if (file.extension().equalsIgnoreCase(Rules.System.GFX.PARTICLE_CONFIGS_DATA_EXTENSION)) {
                configs.put(file.nameWithoutExtension(), file);
            }
        }
    }


    public final void loadMusic() {
        FileHandle dirHandle;
        dirHandle = Gdx.files.internal(Rules.System.SFX.SFX_FOLDER_NAME);
        FileHandle subDirHandle;

        for (FileHandle dir : dirHandle.list()) {
            subDirHandle = Gdx.files.internal(dir.path());
            if (dir.name().equalsIgnoreCase(Rules.System.SFX.MUSIC_FOLDER_NAME)) {
                for (FileHandle file : subDirHandle.list()) {
                    if (file.extension().equalsIgnoreCase(Rules.System.SFX.MUSIC_DATA_EXTENSION)) {
                        load(file.path(), Music.class);
                    }
                }
            }
        }

    }

    public void loadData() {
        loadGFX();
        loadSounds();
        loadMusic();
        loadParticlesConfigs();
        finishLoading();
    }

    @Override
    public void finishLoading() {
        super.finishLoading();
        lightTexture = get(AssetsPaths.Gfx.Other.LIGHT);
        alarmTexture = get(AssetsPaths.Gfx.Menu.ALARM);
        particlesAtlas = get(AssetsPaths.Gfx.Sheets.Misc.PARTICLES_DATA_FILE);
    }

    public ArrayMap<String, FileHandle> getConfigs() {
        return configs;
    }
}

The life-cycle (Main class which implements Application Listener):

   @Override
        public void create() {
            assetsManager = new AssetManagerWrapper();
            assetsManager.loadData();
            soundPlayer = new SoundPlayer(this);
            Gdx.input.setCatchBackKey(true);
            goToMenu();
        }

     @Override
    public void resume() {
        assetsManager.finishLoading();
    }
 @Override
    public void dispose() {
        assetsManager.dispose(); //For some reason this creates the black rectangles bug.
        soundPlayer.dispose();
        if (warScreen != null) {
            warScreen.dispose();
        }
        menuScreen.dispose();
    }

Does anybody have any idea what could cause it? Is that ok that I just remove the call to that method? Thanks in advance.

\$\endgroup\$
  • \$\begingroup\$ What's the life-cycle of your AssetManagerWrapper class? Who calls loadData and when, and who calls dispose and when? \$\endgroup\$ – bornander May 9 '16 at 20:28
  • \$\begingroup\$ assetsManager.loadData(); is called in onCreate() of the application listener class. assetsManager.dispose(); is called in dispose() of the same class. \$\endgroup\$ – Gad Wissberg May 13 '16 at 8:25
0
\$\begingroup\$

According to this section in the AssetManager page. When Android pauses, then the managed libgdx elements, like Textures, get cleaned up an have to be reloaded when the app resumes. To do this, you call Texture.setAssetManager(manager); and then call update on the asset manager like you did when you first loaded.

Secondly, Android apps have no guarantee that they will really stop executing when you quit them. Android is the one that chooses when it cleans up an app that is in the background. Perhaps even though you selected quit, your app was still running and needed the textures loaded again.

\$\endgroup\$
  • \$\begingroup\$ I tried to put these lines in onResume of the Main class: Texture.setAssetManager(assetsManager); assetsManager.update(); But didn't work. \$\endgroup\$ – Gad Wissberg May 13 '16 at 8:23
0
\$\begingroup\$

Just had the same issue. Problem was that I had some texture regions as static fields. And to avoid finding texture region over and over for each object I added some extra static variable "intitialized" so I was finding regions only for first generated object, setting that variable for trued and all other were skipping region finding. That works fine when I run the game for the first time. But when I exit and run app again and it stayed in phone memory that static "initialized" variable also keeps it's value, meaning find regions were not called again. And old regions existed but their memory was flushed so when drawn I was getting only black rectangles for them.

Solved by calling findRegion() again.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.