Android开源音乐播放器之播放器基本功能

2017/6/16 16:40:01 人评论 次浏览 分类:TextView

系列文章

  • Android开源在线音乐播放器——波尼音乐
  • Android开源音乐播放器之播放器基本功能
  • Android开源音乐播放器之高仿云音乐黑胶唱片
  • Android开源音乐播放器之自动滚动歌词
  • Android开源音乐播放器之在线音乐列表自动加载更多

前言

音乐播放器是我们最常用的应用之一,也是每部手机都会预装的应用。作为一个合格的音乐播放器,应该具有哪些功能呢?“无非是播放、暂停、切换歌曲、进度调节、切换播放模式、专辑封面显示、歌词显示、歌曲列表、歌曲管理(由于国产手机大多都是修改过的Android系统,因此系统自带播放器功能也不一样,这里以Android原生播放器为参考)这些功能” 一开始我也是这么认为的,但当我着手做的时候,才发现这些功能远远不够。如手机来电时,音乐需要自动暂停播放,耳机拔出时,同样需要暂停,还要支持耳机线控,等等,这些都是需要我们考虑的。

一个合格的音乐播放器应该具有哪些基本素质?

由于播放、暂停、切换歌曲、进度调节等这些功能过于简单,因此不过多讨论,这里只讨论一些容易被忽略的功能。

扫描本地音乐

扫描歌曲是播放器的基本功能,一般通过ContentProvider配合Media相关类查询系统数据库,获得媒体库中的歌曲信息。

/**
 * 扫描歌曲
 */
public static void scanMusic(Context context, List<Music> musicList) {
    musicList.clear();
    Cursor cursor = context.getContentResolver().query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
            MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    if (cursor == null) {
        return;
    }
    while (cursor.moveToNext()) {
        // 是否为音乐
        int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));
        if (isMusic == 0) {
            continue;
        }
        long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID));
        // 标题
        String title = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));
        // 艺术家
        String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
        // 专辑
        String album = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));
        // 持续时间
        long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
        // 音乐uri
        String uri = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
        // 专辑封面id,根据该id可以获得专辑图片uri
        long albumId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
        String coverUri = getCoverUri(context, albumId);
        // 音乐文件名
        String fileName = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));
        // 音乐文件大小
        long fileSize = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
        // 发行时间
        String year = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.YEAR)));
        Music music = new Music();
        music.set...
        musicList.add(music);
    }
    cursor.close();
}

/**
 * 查询专辑封面图片uri
 */
private static String getCoverUri(Context context, long albumId) {
    String uri = null;
    Cursor cursor = context.getContentResolver().query(
            Uri.parse("content://media/external/audio/albums/" + albumId),
            new String[]{"album_art"}, null, null, null);
    if (cursor != null) {
        cursor.moveToNext();
        uri = cursor.getString(0);
        cursor.close();
    }
    CoverLoader.getInstance().loadThumbnail(uri);
    return uri;
}

通过以上方法基本可以获得音乐的所有信息,弊端是依赖于Android系统媒体库,有时新增音乐后没有通知系统扫描,就无法获得该音乐的信息,不够灵活。

避免播放器内存被系统回收

我们都知道Android系统有自动回收内存机制,如果系统内存紧张,就会触发该机制,应用就有可能被回收,不过Android提供了前台机制,保证内存不足时也不会回收该应用。

public class PlayService extends Service {
    // ...

    /**
     * 启动前台机制,播放时调用
     */
    private void updateNotification(Music music) {
        mNotificationManager.cancel(NOTIFICATION_ID);
        startForeground(NOTIFICATION_ID, SystemUtils.createNotification(this, music));
    }

    /**
     * 取消前台机制,暂停时调用
     */
    private void cancelNotification(Music music) {
        stopForeground(true);
        mNotificationManager.notify(NOTIFICATION_ID, SystemUtils.createNotification(this, music));
    }

    // ...
}

来电时暂停播放

“纳尼!难道来电时不是自动暂停吗?” “图样图森破!”

private IntentFilter mNoisyFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

public class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent serviceIntent = new Intent(context, PlayService.class);
        serviceIntent.setAction(Actions.ACTION_MEDIA_PLAY_PAUSE);
        context.startService(serviceIntent);
    }
}

播放时注册广播接收器,暂停时取消注册即可。

捕获/丢弃音乐焦点

大家可能不懂这个标题是什么意思,别着急,让我细细道来。大家有没有试过,如果手机上安装了两个音乐播放器,当一个正在播放的时候,打开第二个播放歌曲,有没有发现第一个自动暂停了?“纳尼!难道不是自动暂停?” “图样……”

public class PlayService extends Service implements AudioManager.OnAudioFocusChangeListener {
    // ...

    private void start() {
        mPlayer.start();
        isPause = false;
        mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    }

    public int pause() {
        if (!isPlaying()) {
            return -1;
        }
        mPlayer.pause();
        mAudioManager.abandonAudioFocus(this);
        return mPlayingPosition;
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_LOSS:
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                if (isPlaying()) {
                    pause();
                }
                break;
        }
    }

    // ...
}

播放时请求焦点,如果请求成功,其他播放器将失去焦点,暂停播放,暂停时丢弃焦点。

耳机线控

“纳尼……” “Shut up !” 是的,需要我们自己控制。

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
        if (event == null || event.getAction() != KeyEvent.ACTION_UP) {
            return;
        }
        Intent serviceIntent;
        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_MEDIA_PLAY:
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            case KeyEvent.KEYCODE_HEADSETHOOK:
                serviceIntent = new Intent(context, PlayService.class);
                serviceIntent.setAction(Actions.ACTION_MEDIA_PLAY_PAUSE);
                context.startService(serviceIntent);
                break;
            case KeyEvent.KEYCODE_MEDIA_NEXT:
                serviceIntent = new Intent(context, PlayService.class);
                serviceIntent.setAction(Actions.ACTION_MEDIA_NEXT);
                context.startService(serviceIntent);
                break;
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                serviceIntent = new Intent(context, PlayService.class);
                serviceIntent.setAction(Actions.ACTION_MEDIA_PREVIOUS);
                context.startService(serviceIntent);
                break;
        }
    }
}

<receiver android:name=".receiver.RemoteControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

总结

本文主要讨论了音乐播放器容易忽略的重要功能,如果还有其他的本文没有提到的,请大家不吝赐教。

分享到: 腾讯 新浪 人人网 邮件 收藏夹 复制网址 更多

上一篇:Android开源音乐播放器之高仿云音乐黑胶唱片

下一篇:没有了

相关教程


共有访客发表了评论 网友评论

验证码: 看不清楚?