要开发一个 Android 音乐播放器,相信各位 Android 开发者首先想到的是 MediaSession 框架。但 MediaSession 框架仅对基础功能进行了封装,要使用它开发一个音乐播放器,开发者还需要编写大量的代码。例如,响应媒体按钮、请求和管理音频焦点、在输出设备改变时暂停播放。此外,由于音乐播放器需要在后台运行,因此还得维护一个 Notification,以便在后台运行时,用户可以通过这个 Notification 对播放器进行控制。如果你的播放器还需要支持播放列表、线控播放、音质切换、睡眠定时器等一系列功能,那么接下来就有的忙了。不过不用担心, snow 框架已经为您做好了其中的大部分工作。

snow 框架简介

snow 是一个 Android 音乐播放器框架,基于 MediaSession 框架构建,并提供了对 MediaSession 框架的兼容 snow 框架已经为开发者处理好了大部分繁琐的事情,例如,响应媒体按钮、请求和管理音频焦点、在输出设备改变时暂停播放、播放列表,线控播放等...

仓库地址 https://github.com/jrfeng/snow

支持的功能:

  • 自定义播放器(MediaPlayer, ExoPlayer)
  • 自定义通知栏控制器
  • 自定义音频特效引擎
  • 仅 WiFi 网络播放
  • 切换音质/动态 URL
  • 睡眠定时器
  • 记录播放历史
  • 播放器状态持久化
  • 将以下代码添加到项目根目录中的 build.gradle 文件中:
  • allprojects {
        repositories {
            maven { url 'https://jitpack.io' }
    
  • 将以下代码添加到模块的依赖中:
  • dependencies {
        implementation 'com.github.jrfeng.snow:player:1.0.9'
    
  • 申请权限:
  • <!-- 用于启动前台 Service -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <!-- 用于后台播放 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- 用于播放本地音乐 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <!-- 用于播放网络音乐 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    

    注意!从 Android 6.0(API Level 23) 开始,需要在运行时动态申请存储器访问权限:"android.permission.READ_EXTERNAL_STORAGE"

  • 创建 PlayerService
  • 创建一个类并让其继承 snow.player.PlayerService 类,并且使用 @PersistenceId 注解对其进行标注。你不需要重写这个类的任何方法

    @PersistenId("MyPlayerService")
    public MyPlayerService extends PlayerService {
    

    @PersistenceId 注解用于为当前 PlayerService 设置一个持久化 ID(请保证该 ID 值的唯一性),该 ID 将用于 PlayerService 状态的持久化。如果你没有使用 @PersistenceId 注解设置持久化 ID,则持久化 ID 默认为你的 PlayerService 的完整类名(例如 snow.demo.MyPlayerService)。建议为你的 PlayerService 设置一个持久化 ID,这样即使重命名类也不会导致状态丢失。

  • 注册 PlayerService
  • <service android:name="snow.demo.MyPlayerService">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    <receiver android:name="androidx.media.session.MediaButtonReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>
    
  • 连接到 PlayerService
  • // 创建一个 PlayerClient 对象
    PlayerClient playerClient = PlayerClient.newInstance(context, MyPlayerService.class);
    // 连接到 PlayerService
    playerClient.connect(new PlayerClient.OnConnectCallback() {
        @Override
        public void onConnected(boolean success) {
            // DEBUG
            Log.d("App", "connect: " + success);
    
  • 创建一个播放列表
  • private Playlist createPlaylist() {
        MusicItem song1 = new MusicItem.Builder()
                .setTitle("太阳照常升起")
                .setArtist("久石让")
                .setAlbum("太阳照常升起 电影原声大碟")
                .setDuration(224013)
                .setUri("http://music.163.com/song/media/outer/url?id=441722")
                .setIconUri("http://p2.music.126.net/drqGdK7zgW7B7IFl4lWpoQ==/109951163369835547.jpg")
                .build();
        MusicItem song2 = new MusicItem.Builder()
                .setTitle("钢铁洪流进行曲")
                .setArtist("李旭昊")
                .setAlbum("国庆70周年阅兵BGM")
                .setDuration(186154)
                .setUri("http://music.163.com/song/media/outer/url?id=1394369908")
                .setIconUri("http://p2.music.126.net/KnC_YJjnRTNvCF82_2leCg==/109951164930615683.jpg")
                .build();
        MusicItem song3 = new MusicItem.Builder()
                .setTitle("国际歌-钢琴")
                .setArtist("曹伟健")
                .setAlbum("音迹")
                .setDuration(141369)
                .setUri("http://music.163.com/song/media/outer/url?id=1857796913")
                .build();
        MusicItem song4 = new MusicItem.Builder()
                .setTitle("我爱你中国[Forbid Seek]")
                .setDuration(136000)
                // cross-protocol redirects
                .setUri("https://music.163.com/song/media/outer/url?id=174451")
                .setIconUri("http://p2.music.126.net/x6pVwc6ysKZ9S01jYlYiAw==/97856534887060.jpg")
                // forbid seek
                .setForbidSeek(true)
                .build();
        return new Playlist.Builder()
                .append(song1)
                .append(song2)
                .append(song3)
                .append(song4)
                .build();
    
  • 设置播放列表并播放音乐
  • // 创建播放列表
    Playlist playlist = createPlaylist();
    // 设置播放列表,并播放音乐
    playerClient.setPlaylist(playlist, true);
    

    为了演示 snow 框架的部分功能,我们特意开发了一个示例程序,如下图所示(网盘下载,提取码:7mye):

    提示:示例程序是一个本地音乐播放器,可以在项目的 app 模块中找到该示例程序的源代码。

    如果您对 snow 框架感兴趣,并且希望深入了解,可以查看项目的 Wiki:

  • 自定义 PlayerService
  • 自定义 Notification
  • 使用 ExoPlayer
  • EqualizerActivity
  • jrfeng android
    私信