我在Debug中运行时遇到这个错误数百次,它似乎并没有影响程序,但是我该如何摆脱它呢?
我知道它可以根据其他帖子追溯到SoundPool
09-15 09:03:09.190:错误/AudioCache(34):堆大小溢出!请求大小:1052672,最大大小:1048576
SoundPool is hard code the buffer size as 1M for all loaded file. So you probably will get 'heap size overflow' error when you load too much files into SoundPool. I also have this issue on a game project that will load game sound FX into SoundPool and the solution is as follow:
/**
* Multi SoundPool to prevent memory error.
*/
public class SoundPools {
private static final String TAG = "SoundPools";
private static final int MAX_STREAMS_PER_POOL = 15;
private List<SoundPoolContainer> containers;
public SoundPools() {
containers = Collections.synchronizedList(new ArrayList<SoundPoolContainer>());
}
public void loadSound(Context context, String id, String file) {
Log.d(TAG, "SouldPools load sound " + file);
try {
for (SoundPoolContainer container : containers) {
if (container.contains(id)) {
return;
}
}
for (SoundPoolContainer container : containers) {
if (!container.isFull()) {
container.load(context, id, file);
return;
}
}
SoundPoolContainer container = new SoundPoolContainer();
containers.add(container);
container.load(context, id, file);
} catch (Exception e) {
Log.w(TAG, "Load sound error", e);
}
}
public void playSound(Context context, String id, String file) {
Log.d(TAG, "SouldPools play sound " + file);
try {
for (SoundPoolContainer container : containers) {
if (container.contains(id)) {
container.play(context, id, file);
return;
}
}
for (SoundPoolContainer container : containers) {
if (!container.isFull()) {
container.play(context, id, file);
return;
}
}
SoundPoolContainer container = new SoundPoolContainer();
containers.add(container);
container.play(context, id, file);
} catch (Exception e) {
Log.w(TAG, "Play sound error", e);
}
}
public void onPause() {
for (SoundPoolContainer container : containers) {
container.onPause();
}
}
public void onResume() {
for (SoundPoolContainer container : containers) {
container.onResume();
}
}
private static class SoundPoolContainer {
SoundPool soundPool;
Map<String, Integer> soundMap;
AtomicInteger size;
public SoundPoolContainer() {
this.soundPool = new SoundPool(MAX_STREAMS_PER_POOL, android.media.AudioManager.STREAM_MUSIC, 0);
this.soundMap = new ConcurrentHashMap<String, Integer>(MAX_STREAMS_PER_POOL);
this.size = new AtomicInteger(0);
}
public void load(Context context, String id, String file) {
try {
this.size.incrementAndGet();
soundMap.put(id, soundPool.load(context.getAssets().openFd(file), 1));
} catch (Exception e) {
this.size.decrementAndGet();
Log.w(TAG, "Load sound error", e);
}
}
public void play(Context context, String id, String file) {
android.media.AudioManager audioManager = (android.media.AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
final int streamVolume = audioManager.getStreamVolume(android.media.AudioManager.STREAM_MUSIC);
Integer soundId = soundMap.get(id);
if (soundId == null) {
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
soundPool.play(sampleId, streamVolume, streamVolume, 1, 0, 1f);
}
});
try {
this.size.incrementAndGet();
soundPool.load(context.getAssets().openFd(file), 1);
} catch (IOException e) {
this.size.decrementAndGet();
Log.w(TAG, "Load/Play sound error", e);
}
} else {
try {
soundPool.play(soundId, streamVolume, streamVolume, 1, 0, 1f);
} catch (Exception e) {
Log.w(TAG, "Play sound error", e);
}
}
}
public boolean contains(String id) {
return soundMap.containsKey(id);
}
public boolean isFull() {
return this.size.get() >= MAX_STREAMS_PER_POOL;
}
public void onPause() {
try {
soundPool.autoPause();
} catch (Exception e) {
Log.w(TAG, "Pause SoundPool error", e);
}
}
public void onResume() {
try {
soundPool.autoResume();
} catch (Exception e) {
Log.w(TAG, "Resume SoundPool error", e);
}
}
}
}
也许您的媒体资源太大而无法使用 soundpool。Soundpool 只播放短音效。在 Soundpool API 上,没有关于使用 soundpool 播放资源的最大值的规范,但是如果您使用它,则只播放简短的音效,例如射击游戏中的短暂爆炸。如果这是您的问题,您应该使用 MediaPlayer 播放声音。
此错误不可能与堆大小有关,因为没有任何设备具有如此小的堆 1052672 ~ 1Mb.,而是与SoundPool
缓冲区大小有关。
内存SoundPool
中的原始数据硬编码限制为 1Mb 。PCM_16
此限制适用于您加载到内存中的每个声音,而不适用于每个声音的总和SoundPool
。所以投票最多的答案SoundPools
是非常错误的!
让我介绍一下SoundPoolCompat
哪些AudioTrack
在引擎盖下使用并有自定义bufferSize
。指定缓冲区内的所有数据都将被加载到内存中,并像SoundPool
这样以很小的延迟播放。所有超过该值的数据bufferSize
都将按需加载(这会增加延迟,类似于MediaPlayer
)。Api 与 非常相似SoundPool
,还添加了从 Uri 加载声音的功能(例如 gdrive)。并且有一种playOnce
方法,文件播放后将卸载所有资源。
implementation 'com.olekdia:sound-pool:3.0.2'