Android 在后台服务中收听音量按钮?

Android 在后台服务中收听音量按钮?,android,volume,listen,Android,Volume,Listen,我知道如何在活动中收听音量按钮。但是我可以在后台服务中这样做吗?如果是,怎么做?在这种情况下,Android不会记录与音量按钮交互的API。所以我猜答案是否定的。从关于这个话题的另外两个问题来看,答案是否定的 ,, 服务根本不接收KeyEvent回调。注意:这只适用于活动,而不适用于问题所述的服务。 根据需要回调的上下文,可能会有其他解决方案 为了能够检测音量按钮,活动需要覆盖dispatchKeyEvent功能。为了使它出现在多个活动中,可以编写一个超类,其中包含被重写的函数,该函数由所有后

我知道如何在活动中收听音量按钮。但是我可以在后台服务中这样做吗?如果是,怎么做?

在这种情况下,Android不会记录与音量按钮交互的API。所以我猜答案是否定的。

从关于这个话题的另外两个问题来看,答案是否定的

,,


服务根本不接收KeyEvent回调。

注意:这只适用于活动,而不适用于问题所述的服务。

根据需要回调的上下文,可能会有其他解决方案

为了能够检测音量按钮,活动需要覆盖
dispatchKeyEvent
功能。为了使它出现在多个活动中,可以编写一个超类,其中包含被重写的函数,该函数由所有后续活动扩展

以下是检测音量上升/下降按键的代码:

    // Over-ride this function to define what should happen when keys are pressed (e.g. Home button, Back button, etc.)
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) 
    {
        if (event.getAction() == KeyEvent.ACTION_DOWN)
        {
            switch (event.getKeyCode()) 
            {
                case KeyEvent.KEYCODE_VOLUME_UP:
                    // Volume up key detected
                    // Do something
                    return true;
                case KeyEvent.KEYCODE_VOLUME_DOWN:
                    // Volume down key detected
                    // Do something
                    return true;
            }
        }

        return super.dispatchKeyEvent(event);
    }

AOSP音乐应用程序有一个服务(),通过注册广播接收器()响应音量键事件

下面是注册接收器的代码片段:

    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    ComponentName rec = new ComponentName(getPackageName(),
            MediaButtonIntentReceiver.class.getName());
    mAudioManager.registerMediaButtonEventReceiver(rec);
另外,不要忘记清单:

    <receiver android:name="com.android.music.MediaButtonIntentReceiver">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
            <action android:name="android.media.AUDIO_BECOMING_NOISY" />
        </intent-filter>
    </receiver>


即使音乐应用程序不在前台,该功能也能正常工作。这不是你想要的吗?

这是可能的。使用下面的代码(对于较新的Android版本,尤其是棉花糖,请参阅答案的底部):

然后在onDestroy中注销:

getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
请注意,此示例根据媒体卷的变化来判断,如果您想使用其他卷,请更改它

更新:

上述方法可能对棉花糖不起作用,但自从引入MediaSession以来,现在有了更好的方法!因此,首先必须将代码迁移到MediaController/MediaSession模式,然后使用以下代码:

private VolumeProviderCompat myVolumeProvider = null;

myVolumeProvider = new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, maxVolume, currentVolume) {
    @Override
    public void onAdjustVolume(int direction) {
        // <0 volume down
        // >0 volume up

    }
};

mSession.setPlaybackToRemote(myVolumeProvider);

结帐…这将有助于解决您的问题。。。多个应用程序可能希望从后台监听按钮按下,这可能就是为什么按键事件只能由活动处理的原因,因为它们是用户按键的界面。

我能够使用
MediaSession在android 5+设备上工作。然而,@SSUKK建议的
ContentObserver
在4.4和7.0设备上对我都不起作用(至少在我一直测试的ROM上是如此)。 下面是一个在android 5+上运行的完整示例

服务:

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.media.VolumeProviderCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;

public class PlayerService extends Service {
    private MediaSessionCompat mediaSession;

    @Override
    public void onCreate() {
        super.onCreate();
        mediaSession = new MediaSessionCompat(this, "PlayerService");
        mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
        mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_PLAYING, 0, 0) //you simulate a player which plays something.
                .build());

        //this will only work on Lollipop and up, see https://code.google.com/p/android/issues/detail?id=224134
        VolumeProviderCompat myVolumeProvider =
                new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, /*max volume*/100, /*initial volume level*/50) {
            @Override
            public void onAdjustVolume(int direction) {
                /*
                -1 -- volume down
                1 -- volume up
                0 -- volume button released
                 */
            }
        };

        mediaSession.setPlaybackToRemote(myVolumeProvider);
        mediaSession.setActive(true);
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mediaSession.release();
    }
}
AndroidManifest.xml
中:

<application ...>
    ...
    <service android:name=".PlayerService"/>
</application>
有几件事需要注意:

  • 它完全拦截音量按钮,因此当此代码运行时,您将无法使用音量按钮调整铃声音量。这可能是可以解决的,我只是没有尝试
  • 如果按原样运行示例,即使屏幕关闭且应用已从“最近的应用”列表中删除,音量按钮仍将由应用控制。您必须转到
    设置->应用程序
    ,找到应用程序并强制停止它以恢复音量按钮

您需要播放服务中的空白声音,然后只有您可以收听音量变化。下面的内容对我有用

台阶 1.将blank.mp3放入原始文件夹(从下载) 2.在onStartCommand()上启动介质 3.您必须选择停止并释放mediaplayer。最好在onDestroy()中这样做 4.创建将侦听音量变化的广播接收器 6.在onDestroy()中注销broadccast接收器 这就需要棒棒糖(v5.0/API21)或更高版本 我的目标是调整服务的系统容量。不过,媒体可以采取任何行动

public class VolumeKeyController {

    private MediaSessionCompat mMediaSession;
    private final Context mContext;

    public VolumeKeyController(Context context) {
        mContext = context;
    }

    private void createMediaSession() {
        mMediaSession = new MediaSessionCompat(mContext, KeyUtil.log);

        mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
        mMediaSession.setPlaybackState(new Builder()
                .setState(PlaybackStateCompat.STATE_PLAYING, 0, 0)
                .build());
        mMediaSession.setPlaybackToRemote(getVolumeProvider());
        mMediaSession.setActive(true);
    }

    private VolumeProviderCompat getVolumeProvider() {
        final AudioManager audio = mContext.getSystemService(Context.AUDIO_SERVICE);

        int STREAM_TYPE = AudioManager.STREAM_MUSIC;
        int currentVolume = audio.getStreamVolume(STREAM_TYPE);
        int maxVolume = audio.getStreamMaxVolume(STREAM_TYPE);
        final int VOLUME_UP = 1;
        final int VOLUME_DOWN = -1;

        return new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, maxVolume, currentVolume) {
            @Override
            public void onAdjustVolume(int direction) {
                // Up = 1, Down = -1, Release = 0
                // Replace with your action, if you don't want to adjust system volume
                if (direction == VOLUME_UP) {
                    audio.adjustStreamVolume(STREAM_TYPE,
                            AudioManager.ADJUST_RAISE, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
                }
                else if (direction == VOLUME_DOWN) {
                    audio.adjustStreamVolume(STREAM_TYPE,
                            AudioManager.ADJUST_LOWER, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
                }
                setCurrentVolume(audio.getStreamVolume(STREAM_TYPE));
            }
        };
    }

    // Call when control needed, add a call to constructor if needed immediately
    public void setActive(boolean active) {
        if (mMediaSession != null) {
            mMediaSession.setActive(active);
            return;
        }
        createMediaSession();
    }

    // Call from Service's onDestroy method
    public void destroy() {
        if (mMediaSession != null) {
            mMediaSession.release();
        }
    }
}

不幸的是,这是安卓系统的另一个领域,它有五种不同的“解决问题”的方法,但大多数都不太好用。为了我自己的理智,我将尝试在下面列出所有不同的方法

解决 1)
MediaSession
(来自服务) 丹尼斯·克尼亚舍夫的答复:

缺点:
1.需要Android SDK 21+(Android 9+)

2)
android.media.VOLUME\u已更改\u操作
(来自服务) Nikhil的答复:

缺点:
1.不是SDK的官方部分:
2.忽略第一次按音量键(因为它只显示音量栏)。
3.100%时忽略音量增大键,0%时忽略音量减小键

3)
ContentObserver
(来自服务) 苏克的回答:(第一部分)

缺点:
1.在较新版本的Android中不起作用:
2.忽略第一次按音量键(因为它只显示音量栏)。
3.100%时忽略音量增大键,0%时忽略音量减小键

4)
AudioManager.registerDiaButtoneVentreceiver
(来自服务) 乔的回答:

缺点:
1.在大多数ROM上不起作用:

5)
onKeyDown
(来自活动) 迪帕利的答复:

缺点:
1.如果屏幕关闭,在不同的应用程序中等,则不工作

6)
dispatchKeyEvent
(来自活动) 莫里斯·加文的答复:

缺点:
1.如果屏幕关闭,在不同的应用程序中等,则不工作

结论 我目前使用的解决方案是#1,因为:

  • 这是SDK的官方部分
  • 它可以从服务中使用。(即,无论您使用的是什么应用程序)
  • 它捕获每次音量按键,而不考虑当前的音量/ui状态
  • 它在屏幕关闭时工作

  • 如果您发现了其他问题,或者您发现了其中一些问题的更多缺点,请告诉我

    您需要收听音量按钮还是音量变化?请参阅了解AOSP音乐应用程序如何实现此功能:)有多种方法可以从服务接收音量按钮事件。事实上,有四种不同的方式!有关不同方法的摘要,请参阅。在库存ROM中,音量按钮不被视为“媒体按钮”。我猜此应用程序能够获得音量按钮,因为它使用自定义android ROM运行。@elgui能否提供指向docu的链接
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.v4.media.VolumeProviderCompat;
    import android.support.v4.media.session.MediaSessionCompat;
    import android.support.v4.media.session.PlaybackStateCompat;
    
    public class PlayerService extends Service {
        private MediaSessionCompat mediaSession;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mediaSession = new MediaSessionCompat(this, "PlayerService");
            mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
            mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                    .setState(PlaybackStateCompat.STATE_PLAYING, 0, 0) //you simulate a player which plays something.
                    .build());
    
            //this will only work on Lollipop and up, see https://code.google.com/p/android/issues/detail?id=224134
            VolumeProviderCompat myVolumeProvider =
                    new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, /*max volume*/100, /*initial volume level*/50) {
                @Override
                public void onAdjustVolume(int direction) {
                    /*
                    -1 -- volume down
                    1 -- volume up
                    0 -- volume button released
                     */
                }
            };
    
            mediaSession.setPlaybackToRemote(myVolumeProvider);
            mediaSession.setActive(true);
        }
    
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mediaSession.release();
        }
    }
    
    <application ...>
        ...
        <service android:name=".PlayerService"/>
    </application>
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        startService(new Intent(this, PlayerService.class));
    }
    
    private MediaPlayer mediaPlayer;
    
    public MyService() {
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
        ........
    
        mediaPlayer = MediaPlayer.create(this, R.raw.blank);
        mediaPlayer.setLooping(true);
        mediaPlayer.start();
    
        .......
    
        return START_STICKY;
    }
    
    @Override
    public void onDestroy() {
    
        mediaPlayer.stop();
        mediaPlayer.release();
    
        super.onDestroy();
    }
    
    int volumePrev = 0;
    
    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
    
            if ("android.media.VOLUME_CHANGED_ACTION".equals(intent.getAction())) {
    
                int volume = intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_VALUE",0);
    
                Log.i(TAG, "volume = " + volume);
    
                if (volumePrev  < volume) {
                    Log.i(TAG, "You have pressed volume up button");
                } else {
                    Log.i(TAG, "You have pressed volume down button");
                }
                volumePrev = volume;
            }
        }
    };
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        .....
    
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.media.VOLUME_CHANGED_ACTION");
        registerReceiver(broadcastReceiver, filter);
    
        ....
    
        return START_STICKY;
    }
    
     @Override
    public void onDestroy() {
        .....
    
        unregisterReceiver(broadcastReceiver);
    
        .....
    
        super.onDestroy();
    }
    
    public class VolumeKeyController {
    
        private MediaSessionCompat mMediaSession;
        private final Context mContext;
    
        public VolumeKeyController(Context context) {
            mContext = context;
        }
    
        private void createMediaSession() {
            mMediaSession = new MediaSessionCompat(mContext, KeyUtil.log);
    
            mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
            mMediaSession.setPlaybackState(new Builder()
                    .setState(PlaybackStateCompat.STATE_PLAYING, 0, 0)
                    .build());
            mMediaSession.setPlaybackToRemote(getVolumeProvider());
            mMediaSession.setActive(true);
        }
    
        private VolumeProviderCompat getVolumeProvider() {
            final AudioManager audio = mContext.getSystemService(Context.AUDIO_SERVICE);
    
            int STREAM_TYPE = AudioManager.STREAM_MUSIC;
            int currentVolume = audio.getStreamVolume(STREAM_TYPE);
            int maxVolume = audio.getStreamMaxVolume(STREAM_TYPE);
            final int VOLUME_UP = 1;
            final int VOLUME_DOWN = -1;
    
            return new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, maxVolume, currentVolume) {
                @Override
                public void onAdjustVolume(int direction) {
                    // Up = 1, Down = -1, Release = 0
                    // Replace with your action, if you don't want to adjust system volume
                    if (direction == VOLUME_UP) {
                        audio.adjustStreamVolume(STREAM_TYPE,
                                AudioManager.ADJUST_RAISE, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
                    }
                    else if (direction == VOLUME_DOWN) {
                        audio.adjustStreamVolume(STREAM_TYPE,
                                AudioManager.ADJUST_LOWER, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
                    }
                    setCurrentVolume(audio.getStreamVolume(STREAM_TYPE));
                }
            };
        }
    
        // Call when control needed, add a call to constructor if needed immediately
        public void setActive(boolean active) {
            if (mMediaSession != null) {
                mMediaSession.setActive(active);
                return;
            }
            createMediaSession();
        }
    
        // Call from Service's onDestroy method
        public void destroy() {
            if (mMediaSession != null) {
                mMediaSession.release();
            }
        }
    }