Listen to volume buttons in background service?

2019-01-01 05:19发布

I know how to listen to volume buttons in an activity. But can I do that in a background service? If yes, how to do that?

9条回答
路过你的时光
2楼-- · 2019-01-01 05:53

checkout Controlling Your App’s Volume and Playback ...This will help to solve your problem... multiple applications might want to listen for button presses from background, this may be the reason why KeyEvents can only be handled by Activities as they are the interface to the user pressing the keys.

查看更多
萌妹纸的霸气范
3楼-- · 2019-01-01 05:55

I was able to make it work on android 5+ devices using MediaSession. However,ContentObserver suggested by @ssuukk didn't work for me on both 4.4 and 7.0 devices (at least on ROMs that I've been testing on). Here is a full example which works on android 5+.

Service:

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

In AndroidManifest.xml:

<application ...>
    ...
    <service android:name=".PlayerService"/>
</application>

In your activity:

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

There are several things to be aware of:

  • It intercepts volume buttons completely so while this code is running you won't be able to adjust ring volume using volume buttons. This might be possible to fix, I just didn't try.
  • If you run the example as-is the volume buttons will remain controlled by the app even when the screen is off and the app has been removed from "Recent Apps" list. You'll have to go to Settings->Applications, find the app and force stop it to get volume buttons back.
查看更多
与风俱净
4楼-- · 2019-01-01 05:57

Note: this only works for Activities, and not Services as the question states.

Depending on the context in which the callback is required an alternative solution might be available.

To be capable of detecting the volume button an Activity would need to override the dispatchKeyEvent function. For this to be present in multiple activities could could write a superclass containing the overridden function which is extended by all subsequent activities.

Here is the code for detecting Volume Up/Down key presses:

    // 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);
    }
查看更多
何处买醉
5楼-- · 2019-01-01 06:02

Android doesn't document APIs on interacting with volume buttons in that case. So I guess the answer is no…

查看更多
君临天下
6楼-- · 2019-01-01 06:09

The AOSP Music app has a Service (MediaPlaybackService) that responds to volume key events by registering a BroadcastReceiver (MediaButtonIntentReceiver).

Here's the code snippet where it registers the receiver:

    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    ComponentName rec = new ComponentName(getPackageName(),
            MediaButtonIntentReceiver.class.getName());
    mAudioManager.registerMediaButtonEventReceiver(rec);

Also, don't forget about manifest:

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

This works even if the Music app is not in the foreground. Isn't that what you want?

查看更多
冷夜・残月
7楼-- · 2019-01-01 06:12

Judging by the couple of other questions about this topic, no.

Other question 1, Other question 2

Services simply do not receive KeyEvent callbacks.

查看更多
登录 后发表回答