Run_实用Android开发Android开发

打造 Android 网络音乐播放器 [1]

2016-05-17  本文已影响3375人  MASAILA

前言

大二的时候做过两个音乐播放器的项目,虽然功能都按需求实现了,但是因为开发比较赶,后面功能加起来的时候,代码就非常乱了,可见架构的重要性,这也是我这个野生程序员比较欠缺的。最近实习比较闲,就有了重写一个音乐播放器并且写一个系列博客的想法,一是可以提升技术,二是可以锻炼下写东西的能力,三是希望以后找工作能有帮助。

http://www.vvvdj.com/showapp/
大二暑假做的一个外包,有兴趣的可以看看哈哈

开发环境

Mac OS X 10.11
Android Studio 2.1.1

实现功能

今天

今天的目标是先把播放的Service基础写好,发送播放事件可以播放歌曲。

开码

1.首先创建项目这个就不用说了,下面是程序的基本结构。

项目结构 代码结构

2.这个要解释一下先 ManagedMediaPlayer 这个类是继承了系统的 MediaPlayer,重写了基本的播放控制函数,加上了一些状态。
MusicPlayer 这个类就是把播放控制的功能封装了。
PlayerService 就一直在后台接受播放控制的事件,消息传递用的是 EventBus,
接受到播放消息后就调用 MusicPlayer 的播放方法。

https://github.com/greenrobot/EventBus
EventBus 是一个 Android 端优化的 publish/subscribe 消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。比如请求网络,等网络返回时通过 Handler 或 Broadcast 通知 UI,两个 Fragment 之间需要通过 Listener 通信,这些需求都可以通过 EventBus 实现。

3.我们先编写 player 包下面的 ManagedMediaPlayer,这个类是继承了 MediaPlayer,主要是实现了拓展一些播放状态。

/**
 * Created by MASAILA on 16/5/13.
 * 继承 MediaPlayer 拓展了状态功能
 */
public class ManagedMediaPlayer extends MediaPlayer implements MediaPlayer.OnCompletionListener {

    public enum Status {
        IDLE, INITIALIZED, STARTED, PAUSED, STOPPED, COMPLETED
    }

    private Status mState;

    private OnCompletionListener mOnCompletionListener;

    public ManagedMediaPlayer() {
        super();
        mState = Status.IDLE;
        super.setOnCompletionListener(this);
    }

    @Override
    public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        super.setDataSource(path);
        mState = Status.INITIALIZED;
    }

    @Override
    public void start() {
        super.start();
        mState = Status.STARTED;
    }

    @Override
    public void setOnCompletionListener(OnCompletionListener listener) {
        this.mOnCompletionListener = listener;
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        mState = Status.COMPLETED;
        if (mOnCompletionListener != null) {
            mOnCompletionListener.onCompletion(mp);
        }
    }

    @Override
    public void stop() throws IllegalStateException {
        super.stop();
        mState = Status.STOPPED;
    }

    @Override
    public void pause() throws IllegalStateException {
        super.pause();
        mState = Status.PAUSED;
    }

    public Status getState() {
        return mState;
    }

    public boolean isComplete() {
        return mState == Status.COMPLETED;
    }

}

代码很简单,应该就不用解释了吧,就是加多了个状态,方便上层的判断。

4.然后就是 MusicPlayer 这个类了,这个类是是把播放控制的代码封装起来了,还有播放队列的控制。

public class MusicPlayer implements MediaPlayer.OnCompletionListener {

    private static MusicPlayer player = new MusicPlayer();

    private MediaPlayer mMediaPlayer;
    private Context mContext;
    private List<Song> mQueue;
    private int mQueueIndex;
    private PlayMode mPlayMode;


    private enum PlayMode {
        LOOP, RANDOM, REPEAT
    }

    public static MusicPlayer getPlayer() {
        return player;
    }

    public static void setPlayer(MusicPlayer player) {
        MusicPlayer.player = player;
    }

    public MusicPlayer() {

        mMediaPlayer = new ManagedMediaPlayer();
        mMediaPlayer.setOnCompletionListener(this);

        mQueue = new ArrayList<>();
        mQueueIndex = 0;

        mPlayMode = PlayMode.LOOP;
    }

    public void setQueue(List<Song> queue, int index) {
        mQueue = queue;
        mQueueIndex = index;
        play(getNowPlaying());
    }

    public void play(Song song) {
        try {
            mMediaPlayer.reset();
            mMediaPlayer.setDataSource(song.getPath());
            mMediaPlayer.prepareAsync();
            mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mMediaPlayer.start();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void pause() {
        mMediaPlayer.pause();
    }

    public void resume() {
        mMediaPlayer.start();
    }

    public void next() {
        play(getNextSong());
    }

    public void previous() {
        play(getPreviousSong());
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        next();
    }

    private Song getNowPlaying() {
        if (mQueue.isEmpty()) {
            return null;
        }
        return mQueue.get(mQueueIndex);
    }

    private Song getNextSong() {
        if (mQueue.isEmpty()) {
            return null;
        }
        switch (mPlayMode) {
            case LOOP:
                return mQueue.get(getNextIndex());
            case RANDOM:
                return mQueue.get(getRandomIndex());
            case REPEAT:
                return mQueue.get(mQueueIndex);
        }
        return null;
    }

    private Song getPreviousSong() {
        if (mQueue.isEmpty()) {
            return null;
        }
        switch (mPlayMode) {
            case LOOP:
                return mQueue.get(getPreviousIndex());
            case RANDOM:
                return mQueue.get(getRandomIndex());
            case REPEAT:
                return mQueue.get(mQueueIndex);
        }
        return null;
    }


    public int getCurrentPosition() {
        if (getNowPlaying() != null) {
            return mMediaPlayer.getCurrentPosition();
        }
        return 0;
    }

    public int getDuration() {
        if (getNowPlaying() != null) {
            return mMediaPlayer.getDuration();
        }
        return 0;
    }

    public PlayMode getPlayMode() {
        return mPlayMode;
    }

    public void setPlayMode(PlayMode playMode) {
        mPlayMode = playMode;
    }

    private int getNextIndex() {
        mQueueIndex = (mQueueIndex + 1) % mQueue.size();
        return mQueueIndex;
    }

    private int getPreviousIndex() {
        mQueueIndex = (mQueueIndex - 1) % mQueue.size();
        return mQueueIndex;
    }

    private int getRandomIndex() {
        mQueueIndex = new Random().nextInt(mQueue.size()) % mQueue.size();
        return mQueueIndex;
    }

    private void release() {
        mMediaPlayer.release();
        mMediaPlayer = null;
        mContext = null;
    }

}

播放队列的控制,就是最开始传进来一个 list mQueue,然后通过更改 mQueueIndex 这个变量控制播放的歌曲。
5.然后我们在 PlayerService 里面写一个 EventBus 的接收函数,然后调用 MusicPlayer 里面的 setQueue 就可以播放了。

public class PlayerService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.getDefault().register(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    //接收EventBus post过来的PlayEvent
    @Subscribe
    public void onEvent(PlayEvent playEvent) {
        switch (playEvent.getAction()) {
            case PLAY:
                MusicPlayer.getPlayer().setQueue(playEvent.getQueue(), 0);
                break;
            case NEXT:
                MusicPlayer.getPlayer().next();
                break;
        }
    }
}

6.最后我们在 Activity 里面 post 一个 PlayEvent 事件就ok了。

public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ButterKnife.bind(this);
        startService(new Intent(this, PlayerService.class));

    }

    @OnClick({R.id.button_play, R.id.button_next, R.id.button_seek})
    public void onClick(View view) {
        PlayEvent playEvent;
        switch (view.getId()) {
            case R.id.button_play:
                playEvent = new PlayEvent();
                List<Song> queue = new ArrayList<>();
                queue.add(getSong("http://m2.music.126.net/AuxCK2R5aJlTETgi8kwN3g==/5923069139252948.mp3"));
                queue.add(getSong("http://m2.music.126.net/AuxCK2R5aJlTETgi8kwN3g==/5923069139252948.mp3"));
                playEvent.setAction(PlayEvent.Action.PLAY);
                playEvent.setQueue(queue);
                EventBus.getDefault().post(playEvent);
                break;
        }
    }

    private Song getSong(String url) {
        Song song = new Song();
        song.setPath(url);
        return song;
    }

}

下面是 Song 和 PlayEvent 的代码。

public class Song {

    private String path;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

public class PlayEvent {

    public enum Action {
        PLAY, STOP, RESUME, NEXT, PREVIOES, SEEK
    }

    private Action mAction;
    private Song mSong;
    private List<Song> mQueue;
    private int seekTo;

    public Song getSong() {
        return mSong;
    }

    public void setSong(Song song) {
        mSong = song;
    }

    public Action getAction() {
        return mAction;
    }

    public void setAction(Action action) {
        mAction = action;
    }

    public List<Song> getQueue() {
        return mQueue;
    }

    public void setQueue(List<Song> queue) {
        mQueue = queue;
    }

    public int getSeekTo() {
        return seekTo;
    }

    public void setSeekTo(int seekTo) {
        this.seekTo = seekTo;
    }
}

7.然后就可以听到美妙的音乐了哈哈哈。

然而什么都看不到哈哈哈
mp3的文件地址可能会失效,可以自己添加一个,或者在这里拿到最新的
http://music.163.com/api/song/detail?id=28755558&ids=[28755558]

https://github.com/MASAILA/Oink/tree/9ea6f1d36e7acba41c252dccd6f7ae5464c45195
今天的代码都在 github 上,有什么问题欢迎留言给我哈哈~

上一篇下一篇

猜你喜欢

热点阅读