Android播放音乐时,在设置中调节铃声大小或者设置铃声的时候提示音与音乐一起播放

最开始遇到这个问题的思路是在选择音乐时发送一条广播给music然后让它暂停音乐

代码如下:

public class MainActivity extends AppCompatActivity {
    private static boolean flagMusic;
    private AudioManager audioManager = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        boolean vIsActive = audioManager.isMusicActive();
        if (vIsActive) {
            Toast.makeText(getApplicationContext(), "有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = true;//记录音乐播放状态 true为正在播放 ,反之
            pauseMusic();//暂停音乐播放
        } else {
            Toast.makeText(getApplicationContext(), "没有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = false;
        }
    }

    private void pauseMusic() {
        Intent freshIntent = new Intent();
        freshIntent.setAction("com.android.music.musicservicecommand.pause");
        freshIntent.putExtra("command", "pause");
        sendBroadcast(freshIntent);
    }

    private void continuMusic() {
        Intent freshIntent = new Intent();
        freshIntent.setAction("com.android.music.musicservicecommand.togglepause");
        freshIntent.putExtra("command", "togglepause");
        sendBroadcast(freshIntent);
    }

    @Override
    protected void onStart() {
        super.onStart();
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        boolean vIsActive = audioManager.isMusicActive();
        if (vIsActive) {
            Toast.makeText(getApplicationContext(), "有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = true;//记录音乐播放状态 true为正在播放 ,反之
            pauseMusic();//暂停音乐播放
        } else {
            Toast.makeText(getApplicationContext(), "没有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = false;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        boolean vIsActive = audioManager.isMusicActive();
        if (!vIsActive) {
            Toast.makeText(getApplicationContext(), "有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = true;//记录音乐播放状态 true为正在播放 ,反之
            continuMusic();//暂停音乐播放
        } else {
            Toast.makeText(getApplicationContext(), "没有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        boolean vIsActive = audioManager.isMusicActive();
        if (!vIsActive) {
            Toast.makeText(getApplicationContext(), "有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = true;//记录音乐播放状态 true为正在播放 ,反之
            continuMusic();//暂停音乐播放
        } else {
            Toast.makeText(getApplicationContext(), "没有音乐在播放", Toast.LENGTH_SHORT).show();
            flagMusic = false;
        }
    }
}

但是这个只针对自带音乐app生效 所以换了个思路

在设置调节音量的时候获取音频焦点 调节结束的时候松开之后释放音频焦点
这样在调节的时候就可以暂停音乐 松开之后音乐自动就开始播放了

首先在源码中找到对应的类:
/frameworks/base/core/java/android/preference/SeekBarVolumizer.java
类应是调节音量的SeekBar
首先来看Seekar的三个回调函数:

@Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        mHandler.sendEmptyMessageDelayed(1,500);
        //开始调节SeekBar的时候会调用这里
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    //调节SeekBar结束的时候会调用这里
    }

我们来看一下AOSP中的源码:

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
        if (fromTouch) {
            postSetVolume(progress);
        }
        if (mCallback != null) {
            mCallback.onProgressChanged(seekBar, progress, fromTouch);
        }
    }

    private void postSetVolume(int progress) {
        if (mHandler == null) return;
        // Do the volume changing separately to give responsive UI
        mLastProgress = progress;
        mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
        mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
    }

    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    public void onStopTrackingTouch(SeekBar seekBar) {
        postStartSample();//松开SeekBar的时候播放提示音
    }

方法其实很简单:
在onStartTrackingTouch()的时候获取到音频焦点
在onStopTrackingTouch()时释放焦点
这样在滑动SeekBar的时候后台的音乐会暂停
松开SeekBar的时候音乐就继续播放了,代码如下:

public void onStartTrackingTouch(SeekBar seekBar) {
        mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    }

    public void onStopTrackingTouch(SeekBar seekBar) {
        mAudioManager.abandonAudioFocus(mAudioFocusListener);
        postStartSample();
        Log.e("SeekBarVolumizer","Start to play cue music");
    }
    
    private AudioManager.OnAudioFocusChangeListener mAudioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            Log.d(TAG, "onAudioFocusChange:" + focusChange );
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_LOSS:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    break;
            }
        }
    };

但是 当你编译好之后会发现
松开SeekBar的时候 后台的音乐会和调节音量后的提示音一起播放了
原因是因为在系统播放提示音的时候没有去requestAudioFocus()
而直接去播放了提示音
顺着postStartSample();往下找会发现:

private void postStartSample() {
        if (mHandler == null) return;
        mHandler.removeMessages(MSG_START_SAMPLE);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
                isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
    }

发送了一条MSG_START_SAMPLE,最后执行的代码如下:

private void onStartSample() {
        if (!isSamplePlaying()) {
            if (mCallback != null) {
                mCallback.onSampleStarting(this);
            }

            synchronized (this) {
                if (mRingtone != null) {
                    try {
                        mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
                                .getAudioAttributes())
                                .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
                                .build());
                        mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);        //获得音频焦点
                        mRingtone.play();  //播放提示音
                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_INIT_WAITING),1000); //延迟1秒钟之后释放焦点
                    } catch (Throwable e) {
                        Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
                    }
                }
            }
        }
    }

handleMessage()如下:

@Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_SET_STREAM_VOLUME:
                if (mMuted && mLastProgress > 0) {
                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
                } else if (!mMuted && mLastProgress == 0) {
                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
                }
                mAudioManager.setStreamVolume(mStreamType, mLastProgress,
                        AudioManager.FLAG_SHOW_UI_WARNINGS);
                break;
            case MSG_START_SAMPLE:
                onStartSample();
                break;
            case MSG_STOP_SAMPLE:
                onStopSample();
                break;
            case MSG_INIT_SAMPLE:
                onInitSample();
                break;
                case MSG_INIT_WAITING:
                mAudioManager.abandonAudioFocus(mAudioFocusListener);
                break;
            default:
                Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
        }
        return true;
    }

这样滑动SeekBar的时候音乐会暂停下来 松开SeekBar之后播放提示音 一秒之后音乐就会继续播放啦
(≧▽≦)