Android如何判断当前手机是否正在播放音乐,并获取到正在播放的音乐的信息

Stella981
• 阅读 763

我想实现如下的场景,判断当前Android手机上是否正在播放音乐,如果是,通过某个特定的手势,

或者点击某个按键,将当前我正在听的音乐共享出去。

第一步,就是判断当前是否有音乐正在播放。

最开始我想得有点复杂,以为要深入framework或更下层去做手脚才行,找了一下资料,发现AudioManager对外暴露了接口。

  isMusicActive()  

通过这个接口就可以判断当前系统是否有音乐在播放了。

还有一个问题,如果我想在音乐一开始就已经播放的时候,就知道这个事件,以便进行特殊的处理。

再进一步看一下 AudioManager 的源码,发现其中有如下方法:

  public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)

从字面意思来看:请求音频焦点,再看这个函数的返回值:

/** 
     * A failed focus change request. 
     */  
    public static final int AUDIOFOCUS_REQUEST_FAILED = 0;  
    /** 
     * A successful focus change request. 
     */  
    public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;

Managing Audio Focus

管理音频焦点

多个应用都在播放音频的可能性,所以考虑应用间如何交互非常重要。为避免每个音乐应用同时播放,Android使用音频焦点来协调音频的播放----只有获取到音频焦点的应用可以播放音频。

在你的应用开始播放音频之前,它应该先请求--并接收音频焦点。同样,它也应该知道当监听到失去音频焦点后如何合理地进行响应。

沿着这个路应该是对的,写了下面的测试代码进行验证。这个主要是Service的实现,你还需要实现一个Activity去启动Service、结束Service:

public class MainService extends Service {
    private static final String TAG = "MainService";

    private MediaPlayer player;

    private AudioManager mAm;

    private MyOnAudioFocusChangeListener mListener;

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate");

        player = MediaPlayer.create(this, R.raw.test); // 在res目录下新建raw目录,复制一个test.mp3文件到此目录下。
        player.setLooping(false);

        mAm = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
        mListener = new MyOnAudioFocusChangeListener();
    }

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

    @Override
    public void onStart(Intent intent, int startid) {
        Toast.makeText(this, "My Service Start", Toast.LENGTH_LONG).show();
        Log.i(TAG, "onStart");

        // Request audio focus for playback
        int result = mAm.requestAudioFocus(mListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            Log.i(TAG, "requestAudioFocus successfully.");

            // Start playback.
            player.start();
        } else {
            Log.e(TAG, "requestAudioFocus failed.");
        }
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "My Service Stoped", Toast.LENGTH_LONG).show();
        Log.i(TAG, "onDestroy");
        player.stop();

        mAm.abandonAudioFocus(mListener);
    }

    private class MyOnAudioFocusChangeListener implements OnAudioFocusChangeListener {
        @Override
        public void onAudioFocusChange(int focusChange) {
            Log.i(TAG, "focusChange=" + focusChange);
        }
    }
}

和 天天动听 结合起来测试,先打开天天动听播放音乐,再启动这个Service,发现天天动听自动暂停,再停止这个Service,天天动听又开始播放了。
反过来,我先启动这个Service,再播放、暂停天天动听,“Log.i(TAG, "focusChange=" + focusChange);” 这个确实有输出日志。
主流的音乐播放器,都遵循此规则的,所以通过使用Android的这个机制,我们就可以监控音乐的播放了。
还有一个问题,如何知道当前播放的音乐信息呢?两个思路:
1、通过在后台自动截取音频流的输出,通过服务器进行听歌识曲;
2、通过在SystemUI中拦截主流音乐播放器的通知;
第1个思路,从原理上是可行的,但是实现起来难度比较大,而且严重依赖网络;
还是先来分析一下第2个思路。
先找主流的Android音乐播放器来做个简单地测试,比如:天天动听、QQ音乐、酷狗音乐、酷我音乐、百度音乐等,在播放过程中,都会向状态栏中发一个Notification消息,其中已经包含歌曲信息。那我只需要做一个特殊的拦截并进行包名匹配,就可以获取正在播放的音乐了。
具体思路如下:
1、实现一个服务,这个服务在Android手机启动时,自动运行起来,通过 AudioManager.requestAudioFocus() 获取音频焦点,但什么事都不干,只为有其它音乐播放器开始运行时,得到一个通知消息;
2、修改SystemUI,当主流音乐播放器发Notification到状态栏时,从中获取到音乐信息;
3、步骤1的Listener就可以集成到SystemUI中,这样当音乐焦点被其它音乐播放器抢走后,再结合最近收到的Notification通知,这样更准确一些;
这样,基本上就可以实现我们想要的场景了。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
java中比较两个时间的差值
项目背景1.某篇文稿的发布时间是publishDate,例如:2020072118:00:41。2.现要求判断该篇文稿的发布时间是否在近30天之内。publicstaticlongdayDiff(DatecurrentDate,DatepublishDate){LongcurrentTimecurrentDat
Wesley13 Wesley13
2年前
java 之 音乐播放代码
//需求:通过代码播放音乐//1.读取文件//2.将音乐文件放到播放代码中//3.播放publicstaticvoidmain(Stringargs)throwsMalformedURLException{ScannerinputnewScanner(System
Stella981 Stella981
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这