Я не уверен, что это ошибка SDK или нет, но она связана с AudioAttributes.FLAG_DEEP_BUFFER
.Когда вы строите AudioTrack
через Builder, этот флаг устанавливается по умолчанию.См. Ваш случай здесь : проверка shouldEnablePowerSaving()
возвращает значение true и регистр переключателя приводит к PERFORMANCE_MODE_POWER_SAVING
с включенным FLAG_DEEP_BUFFER
.
Чтобы исправить это, вы должны отключить этот флаг дляПример добавления .setFlags(AudioAttributes.FLAG_LOW_LATENCY)
вызова к вашему AudioAttributes
, но для этого требуется минимум SDK 24. В противном случае вы можете вообще отказаться от использования AudioTrack.Builder
и построить трек следующим образом:
int audioSampleRate = 48000;
int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
int bufSize = AudioTrack.getMinBufferSize(audioSampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, audioSampleRate,
channelConfig, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM);
Или вы можете проверить код shouldEnablePowerSaving()
проверьте и сделайте так, чтобы он не проходил другим способом.
ОБНОВЛЕНИЕ : Таким образом, вышеупомянутое решение просто перенесло утечку в другой аудиопоток.Я исследовал дальше и заметил, что surfaceCreated()
вручает тот же объект Surface
на моем устройстве Android 8И действительно, это правильное поведение, поскольку Android 7 .Я предполагаю, что это как-то нарушает логику поверхности mediaSync: если вы удалите вызов на mediaSync.createInputSurface()
и добавите вызов mediaSync.setSurface(null)
перед его освобождением, утечка исчезнет.
Я не знаю, как обойти этопроблема, поскольку Surface
повторно используется системой, и нет способа узнать, когда он будет фактически уничтожен.Я предлагаю перейти на TextureView
, он имеет похожий, но более понятный API и не разрушает поверхность при паузе активности.Вам нужно будет удалить вызов createMediaSync()
из onResume()
и использовать его следующим образом:
setContentView(R.layout.activity_main);
final TextureView textureView = findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
createMediaSync();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
//release resources here
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
Удачи!