【问题标题】:android soundpool heapsize overflowandroid soundpool heapsize 溢出
【发布时间】:2011-09-15 09:09:47
【问题描述】:

我在Debug中运行了数百次这个错误,似乎并没有影响程序,但是我该如何摆脱它呢?

我知道根据其他帖子可以追溯到SoundPool

09-15 09:03:09.190:错误/AudioCache(34):堆大小溢出!请求大小:1052672,最大大小:1048576

【问题讨论】:

  • 然后增加你的堆大小
  • 您在哪个设备上出现该错误?它也发生在galaxytab10.1
  • @Roflcoptr 这是一个糟糕的建议,并且不适用于 Android。应用无法在 Android 上设置 JVM 堆大小。
  • Soundpool 的设计目的不是容纳大量数据,而只是一小部分音效。如果需要,请考虑使用 MediaPlayer 作为替代方案。

标签: android overflow soundpool


【解决方案1】:

SoundPool 将所有加载文件的缓冲区大小硬编码为 1M。因此,当您将太多文件加载到 SoundPool 中时,您可能会收到“堆大小溢出”错误。我在一个将游戏音效加载到 SoundPool 中的游戏项目上也遇到了这个问题,解决方案如下:

  1. 在 MediaPlayer 中播放长/大背景音乐。
  2. 在多个 SoundPool 实例中播放短声音文件以防止堆错误。多 SoundPool 的示例代码:


 /**
  * 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);
      }
    }
  }

}

【讨论】:

    【解决方案2】:

    也许您的媒体资源太大而无法使用 soundpool。 Soundpool 只播放短音效。在 Soundpool API 上,没有关于使用 soundpool 播放资源的最大值的规范,但是如果您使用它,则只播放简短的音效,例如射击游戏中的短暂爆炸。 如果这是您的问题,您应该使用 MediaPlayer 播放声音。

    【讨论】:

      【解决方案3】:

      此错误不可能与堆大小有关,因为没有任何设备具有如此小的堆 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'
      

      https://gitlab.com/olekdia/common/libraries/sound-pool

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-17
        • 1970-01-01
        • 2013-08-12
        • 2014-04-29
        • 2011-02-12
        • 1970-01-01
        • 2011-09-11
        相关资源
        最近更新 更多