Libgdx 使用多线程来加载资源,提升资源加载速度

在Libgdx中加载游戏的资源,我们一般使用AssetManager来进行异步加载,但是其是单线程的,有时候资源过多加载实在是太慢,而且现在手机的cpu核数越来越多,下面是自己动手实现的一个多线程版本,其实原理是通过查手机cpu核数将资源动态的分配给多个AssetsManager加载

package com.mytian.mgarden.utils.libgdxutil;

import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetErrorListener;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;

import java.lang.reflect.Field;
import java.util.HashMap;

/**
 * Created by gang.zhou on 2018/1/5.
 */

public class AssetsManagerPool implements Disposable {
    public static int SIZE = Math.max(Runtime.getRuntime().availableProcessors() / 2, 1);
    private Array<Assets> mAssetsArray = new Array(SIZE);
    private int counter = 0;
    private static HashMap<Class, Array<String>> pathM = new HashMap<>();
    private static AssetsManagerPool instance;
    private volatile boolean isLoad;

    public static AssetsManagerPool getInstance() {
        synchronized (AssetsManagerPool.class) {
            if (null == instance) {
                instance = new AssetsManagerPool();
            }
        }
        return instance;
    }

    private AssetsManagerPool() {
        for (int i = 0; i < SIZE; i++) {
            mAssetsArray.add(new Assets(new InternalFileHandleResolver()));
        }
        for (final Assets assets : mAssetsArray) {
            assets.setErrorListener(new AssetErrorListener() {
                @Override
                public void error(AssetDescriptor asset, Throwable throwable) {
                    throw new GdxRuntimeException(throwable);
                }
            });
        }
    }

    public synchronized void add(final String filePath) {
        for (final Assets assets : mAssetsArray) {
            if (assets.isLoaded(filePath)) {
                return;
            }
        }
        mAssetsArray.get(counter % SIZE).add(filePath);
        ++counter;
        isLoad = true;
    }


    public static Array<String> getPaths(Class<?> cls) {
        if (pathM.containsKey(cls)) {
            return pathM.get(cls);
        }
        final Array<String> filePaths = new Array<>();
        final Class<?>[] classes = cls.getDeclaredClasses();
        if (null != classes) {
            for (final Class<?> c : classes) {
                filePaths.addAll(getPaths(c));
            }
        }
        final Field[] fields = cls.getFields();
        try {
            for (final Field f : fields) {
                if (String.class == f.getType()) {
                    f.setAccessible(true);
                    filePaths.add((String) f.get(null));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        pathM.put(cls, filePaths);
        return filePaths;
    }

    public synchronized void addDir(final Class<?> cls) {
        final Array<String> filePaths = getPaths(cls);
        for (final String path : filePaths) {
            if (path.endsWith(".png")) {
                if (filePaths.contains(path.replace(".png", ".fnt"), false)
                        || filePaths.contains(path.replace(".png", ".atlas"), false)
                        || filePaths.contains(path.replace(".png", ".skel"), false)
                        || filePaths.contains(path.replace(".png", ".json"), false)) {
                    continue;
                }
                boolean isFind = false;
                for (int i = 2; i < 20; i++) {
                    if (path.endsWith(i + ".png")) {
                        if (filePaths.contains(path.replace(i + ".png", ".atlas"), false)
                                || filePaths.contains(path.replace(i + ".png", ".fnt"), false)
                                || filePaths.contains(path.replace(i + ".png", ".skel"), false)
                                || filePaths.contains(path.replace(i + ".png", ".json"), false)) {
                            isFind = true;
                        }
                        break;
                    }
                }
                if (isFind) {
                    continue;
                }
            } else if (path.endsWith(".atlas")) {
                if (filePaths.contains(path.replace(".atlas", ".json"), false)
                        || filePaths.contains(path.replace(".atlas", ".skel"), false)) {
                    continue;
                }
            } else if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            add(path);
        }
    }

    public synchronized void removeDir(Class<?> cls) {
        final Array<String> filePaths = getPaths(cls);
        for (final String path : filePaths) {
            if (path.endsWith(".png")) {
                if (filePaths.contains(path.replace(".png", ".fnt"), false)
                        || filePaths.contains(path.replace(".png", ".atlas"), false)
                        || filePaths.contains(path.replace(".png", ".skel"), false)
                        || filePaths.contains(path.replace(".png", ".json"), false)) {
                    continue;
                } else {
                    boolean isFind = false;
                    for (int i = 2; i < 15; i++) {
                        if (path.endsWith(i + ".png")) {
                            if (filePaths.contains(path.replace(i + ".png", ".atlas"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".fnt"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".skel"), false)
                                    || filePaths.contains(path.replace(i + ".png", ".json"),
                                    false)) {
                                isFind = true;
                            }
                            break;
                        }
                    }
                    if (isFind) {
                        continue;
                    }
                }
            } else if (path.endsWith(".atlas")) {
                if (filePaths.contains(path.replace(".atlas", ".json"), false)
                        || filePaths.contains(path.replace(".atlas", ".skel"), false)) {
                    continue;
                }
            } else if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            unload(path);
        }
    }


    public synchronized void unload(final String path) {
        for (final Assets assets : mAssetsArray) {
            try {
                assets.unload(path);
            } catch (Exception e) {

            }
        }
    }

    public synchronized Array<String> getAssetNames(){
        final Array<String> assetNameArray = new Array<>();
        for (final Assets assets : mAssetsArray) {
            assetNameArray.addAll(assets.getAssetNames());
        }
        return assetNameArray;
    }

    public boolean update() {
        boolean complete = true;
        for (final Assets assets : mAssetsArray) {
            complete = assets.update() && complete;
        }
        if (complete) {
            counter = 0;
        }
        return complete;
    }

    public float getProgress() {
        float progress = 0;
        for (final Assets assets : mAssetsArray) {
            progress += assets.getProgress();
        }
        return progress / SIZE;
    }

    public synchronized <T> T get(String path) {
        for (final Assets assets : mAssetsArray) {
            if (assets.isLoaded(path)) {
                return (T) assets.get(path, assets.getAssetType(path));
            }
        }
        mAssetsArray.get(counter % SIZE).add(path);
        mAssetsArray.get(counter % SIZE).finishLoadingAsset(path);
        return mAssetsArray.get(counter++ % SIZE).get(path);
    }

    public void render() {
        if (null != instance && isLoad) {
            isLoad = !update();
        }
    }

    public FileHandle file(String path) {
        return mAssetsArray.get(0).getFileHandleResolver().resolve(path);
    }

    public synchronized void clear() {
        for (final Assets assets : mAssetsArray) {
            assets.finishLoading();
            assets.clear();
        }
    }

    public void finishLoading() {
        for (final Assets assets : mAssetsArray) {
            assets.finishLoading();
        }
    }

    @Override
    public synchronized void dispose() {
        synchronized (AssetsManagerPool.class) {
            instance = null;
        }
        for (final Assets assets : mAssetsArray) {
            try {
                assets.dispose();
            } catch (Exception e) {

            }
        }
        mAssetsArray.clear();
    }
}
package com.mytian.mgarden.utils.libgdxutil;

import com.badlogic.gdx.assets.AssetLoaderParameters;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.BitmapFontLoader;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.TextureLoader;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.async.AsyncExecutor;
import com.esotericsoftware.spine.SkeletonData;
import com.mytian.mgarden.stages.classes.BaseClassLoadingGroup;
import com.mytian.mgarden.stages.classes.ClassLoadingGroup;
import com.mytian.mgarden.stages.classes.hh.ClassLoadingGroupHH;
import com.mytian.mgarden.utils.particleutil.CCDictionaryLoader;

import java.lang.reflect.Field;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * Created by ayo on 2016/12/9.
 */

public class Assets extends AssetManager {
    private static Assets instance;
    public final static TextureLoader.TextureParameter RGB4444_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();
    public final static TextureLoader.TextureParameter RGB888_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();
    public final static TextureLoader.TextureParameter RGB565_TEXTURE_PARAMETER = new TextureLoader.TextureParameter();


    public final static  BitmapFontLoader.BitmapFontParameter BITMAP_FONT_PARAMETER_TEXTURE_FILTER =
            new BitmapFontLoader.BitmapFontParameter();
    static{
        BITMAP_FONT_PARAMETER_TEXTURE_FILTER.minFilter = Texture.TextureFilter.Linear;
        BITMAP_FONT_PARAMETER_TEXTURE_FILTER.magFilter = Texture.TextureFilter.Linear;
    }

    private volatile boolean isLoad;

    static {
        RGB4444_TEXTURE_PARAMETER.format = Pixmap.Format.RGBA4444;
        RGB888_TEXTURE_PARAMETER.format = Pixmap.Format.RGB888;
        RGB565_TEXTURE_PARAMETER.format = Pixmap.Format.RGB565;
    }

    protected Assets(FileHandleResolver resolver) {
        super(resolver);
        instance = this;
        setLoader(SkeletonData.class, new SkeletonLoader(resolver));
        setLoader(mytian.esotericsoftware.spine.SkeletonData.class
                , new SkeletonLoader3(resolver));
        setLoader(ObjectMap.class, new CCDictionaryLoader(resolver));
        try {
            Field executor = AssetManager.class.getDeclaredField("executor");
            executor.setAccessible(true);
            final AsyncExecutor mAsyncExecutor = (AsyncExecutor) executor.get(this);
            executor = AsyncExecutor.class.getDeclaredField("executor");
            executor.setAccessible(true);
            final ThreadPoolExecutor mThreadPoolExecutor = (ThreadPoolExecutor) executor.get(mAsyncExecutor);
            mThreadPoolExecutor.setThreadFactory(new ThreadFactory() {
                @Override
                public Thread newThread(Runnable runnable) {
                    final Thread thread = new Thread(runnable, "AsynchExecutor-Thread " + System.currentTimeMillis());
                    thread.setPriority(Thread.MIN_PRIORITY);
                    thread.setDaemon(true);
                    return thread;
                }
            });
            mThreadPoolExecutor.setCorePoolSize(0);
            mThreadPoolExecutor.setMaximumPoolSize(1);
            mThreadPoolExecutor.setKeepAliveTime(3, TimeUnit.MINUTES);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 可以获取到已经加载好的texture、textureAtlas、particleEffect、sound。
     * 如果未加载或未加载完成,会采用同步方式加载一个并返回。
     *
     * @param <T>
     */

    @SuppressWarnings("unchecked")
    public <T> T get(String path) {
        if (isLoaded(path)) {
            return (T) get(path, getAssetType(path));
        } else {
            add(path);
            finishLoadingAsset(path);
            return get(path);
        }
    }

    public FileHandle file(String path) {
        return getFileHandleResolver().resolve(path);
    }

    /**
     * 添加要加载的资源到队列,此时并未加载。加载为异步。 默认仅加载png/jpg、p/plist(粒子文件)、atlas、mp3/wav格式的文件。
     *
     * @param filePath 资源的internal路径。可直接传入com.mytian.R包中的变量
     */
    public void add(final String filePath) {
        if (isLoaded(filePath)) {
            return;
        }
        final String lowerPath = filePath.toLowerCase();
        if (lowerPath.endsWith(".png") || lowerPath.endsWith(".jpg")) {
            if (lowerPath.contains("rgb4444_")) {
                load(filePath, Texture.class, RGB4444_TEXTURE_PARAMETER);
            } else if (lowerPath.contains("rgb888_")) {
                load(filePath, Texture.class, RGB888_TEXTURE_PARAMETER);
            } else if (lowerPath.contains("rgb565_")) {
                load(filePath, Texture.class, RGB565_TEXTURE_PARAMETER);
            } else {
                load(filePath, Texture.class);
            }
        } else if (lowerPath.endsWith(".mp3") || lowerPath.endsWith(".wav")) {
            load(filePath, Sound.class);
        } else if (lowerPath.endsWith(".json") || lowerPath.endsWith(".skel")) {
            if (lowerPath.contains("3_6_52_1")) {
                if (lowerPath.endsWith("erji59.skel") || lowerPath.endsWith("erji60.skel")
                    || lowerPath.endsWith("erji61.skel") || lowerPath.endsWith("erji62.skel")
                    || lowerPath.endsWith("erji63.skel")) {
                    load(filePath, mytian.esotericsoftware.spine.SkeletonData.class,new
                        SkeletonLoader3.SkeletonDataParam(0.90f));
                } else {
                    load(filePath, mytian.esotericsoftware.spine.SkeletonData.class);
                }
            } else {
                load(filePath, SkeletonData.class);
            }
        } else if (lowerPath.endsWith(".atlas")) {
            load(filePath, TextureAtlas.class);
        } else if (lowerPath.endsWith(".fnt")) {
            load(filePath, BitmapFont.class, BITMAP_FONT_PARAMETER_TEXTURE_FILTER);
        } else if (lowerPath.endsWith(".plist")) {
            load(filePath, ObjectMap.class);
        }
    }

    /**
     * 遍历所传R包中的类下的所有文件路径。
     *
     * @param cls 传入com.mytian.R包中的类或内部类。
     */
    public synchronized void addDir(final Class<?> cls) {
        Array<String> filePaths = getPaths(cls);
        for (String path : filePaths) {
            if (path.endsWith(".png") && (filePaths.contains(path.replace(".png", ".atlas"), false)
                    || filePaths.contains(path.replace(".png", ".p"), false))) {
                continue;
            }
            if (path.endsWith(".json") && !filePaths.contains(path.replace(".json", ".atlas"), false)) {
                continue;
            }
            if (path.endsWith(".mp3") || path.endsWith(".wav")) {
                if (!path.contains("sfx") && !path.contains("svo")) {
                    continue;
                }
            }
            add(path);
        }
    }

    public synchronized void addDir(String className) {
        try {
            addDir(Class.forName(className));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 反射遍历R类下的文件路径
    protected Array<String> getPaths(Class<?> cls) {
        Array<String> filePaths = new Array<String>();
        Class<?>[] classes = cls.getDeclaredClasses();
        if (classes != null) {
            for (Class<?> c : classes) {
                filePaths.addAll(getPaths(c));
            }
        }
        Field[] fileds = cls.getFields();
        try {
            for (Field f : fileds) {
                if (f.getType() == String.class) {
                    f.setAccessible(true);
                    String value = (String) f.get(null);
                    filePaths.add(value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filePaths;
    }

    /**
     * 释放该R包中的类下的所有文件路径对应的资源
     *
     * @param cls
     */

    public synchronized void removeDir(Class<?> cls) {
        Array<String> filePaths = getPaths(cls);
        for (String path : filePaths) {
            if (isLoaded(path)) {
                unload(path);
            }
        }
    }


    /**
     * 销毁所有已加载的资源,销毁异步执行器,并停止仍在进行的异步加载
     */
    @Override
    public synchronized void dispose() {
        synchronized (Assets.class) {
            instance = null;
            try {
                super.dispose();
            } catch (Exception e) {

            }
            try {
                if (null != ClassLoadingGroup.mNativeFont) {
                    ClassLoadingGroup.mNativeFont.dispose();
                    ClassLoadingGroup.mNativeFont = null;
                }
            } catch (Exception e) {

            }
            try {
                if (null != ClassLoadingGroupHH.mNativeFont) {
                    ClassLoadingGroupHH.mNativeFont.dispose();
                    ClassLoadingGroupHH.mNativeFont = null;
                }
            } catch (Exception e) {

            }
            try {
                if (null != BaseClassLoadingGroup.mNativeFont) {
                    BaseClassLoadingGroup.mNativeFont.dispose();
                    BaseClassLoadingGroup.mNativeFont = null;
                }
            } catch (Exception e) {

            }
        }
    }

    @Override
    public synchronized <T> void load(final String fileName, final Class<T> type
            , AssetLoaderParameters<T> parameter) {
        super.load(fileName, type, parameter);
        isLoad = true;
    }

    public void render() {
        if (null != instance && isLoad) {
            isLoad = !update();
        }
    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • 前言 个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的。这篇文章主要...
    touch_The_Sky阅读 390评论 0 2
  • 前言 个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的。这篇文章主要...
    尧淳阅读 672评论 0 17
  • 前言 这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。 这些多线程的问题,有些来源于各大网站...
    java成功之路阅读 830评论 0 9
  • 40个问题汇总 1、多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在...
    写代码的杰西阅读 449评论 0 6
  • 我从来没有输过,更不想输在分手这件事上。 在一个沉寂了半年的微信老乡群里面,突然发来了一条信息。 老高:有谁在深圳...
    方不见阅读 5,295评论 30 99