是否应在后台线程调用Android AudioManager.adjustStreamVolume以避免ANR?
AudioManager.adjustStreamVolume引发ANR,是否需要移至后台线程?
我原本用以下代码处理用户按下VolumeUp按键时增大音乐音量:
@Override public boolean onKeyDown(int keyCode, KeyEvent e) { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { AudioManager audio = (AudioManager)getApplicationContext().getSystemService(AUDIO_SERVICE); audio.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); return true; } }
但在Google开发者控制台中发现了ANR问题,提示如下:
该Binder调用耗时过长,导致主线程等待并触发ANR。
at android.os.BinderProxy.transactNative (Native method) at android.os.BinderProxy.transact (Binder.java: 1154) at android.media.IAudioServiceStub$Proxy.adjustStreamVolume (IAudioService.java: 1150) at android.media.AudioManager.adjustStreamVolume (AudioManager.java: 820) at com.example.MyActivity.onKeyDown (MyActivity.java:42)您应用的代码触发了上述Binder调用,触发Binder调用的代码应移至主线程外。
请问是否需要在后台Thread中调用AudioManager.adjustStreamVolume?我写了这样的修改代码:
@Override public boolean onKeyDown(int keyCode, KeyEvent e) { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { new Thread(this::increaseVolume).start(); return true; } } private void increaseVolume() { AudioManager audio = (AudioManager)getApplicationContext().getSystemService(AUDIO_SERVICE); audio.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); }
需要,而且你的修改方向是对的。
从ANR调用栈可以明确看到,adjustStreamVolume内部的Binder跨进程调用在某些场景下耗时超出了主线程的容忍阈值,导致主线程阻塞触发ANR。把这个操作移到后台线程执行,能避免主线程被长时间占用,解决ANR问题。
不过有几个细节可以优化:
- 不要每次按键都创建新线程,可以用固定线程池或者HandlerThread复用线程,减少线程创建开销。
AudioManager实例可以提前在主线程初始化(比如Activity的onCreate方法中),不需要每次在后台线程重复调用getSystemService。
优化后的示例代码:
private AudioManager audioManager; private HandlerThread volumeThread; private Handler volumeHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); audioManager = (AudioManager) getApplicationContext().getSystemService(AUDIO_SERVICE); volumeThread = new HandlerThread("VolumeAdjustThread"); volumeThread.start(); volumeHandler = new Handler(volumeThread.getLooper()); } @Override public boolean onKeyDown(int keyCode, KeyEvent e) { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { volumeHandler.post(this::increaseVolume); return true; } return super.onKeyDown(keyCode, e); } private void increaseVolume() { audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); } @Override protected void onDestroy() { super.onDestroy(); volumeThread.quitSafely(); }
内容的提问来源于stack exchange,提问作者A.G.
相关产品推荐
相关产品推荐

