在Android上监听音量变化事件

19

有没有一种方法可以监听 Android 设备的音量变化事件,而不只是接管音量按钮?

我所找到的唯一有效方法是这里,但它仅在音量控制消失后才开始生效。

并非所有设备都有音量按钮,我需要尽早捕获音量变化,而不是在音量对话框消失之后。


使用ContentObserver并从AudioManager中获取音量。我最近使用了它,效果很好。在stackoverflow上查看此解决方案 - user2503849
这是一篇较旧的文章,但令人遗憾的是,直到现在在Android中仍没有实现广播...五年过去了,仍然没有广播... - Opiatefuchs
1
@Opiatefuchs 可以在这里创建一个请求:https://issuetracker.google.com/issues。我会点赞 :) - android developer
你说得对。已完成...https://issuetracker.google.com/issues/62158875 但这是我第一次创建建议,我没有类似广播的代码示例。我希望他们会接受它... - Opiatefuchs
5个回答

30

更好的方式是,您可以按照以下方式注册ContentObserver

  getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, new ContentObserver(){...} );

您的ContentObserver可能看起来像这样:

public class SettingsContentObserver extends ContentObserver {
    private AudioManager audioManager;

    public SettingsContentObserver(Context context, Handler handler) {
        super(handler);
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    @Override
    public boolean deliverSelfNotifications() {
        return false;
    }

    @Override
    public void onChange(boolean selfChange) {
        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

        Log.d(TAG, "Volume now " + currentVolume);
    }
}

完成后:

getApplicationContext().getContentResolver().unregisterContentObserver(mContentObserver);

但需要注意的是,如果快速按下大量按钮,通知有时会出现延迟。


我该如何寻找音量按钮的长按功能? - Ruchir Baronia
onChanged 对我来说从未被调用,你知道为什么吗?API 23。权限需要以某种方式声明吗?没有错误,它只是从未被调用。 - Elliptica

4

目前,我的做法是使用onKeyDown监听音量按钮(并检查KEYCODE_VOLUME_DOWN、KEYCODE_VOLUME_MUTE、KEYCODE_VOLUME_UP),然后使用一个处理程序将新的可运行对象发布来检查音量级别。

此外,由于一些设备有音量对话框,我已添加了一个侦听器以在其消失时执行操作,参考此链接


1
由于 android.media.VOLUME_CHANGED_ACTION 不是一个可行的选项,而且 registerMediaButtonEventReceiver 甚至不起作用,你的方法似乎是目前唯一的途径。感谢 +1。 - uTubeFan
@ka3ak 你也在真实设备上测试过吗?如果没有,并且你没有这样的设备,你可以通过 Github 给我展示一个样例项目,我会为你测试。 - android developer
@androiddeveloper 我只在真实设备上测试了KeyEvent.KEYCODE_HEADSETHOOK事件,这是一个可以用于接听电话的耳机按钮,但我认为如果显示屏变暗,onKeyDown()对于任何按键都不起作用。我已经在这个论坛上遇到了讨论该问题的帖子,但我找不到它。如果我找到了,我会发布它。与此同时,您可以进行测试。我认为这并不太难。只需尝试使用您在答案中提到的事件来重现它,同时您手机的显示屏处于变暗状态。 - ka3ak
如果设备被调暗,前台应用程序不是你的应用程序,据我所知,因此它不应该获取事件。 - android developer
@ka3ak,我认为除了辅助功能服务之外,你已经没有其他选择了,因为你想要全局处理它,而不仅仅是当你的应用程序真正可见并在前台时。 - android developer
显示剩余2条评论

2

使用广播接收器VOLUME_CHANGED_ACTION,然后使用AudioManager获取当前音量。

<receiver android:name="VolumeChangeReceiver" >
    <intent-filter>
         <action android:name="android.media.VOLUME_CHANGED_ACTION" />
    </intent-filter>
</receiver>

你确定它能工作吗?你读过这个吗:https://dev59.com/AV_Va4cB1Zd3GeqPUpbW#8974510? - android developer
@androiddeveloper 在许多情况下它是有效的,但由于它是系统私有的,因此可能会在没有通知的情况下发生更改/删除,所以不能保证。 - CCJ
很简单:在SDK上获取内部/隐藏API,当类中的常量消失(Android Studio找不到引用并抛出错误)时,就该考虑另一个解决方案了。顺便说一下,对于任何正在寻找的人,该常量是AudioManager.VOLUME_CHANGED_ACTION。 - Edw590

1

2023年使用BroadcastReceiver的解决方案

可通过音量按钮和Android系统UI操作。 在此示例中,我将BroadcastReceiver作为内部类使用。 我在片段中使用类似的实现来更新拖动条。

public class AFragment extends Fragment {
    private Context context;
    private AudioManager audioManager;
    // THE STREAM TYPE YOU WANT VOLUME FROM
    private final int streamType = AudioManager.STREAM_MUSIC;
    private VolumeChangeListener volumeChangeListener;
    private IntentFilter intentFilter;
    private final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";

    public AFragment(Context context) {
        this.context = context;
        this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        this.volumeChangeListener = new VolumeChangeListener();
        this.intentFilter = new IntentFilter(VOLUME_CHANGED_ACTION);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        context.unregisterReceiver(volumeChangeListener);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context.registerReceiver(volumeChangeListener, intentFilter);
    }

    private class VolumeChangeListener extends BroadcastReceiver {
        // VOLUME CHANGED ACTION triggers 3 times
        // Making use of increment variable to make sure to skip the extra calls
        private int callCount = 0;

        @Override
        public void onReceive(Context context, Intent intent) {
            callCount++;
            if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) {
                if (callCount % 3 == 0) {
                    int SYSTEM_currentVolume = audioManager.getStreamVolume(streamType);

                    // DO WHATEVER YOU WANT WITH THE NEW VOLUME VALUE

                }
            }
        }
    }
}

IntentFilter android.media.VOLUME_CHANGED_ACTION 可在此处找到:Android Google Source


0
你可以使用: registerMediaButtonEventReceiver (ComponentName eventReceiver) 将组件注册为 MEDIA_BUTTON 意图的唯一接收者。
//  in your activity.
MediaButtonReceiver receiver = new MediaButtonReceiver();

// in onCreate put
registerMediaButtonEventReceiver(receiver); 

class MediaButtonReceiver implements BroadcastReceiver {
     void onReceive(Intent intent) {
          KeyEvent ke = (KeyEvent)intent.getExtra(Intent.EXTRA_KEY_EVENT); 
          if (ke .getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) {
            //action when volume goes down
          }
           if (ke .getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {
              //action when volume goes up
          }
     } 
}

   //In both onStop and onPause put :
   unregisterMediaButtonEventReceiver(receiver);

我们在这里定义了一个处理 ACTION_MEDIA_BUTTON 的 BroadcastReceiver,并使用包含导致广播发生的按键事件的 EXTRA_KEY_EVENT 来获取按下的内容并采取相应措施。

5
但这只是捕获音量按钮吗?对于没有音量按钮的设备会发生什么?还有,对于拥有音量按钮但用户在显示音量滑动条后触摸了它的设备呢?在这种情况下,我将无法捕获事件... - android developer
1
registerMediaButtonEventReceiver 可以用于耳机按钮,但不能用于手机硬件按钮。我刚试过了。 - uTubeFan
此功能在API 21+中已被弃用。 - Christopher Perry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接