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

时间:2012-04-14 13:44:41

标签: android volume listen

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

10 个答案:

答案 0 :(得分:29)

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

public class SettingsContentObserver extends ContentObserver {
    int previousVolume;
    Context context;

    public SettingsContentObserver(Context c, Handler handler) {
        super(handler);
        context=c;

        AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
    }

    @Override
    public boolean deliverSelfNotifications() {
        return super.deliverSelfNotifications();
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);

        int delta=previousVolume-currentVolume;

        if(delta>0)
        {
            Logger.d("Ściszył!"); // volume decreased.
            previousVolume=currentVolume;
        }
        else if(delta<0)
        {
            Logger.d("Zrobił głośniej!"); // volume increased.
            previousVolume=currentVolume;
        }
    }
}

然后在您的服务onCreate中注册:

mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );

然后在onDestroy中取消注册:

getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);

请注意,此示例通过更改媒体音量来判断,如果要使用其他音量,请更改它!

更新:

上面的方法据说不适用于Marshmallow,但是自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);

即使屏幕关闭,也会以某种方式检测音量按钮按下(如果适用于您的平台,请务必注册正确的媒体按钮意图接收器!)

更新2,因为GalDude要求获取有关获取媒体MediaSession / MediaController的更多信息。对不起,但是因为我停止使用Java,它将在Kotlin:

lateinit var mediaSession: MediaSessionCompat // you have to initialize it in your onCreate method
val kontroler: MediaControllerCompat
 get() = mediaSession.controller // in Java it's just getController() on mediaSession

// in your onCreate/start method:
mediaSession = MediaSessionCompat(this, "YourPlayerName", receiver, null)
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession.isActive = true
if (ratingIsWorking) // note: rating crashes on some machines you have to check it!
    mediaSession.setRatingType(RatingCompat.RATING_5_STARS)

mediaSession.setCallback(object : MediaSessionCompat.Callback() {
...
// here you have to implement what happens with your player when play/pause/stop/ffw etc. is requested - see exaples elsewhere
})

// onDestroy/exit method:
mediaSession.isActive = false
mediaSession.release()

答案 1 :(得分:19)

AOSP音乐应用程序有一个服务(MediaPlaybackService),通过注册BroadcastReceiver(MediaButtonIntentReceiver)来响应音量键事件。

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

    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>

即使音乐应用不在前台,这仍然有效。这不是你想要的吗?

答案 2 :(得分:10)

根据关于这个主题的其他几个问题判断,没有。

Other question 1Other question 2

服务根本不接收KeyEvent回调。

答案 3 :(得分:6)

我能够使用MediaSession使其在Android 5+设备上运行。但是,@ suukk建议的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>

在您的活动中:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    startService(new Intent(this, PlayerService.class));
}

有几点需要注意:

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

答案 4 :(得分:4)

您需要播放服务中的空白声音,然后才能收听音量变化。以下为我工作

步骤

1。将blank.mp3放入原始文件夹(从here下载)

2。在onStartCommand()

启动媒体
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;
}

3。您必须选择停止并释放媒体播放器。最好在onDestroy()

中这样做
@Override
public void onDestroy() {

    mediaPlayer.stop();
    mediaPlayer.release();

    super.onDestroy();
}

4。创建将侦听音量变化的广播接收器

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;
        }
    }
};

5。在onStartCommand()

中注册广播接收器
@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;
}

6。在onDestroy()

中取消注册广播接收器
 @Override
public void onDestroy() {
    .....

    unregisterReceiver(broadcastReceiver);

    .....

    super.onDestroy();
}

这就是

答案 5 :(得分:1)

不幸的是,这是Android的另一个领域,其中有五种不同的方法可以“解决问题”,但大多数方法效果都不理想。为了我自己的理智,我将尝试在下面列出所有不同的方法。

解决方案

1)MediaSession(来自服务)

Denis Kniazhev的答案:https://stackoverflow.com/a/43304591/2441655

  

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

2)android.media.VOLUME_CHANGED_ACTION(来自服务)

Nikhil的回答:https://stackoverflow.com/a/44040282/2441655

  

缺点:
  1.不是SDK的正式组成部分:https://stackoverflow.com/a/8974510/2441655
  2.忽略音量键的第一次按下(因为它只显示音量条)。
  3.当音量为100%时忽略音量增大键,而音量为0%时忽略音量减小键。

3)ContentObserver(来自服务)

ssuukk的答案:https://stackoverflow.com/a/15292255/2441655(第一部分)

  

缺点:
  1.在新版本的Android中不起作用:comment by dsemi
  2.忽略音量键的第一次按下(因为它只显示音量条)。
  3.当音量为100%时忽略音量增大键,而音量为0%时忽略音量减小键。

4)AudioManager.registerMediaButtonEventReceiver(来自服务)

乔的回答:https://stackoverflow.com/a/11510564/2441655

  

缺点:
  1.不适用于大多数rom:comment by elgui

5)onKeyDown(来自活动)

dipali的回答:https://stackoverflow.com/a/21086563/2441655

  

缺点:
  1.如果屏幕关闭,在其他应用程序中等,则不起作用。

6)dispatchKeyEvent(来自活动)

莫里斯·加文的答案:https://stackoverflow.com/a/11462962/2441655

  

缺点:
  1.如果屏幕关闭,在其他应用程序中等,则不起作用。

结论

我当前正在使用的解决方案是#1,因为:

  1. 这是SDK的正式组成部分。
  2. 可以从服务使用。 (即,无论您使用的是哪个应用程序)
  3. 它捕获每一次音量键的按下,而与当前音量/ ui状态无关。
  4. 在屏幕关闭时可以使用。

让我知道您是否找到其他任何一个,或者是否发现其中一些更多的缺点!

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

根据需要回调的上下文,可能会提供替代解决方案。

为了能够检测音量按钮,Activity需要覆盖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);
    }

答案 8 :(得分:0)

checkout Controlling Your App’s Volume and Playback ...这将有助于解决您的问题...多个应用程序可能想要从后台监听按钮按下,这可能是KeyEvents只能由活动处理的原因用户按键的界面。

答案 9 :(得分:0)

这需要Lollipop(v5.0 / API 21)或更高版本

我的目标是从服务中调整系统音量。不过可以采取任何行动。

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();
        }
    }
}