Android平台LibGdx多线程下载图片频繁卡顿问题求助
这个问题我碰到过好几次了,核心原因大多是手动线程管理混乱、GL上下文操作不规范,再加上缺乏缓存机制导致的。给你几个针对性的解决方案:
别自己瞎创建线程,用LibGDX自带的
AsyncExecutor
手动new Thread会导致线程数量不受控,多次调用后线程池直接爆炸,CPU调度不过来必然卡顿。LibGDX的AsyncExecutor是专门为异步任务设计的,能精准控制并发数:// 初始化并发数(建议根据CPU核心数调整,比如设为2-4) private AsyncExecutor asyncExecutor = new AsyncExecutor(2); private void setIcoItemImage(final int i) { asyncExecutor.submit(new AsyncTask<Texture>() { @Override public Texture call() throws Exception { // 后台线程只做下载和Texture创建(Texture可后台创建,LibGDX内部会处理上下文同步) String imageUrl = yourImageUrlList.get(i); URL url = new URL(imageUrl); InputStream input = url.openStream(); return new Texture(input); } @Override public void onSuccess(Texture texture) { // 回到GL线程(渲染线程)创建Sprite并更新UI Sprite sprite = new Sprite(texture); yourIcoItems.get(i).setSprite(sprite); } @Override public void onFailure(Throwable t) { t.printStackTrace(); // 处理加载失败逻辑 } }); }绝对别在后台线程操作Sprite/UI相关内容
Sprite依赖OpenGL上下文,只有GL线程(也就是LibGDX的渲染线程)能安全操作它。你之前大概率是在后台直接创建Sprite,这会导致上下文冲突,轻则卡顿重则崩溃。上面的示例里,call()只做无上下文依赖的下载和Texture创建,onSuccess()在GL线程处理Sprite和UI更新,这才是正确姿势。加个图片缓存,避免重复下载和资源浪费
多次调用同一张图片时,重复下载太耗资源了,用LruCache做内存缓存:private LruCache<String, Texture> imageCache; // 初始化缓存,比如设为内存的1/8 private void initCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; imageCache = new LruCache<String, Texture>(cacheSize) { @Override protected int sizeOf(String key, Texture value) { // 返回Texture占用的内存大小(单位KB) return value.getWidth() * value.getHeight() * 4 / 1024; } }; } // 在AsyncTask的call()方法里先查缓存 @Override public Texture call() throws Exception { String imageUrl = yourImageUrlList.get(i); Texture cachedTexture = imageCache.get(imageUrl); if (cachedTexture != null && !cachedTexture.isDisposed()) { return cachedTexture; } // 无缓存再下载 URL url = new URL(imageUrl); InputStream input = url.openStream(); Texture texture = new Texture(input); imageCache.put(imageUrl, texture); return texture; }进阶点还可以加磁盘缓存,把下载的图片存到本地,下次直接从文件加载,进一步减少网络请求。
控制并发数,别贪多
AsyncExecutor的并发数别设太大,手机CPU一般是4核8线程,设2-4就够了。线程太多会导致CPU频繁切换上下文,反而拖慢整体速度。及时释放资源,避免内存泄漏
当新闻界面销毁或Item被回收时,要主动释放Texture和Sprite资源:@Override public void dispose() { super.dispose(); // 清空缓存并释放所有Texture for (Texture texture : imageCache.snapshot().values()) { if (!texture.isDisposed()) { texture.dispose(); } } imageCache.evictAll(); asyncExecutor.shutdown(); // 关闭AsyncExecutor }用AssetManager做进阶管理(可选)
如果项目复杂度高,LibGDX的AssetManager是更专业的选择,它自带异步加载、缓存、资源依赖管理:private AssetManager assetManager = new AssetManager(); private void loadImage(String url, int index) { if (!assetManager.isLoaded(url)) { // 网络图片需先下载到本地,再用AssetManager加载本地路径 assetManager.load(localFilePath, Texture.class); assetManager.finishLoadingAssetAsync(localFilePath, new AssetLoaderListener<Texture>() { @Override public void onAssetLoaded(Texture asset, String fileName) { Sprite sprite = new Sprite(asset); yourIcoItems.get(index).setSprite(sprite); } @Override public void onError(Throwable throwable, String fileName) { throwable.printStackTrace(); } }); } else { Texture texture = assetManager.get(localFilePath, Texture.class); yourIcoItems.get(index).setSprite(new Sprite(texture)); } }
这些方案应该能解决你50%概率卡顿的问题,核心就是规范线程管理、避免GL线程外操作UI资源、做好缓存和资源释放。
内容的提问来源于stack exchange,提问作者iappmaker

