Android 听后台服务中的音量按钮?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10154118/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Listen to volume buttons in background service?
提问by
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?
我知道如何在活动中收听音量按钮。但是我可以在后台服务中做到这一点吗?如果是,如何做到这一点?
采纳答案by Bryan Herbst
Judging by the couple of other questions about this topic, no.
从关于这个话题的其他几个问题来看,没有。
Other question 1, Other question 2
Services simply do not receive KeyEvent callbacks.
服务根本不接收 KeyEvent 回调。
回答by ssuukk
It is possible. Use code below (for newer Android versions, especially Marshmallow, see bottom of the answer):
有可能的。使用下面的代码(对于较新的 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;
}
}
}
Then in your service onCreate register it with:
然后在您的服务 onCreate 中注册它:
mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
Then unregister in onDestroy:
然后在 onDestroy 中注销:
getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
Note that this example judges by change of media volume, if you want to use other volume, change it!
注意这个例子是通过媒体音量的变化来判断的,如果你想使用其他音量,改变它!
UPDATE:
更新:
Above method supposedly doesn't work on Marshmallow, BUT there's much better way now since MediaSession was introduced! So first you have to migrate your code to MediaController/MediaSession pattern and then use this code:
上面的方法据说不适用于棉花糖,但是自从引入 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);
Somehow volume button presses are detected even with screen off (just be sure to register proper media button intent receiver if applicable for your platform!)
即使屏幕关闭,也会以某种方式检测到音量按钮按下(如果适用于您的平台,请确保注册正确的媒体按钮意图接收器!)
UPDATE 2 since GalDude requested some more info on getting media MediaSession/MediaController. Sorry, but since I stopped using Java it will be in Kotlin:
更新 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()
回答by Joe
The AOSP Music app has a Service (MediaPlaybackService) that responds to volume key events by registering a BroadcastReceiver (MediaButtonIntentReceiver).
AOSP 音乐应用有一个服务 ( MediaPlaybackService),它通过注册一个 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?
即使音乐应用程序不在前台,这也有效。这不是你想要的吗?
回答by Denis Kniazhev
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+.
我能够使用MediaSession
. 但是,ContentObserver
@ssuukk 的建议在 4.4 和 7.0 设备上都不适用于我(至少在我一直在测试的 ROM 上)。这是一个适用于 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
:
在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 completelyso 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.
- 它完全拦截音量按钮,因此当此代码运行时,您将无法使用音量按钮调整铃声音量。这可能可以解决,我只是没有尝试。
- 如果您按原样运行示例,即使屏幕关闭并且应用程序已从“最近的应用程序”列表中删除,音量按钮仍由应用程序控制。您必须转到
Settings->Applications
,找到该应用程序并强制停止它以恢复音量按钮。
回答by Venryx
Unfortunately, this is another area of Android where there are like five different ways to "solve the problem", but most of them don't work very well. For my own sanity, I'll attempt to list all the different approaches below.
不幸的是,这是 Android 的另一个领域,其中有五种不同的方法可以“解决问题”,但大多数都不能很好地工作。为了我自己的理智,我将尝试在下面列出所有不同的方法。
Solutions
解决方案
1) MediaSession
(from Service)
1)MediaSession
(来自服务)
Answer by Denis Kniazhev: https://stackoverflow.com/a/43304591/2441655
Denis Kniazhev 的回答:https://stackoverflow.com/a/43304591/2441655
Drawbacks:
1. Requires Android SDK 21+ (Android 9+).
缺点:
1. 需要 Android SDK 21+ (Android 9+)。
2) android.media.VOLUME_CHANGED_ACTION
(from Service)
2)android.media.VOLUME_CHANGED_ACTION
(来自服务)
Answer by Nikhil: https://stackoverflow.com/a/44040282/2441655
Nikhil 的回答:https: //stackoverflow.com/a/44040282/2441655
Drawbacks:
1. Not an official part of the SDK: https://stackoverflow.com/a/8974510/2441655
2. Ignores first-press of volume-key (since it only shows the volume-bar).
3. Ignores volume-up key when at 100%, and volume-down key when at 0%.
缺点:
1. 不是 SDK 的官方部分:https: //stackoverflow.com/a/8974510/2441655
2. 忽略音量键的第一次按下(因为它只显示音量条)。
3. 100% 时忽略音量增大键,0% 时忽略音量减小键。
3) ContentObserver
(from Service)
3)ContentObserver
(来自服务)
Answer by ssuukk: https://stackoverflow.com/a/15292255/2441655(first part)
ssuukk 的回答:https://stackoverflow.com/a/15292255/2441655 (第一部分)
Drawbacks:
1. Doesn't work in newer versions of Android: comment by dsemi
2. Ignores first-press of volume-key (since it only shows the volume-bar).
3. Ignores volume-up key when at 100%, and volume-down key when at 0%.
缺点:
1. 在较新版本的 Android 中不起作用:dsemi 的评论
2. 忽略音量键的第一次按下(因为它只显示音量条)。
3. 100% 时忽略音量增大键,0% 时忽略音量减小键。
4) AudioManager.registerMediaButtonEventReceiver
(from Service)
4)AudioManager.registerMediaButtonEventReceiver
(来自服务)
Answer by Joe: https://stackoverflow.com/a/11510564/2441655
乔的回答:https: //stackoverflow.com/a/11510564/2441655
Drawbacks:
1. Doesn't work on most roms: comment by elgui
5) onKeyDown
(from Activity)
5)onKeyDown
(来自活动)
Answer by dipali: https://stackoverflow.com/a/21086563/2441655
dipali 的回答:https://stackoverflow.com/a/21086563/2441655
Drawbacks:
1. Doesn't work if screen is off, in different app, etc.
缺点:
1. 如果屏幕关闭,在不同的应用程序中等,则不起作用。
6) dispatchKeyEvent
(from Activity)
6)dispatchKeyEvent
(来自活动)
Answer by Maurice Gavin: https://stackoverflow.com/a/11462962/2441655
莫里斯·加文的回答:https: //stackoverflow.com/a/11462962/2441655
Drawbacks:
1. Doesn't work if screen is off, in different app, etc.
缺点:
1. 如果屏幕关闭,在不同的应用程序中等,则不起作用。
Conclusion
结论
The solution I'm currently using is #1, because:
我目前使用的解决方案是#1,因为:
- It's an official part of the SDK.
- It is usable from a service. (ie. regardless of what app you're in)
- It captures every volume-key press, regardless of current-volume/ui-state.
- It works when the screen is off.
- 它是 SDK 的官方部分。
- 从服务可以使用。(即。不管你在什么应用程序中)
- 无论当前音量/用户界面状态如何,它都会捕获每次按下音量键。
- 它在屏幕关闭时工作。
Let me know if you find any others -- or if you've found more drawbacks to some of them!
如果您发现任何其他人,或者您发现其中一些人的更多缺点,请告诉我!
回答by bikram
You need to play blank sound from service then only you can listen to volume changes. Following worked for me
您需要从服务中播放空白声音,然后才能听到音量变化。以下为我工作
Steps
脚步
1. Put blank.mp3 in raw folder (Download from here)
1. 将 blank.mp3 放入 raw 文件夹(从这里下载)
2. Start media at onStartCommand()
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. You must choose to stop and release mediaplayer. It's better to do so in onDestroy()
3. 您必须选择停止和释放媒体播放器。最好在 onDestroy() 中这样做
@Override
public void onDestroy() {
mediaPlayer.stop();
mediaPlayer.release();
super.onDestroy();
}
4. Create Broadcast receiver that will listen for volume changes
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. Register the broadcast receiver in onStartCommand()
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. Unregister broadccast receiver in onDestroy()
6. 在 onDestroy() 中注销广播接收器
@Override
public void onDestroy() {
.....
unregisterReceiver(broadcastReceiver);
.....
super.onDestroy();
}
That's all
就这样
回答by Gibolt
This requires Lollipop (v5.0/API 21) or higher
这需要 Lollipop (v5.0/API 21) 或更高版本
My goal was to adjust system volume from a Service. Any action can be taken on press though.
我的目标是从服务调整系统音量。不过,媒体可以采取任何行动。
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();
}
}
}
回答by Ashish
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.
checkout Controlling Your App's Volume and Playback...这将有助于解决您的问题...多个应用程序可能想要从后台监听按钮按下,这可能是 KeyEvents 只能由 Activity 处理的原因,因为它们是界面给用户按键。
回答by Maurice Gavin
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.
为了能够检测音量按钮,Activity 需要覆盖该dispatchKeyEvent
功能。为了在多个活动中出现这个,可以编写一个包含被所有后续活动扩展的覆盖函数的超类。
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);
}
回答by Lincoln Hawk
Android doesn't document APIs on interacting with volume buttons in that case. So I guess the answer is no…
在这种情况下,Android 不会记录与音量按钮交互的 API。所以我想答案是否定的……