在Libgdx中加載游戲的資源素邪,我們一般使用AssetManager來(lái)進(jìn)行異步加載,但是其是單線程的猪半,有時(shí)候資源過(guò)多加載實(shí)在是太慢兔朦,而且現(xiàn)在手機(jī)的cpu核數(shù)越來(lái)越多斜姥,下面是自己動(dòng)手實(shí)現(xiàn)的一個(gè)多線程版本馍刮,其實(shí)原理是通過(guò)查手機(jī)cpu核數(shù)將資源動(dòng)態(tài)的分配給多個(gè)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();
}
}
/**
* 可以獲取到已經(jīng)加載好的texture砂碉、textureAtlas顽分、particleEffect虫几、sound寓娩。
* 如果未加載或未加載完成趟径,會(huì)采用同步方式加載一個(gè)并返回雾家。
*
* @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);
}
/**
* 添加要加載的資源到隊(duì)列英融,此時(shí)并未加載盏檐。加載為異步。 默認(rèn)僅加載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包中的類或內(nèi)部類熊响。
*/
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包中的類下的所有文件路徑對(duì)應(yīng)的資源
*
* @param cls
*/
public synchronized void removeDir(Class<?> cls) {
Array<String> filePaths = getPaths(cls);
for (String path : filePaths) {
if (isLoaded(path)) {
unload(path);
}
}
}
/**
* 銷毀所有已加載的資源,銷毀異步執(zhí)行器诗赌,并停止仍在進(jìn)行的異步加載
*/
@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();
}
}
}