Android开发之第一个小项目--我的MP3播放器之后台服务Service的实现(四)

这篇是讲后台控制歌曲播放service的代码。我自己感觉解析没怎么写好,看的有问题就提,我会解答的。
嗯,最进弄到了个制作gif的软件,我在最后一篇,发送个软件运行的动态图和这个程序的整体代码逻辑。
哎!新人博客还是没什么人看,自己加油吧!!!!

package com.verzqli.service;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.HttpHandler;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.verzqli.fragment.SongLrcFragment;
import com.verzqli.model.LrcSong;
import com.verzqli.model.PlaySong;
import com.verzqli.model.Song;
import com.verzqli.music.MusicPlayActivity;
import com.verzqli.utils.AppControl;
import com.verzqli.utils.LogUtil;
import com.verzqli.utils.LrcGet;
import com.verzqli.utils.MusicLoader;

import org.litepal.crud.DataSupport;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class MusicPlayService extends Service {

    private Intent intent;                       //获取actyivity传来的参数

    private String url;                          //歌曲路径

    private MediaPlayer mediaPlayer;             //创建播放器

    private List<Song> songListService;          //歌曲列表

    public int currentTime;                      //当前播放时间

    public int duration;                         //歌曲总长度

    private int currentSongPosition;             //当前播放音乐位置

    private int MSG;                             //播放按键处理事件

    private Boolean isPause = false;             //是否暂停

    private int progress;                        //歌曲进度

    private int state = 2;                       //控制歌曲的状态,随机还是循环

    private MySongStateReceiver mySongStateReceiver;  //自定义广播接收器

    private ContentValues values;                 //存入数据库的歌曲状态
    private LrcGet lrcGet;

    private List<LrcSong> lrcSongList;

    private int index = 0;                       //歌词检索值



    //服务要发送的一些Action
    public static final String SONG_STATE_UPDATE = "com.verzqli.SONG_STATE_UPDATE";  //更新动作
    public static final String MUSIC_CURRENT = "com.verzqli.action.MUSIC_CURRENT";  //音乐当前时间改变动作
    public static final String MUSIC_DURATION = "com.verzqli.action.MUSIC_DURATION";//音乐播放长度改变动作

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                if (mediaPlayer != null) {
                    /**
                     * 开始歌曲播放的重要部分了,歌曲的后台service部分
                     * 这个是一秒发送一次广播,发送的是当前歌曲的当前进度,
                     * 发送给播放界面,让其显示在进度条上。
                     */
                    currentTime = mediaPlayer.getCurrentPosition(); // 获取当前音乐播放的位置
                    Intent intent = new Intent();
                    intent.setAction(MUSIC_CURRENT);
                    intent.putExtra("currentTime", currentTime);
                    sendBroadcast(intent);
                    handler.sendEmptyMessageDelayed(1, 1000);
                }

            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        //这两个是关于歌词的部分
        lrcGet = new LrcGet();
        lrcSongList = new ArrayList<LrcSong>();
        //这个数据库的工具是郭霖的litepal,不知道的可以去看郭霖大神的博客
        values = new ContentValues();
        //这个获取的是歌曲列表,好方便后面调用
        songListService = MusicLoader.getInstance(getApplicationContext().getContentResolver()).queryData();
        if (songListService == null) {
            Intent intent = new Intent();
            intent.setAction(SONG_STATE_UPDATE);
            intent.putExtra("currentSongPosition", currentSongPosition);
            sendBroadcast(intent);
        }
        //初始化播放器
        mediaPlayer = new MediaPlayer();
        //
        /**
         * 这个是判断歌曲结束后的时间,当歌曲播放完成后,他会根据歌曲的循环模式
         * 来对歌曲播放完之后进行一个判断,是循环播放,还是随机播放等等。
         * 这个state值默认为2,也就是全部循环,当你点击上一篇博客中讲到的播放界面时,
         * 会把这个值传过来,然后歌曲播放完了进行判断下一首播放什么。
         */
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                if (state == 1) {
                    mediaPlayer.start();                                    //单曲循环
                } else if (state == 2) {
                    currentSongPosition++;
                    if (currentSongPosition > songListService.size() - 1) {
                        currentSongPosition = 0;                             //重复播放
                    }
                    Intent intent = new Intent();
                    intent.setAction(SONG_STATE_UPDATE);
                    intent.putExtra("currentSongPosition", currentSongPosition);
                    // 发送广播,将被Activity组件中的BroadcastReceiver接收到
                    sendBroadcast(intent);
                    url = songListService.get(currentSongPosition).getData();
                    //这个是播放的方法,参数是播放的开始位置。
                    playMusic(0);
                } else if (state == 3) {
                    currentSongPosition = getRandomSongPosition(songListService.size() - 1);
                    Intent intent = new Intent();
                    intent.setAction(SONG_STATE_UPDATE);
                    intent.putExtra("currentSongPosition", currentSongPosition);
                    sendBroadcast(intent);
                    url = songListService.get(currentSongPosition).getData();
                    playMusic(0);
                }

            }
        });
        //注册广播
        mySongStateReceiver = new MySongStateReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MusicPlayActivity.SONG_STATE);
        intentFilter.addAction(AppControl.LRC_SONG_CONTINUE);
        intentFilter.addAction(AppControl.LRC_DOWNLOAD);
        intentFilter.addAction(AppControl.NOTIFICATION_ACTION);
        registerReceiver(mySongStateReceiver, intentFilter);
    }

    /**
     * 获得随机播放的
     *
     * @param all 歌曲总数
     * @return
     */
    private int getRandomSongPosition(int all) {

        return (int) (Math.random() * all);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        /**
         * 下面这个intent为空的判断格外重要,当你退出界面后,歌曲还在后台播放,
         * 此时你通过关闭进程的方式强行关掉程序,但是这个service没有被正确方式关掉,
         * 它会产生一个service被杀死后重启的异常,重启之后,这个intent传过来的
         * 值百分百就为空了,这个异常,当初找了很久,但是没有找到解决办法,
         * 只好用这种粗糙的方式强行堵上。
         * 后来问了下群里的小伙伴,他们说用AIDL的方式可以解决,你们如果写可以试试。
         */
        if (intent == null) {
            Intent service = new Intent();
            service.setAction(AppControl.SERVICE_STOP);
            sendBroadcast(service);

        } else {
            currentSongPosition = intent.getIntExtra("position", -1);
            url = intent.getStringExtra("url");

            currentTime = intent.getIntExtra("currentTime", 0);
            MSG = intent.getIntExtra("MSG", 0);

            switch (MSG) {
                case 0:
                    initLrc();        //初始化歌词设置
                    playMusic(0);
                    break;
                case 4:
                    initLrc();
                    //AppControl.MEDIA_PLAYING_NEXT_SONG下一首
                    playMusic(0);
                    break;
                case 5:
                    initLrc();//AppControl.MEDIA_PLAYING_NEXT_SONG上一首
                    playMusic(0);
                    break;
                case 6:             //AppControl.MEDIA_PLAYING_SONG_PAUSE 暂停
                    songPause();
                    break;
                case 7:             //AppControl.MEDIA_PLAYING_SONG_CONTINUE继续播放
                    songContinue();
                    break;
                case 8:
                    initLrc();        //初始化歌词设置,使拖动进度条之后也能让歌词跟上进度
                    progress = intent.getIntExtra("progress", -1);
                    playMusic(progress);
                    break;
                case 9:
                    progress = intent.getIntExtra("progress", -1);
                    playMusic(progress);
                    break;
            }

        }


    }


    /**
     * 初始化歌词配置
     */
    public void initLrc() {
        lrcGet = new LrcGet();
        //读取歌词文件
        lrcGet.readLRC(songListService.get(currentSongPosition).getData());
        //传回处理后的歌词文件
        lrcSongList = lrcGet.getLrcList();
        if (SongLrcFragment.lrcView != null) {
            SongLrcFragment.lrcView.setmLrcList(lrcSongList);
            //切换带动画显示歌词
            //MusicPlayActivity.lrcView.setAnimation(AnimationUtils.loadAnimation(MusicPlayService.this, R.anim.alpha_z));
            handler.post(mRunnable);
        }


    }

    /**
     * 下载歌词
     */
    private void downLoadLrc(String num) {
        HttpUtils http = new HttpUtils();
        String str = "/storage/sdcard0/" + songListService.get(currentSongPosition).getDisplayName();
        String url = str.substring(0, str.length() - 4) + ".lrc";
        LogUtil.itSelf.Log("downLoadLrc" + num);
        HttpHandler handler = http.download("http://music.baidu.com/data2/lrc/" + num + "/" + num + ".lrc",

                url,
                true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
                false, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
                new RequestCallBack<File>() {

                    @Override
                    public void onStart() {
                        Log.i("TAG", "conn...");
                    }

                    @Override
                    public void onLoading(long total, long current, boolean isUploading) {

                    }

                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                        Toast.makeText(getApplicationContext(), "下载完成", Toast.LENGTH_SHORT).show();
                        initLrc();
                    }


                    @Override
                    public void onFailure(HttpException error, String msg) {
                        Toast.makeText(getApplicationContext(), "加载中失败", Toast.LENGTH_SHORT).show();
                    }
                });
    }

    Runnable mRunnable = new Runnable() {
        @Override
        public void run() {

            SongLrcFragment.lrcView.setIndex(lrcIndex());

            SongLrcFragment.lrcView.invalidate();

            handler.postDelayed(mRunnable, 1000);


        }
    };

    /**
     * 根据时间获取歌词显示的索引值
     *
     * @return
     */
    public int lrcIndex() {
        if (mediaPlayer.isPlaying()) {
            currentTime = mediaPlayer.getCurrentPosition();
            duration = mediaPlayer.getDuration();
        }
        if (currentTime < duration) {
            for (int i = 0; i < lrcSongList.size(); i++) {
                if (i < lrcSongList.size() - 1) {
                    if (currentTime < lrcSongList.get(i).getLrcTime() && i == 0) {
                        index = i;
                    }
                    if (currentTime > lrcSongList.get(i).getLrcTime()
                            && currentTime < lrcSongList.get(i + 1).getLrcTime()) {
                        index = i;
                    }
                }
                if (i == lrcSongList.size() - 1
                        && currentTime > lrcSongList.get(i).getLrcTime()) {
                    index = i;
                }
            }
        }
        return index;
    }

    /**
     * 继续播放
     */
    private void songContinue() {
        if (isPause) {
            mediaPlayer.start();
            isPause = false;
        }
    }

    /**
     * 暂停播放
     */
    private void songPause() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            isPause = true;
        }
    }

    /**
     * 播放音乐
     *
     * @param currentTime
     */
    private void playMusic(int currentTime) {

        try {

            Intent intent = new Intent();
            intent.setAction(SONG_STATE_UPDATE);
            intent.putExtra("currentSongPosition", currentSongPosition);
            // 发送广播,将被Activity组件中的BroadcastReceiver接收到
            sendBroadcast(intent);
            mediaPlayer.reset();
            mediaPlayer.setDataSource(url);
            mediaPlayer.prepare();
            mediaPlayer.setOnPreparedListener(new PrepareListener(currentTime));
            handler.sendEmptyMessage(1);


        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 歌曲准备阶段接口
     */
    public class PrepareListener implements MediaPlayer.OnPreparedListener {
        private int pcurrentTime;

        public PrepareListener(int currentTime) {
            this.pcurrentTime = currentTime;
        }

        @Override
        public void onPrepared(MediaPlayer mp) {
            mediaPlayer.start();                                     // 开始播放
            if (pcurrentTime > 0) {                                  // 如果音乐不是从头播放
                mediaPlayer.seekTo(pcurrentTime);
            }
            Song song = songListService.get(currentSongPosition);
            Intent intent = new Intent();
            intent.setAction(MUSIC_DURATION);
            duration = mediaPlayer.getDuration();
            intent.putExtra("duration", duration);                    //通过Intent来传递歌曲的总长度
            intent.putExtra("text", song.getDisplayName());
            //
            /**
             * 这个是存入当前播放的歌曲进入数据库,
             * 前面我也说到了,我要的效果是退出了界面也能播放歌曲并能更新进度
             * 就是在准备阶段将这个歌曲存入数据库,当进入程序界面时,能从数据库中准确取到当前播放的歌曲信息
             */
            values.put("displayName", song.getDisplayName());
            values.put("duration", song.getDuration());
            values.put("songPosition", currentSongPosition);
            DataSupport.update(PlaySong.class, values, 1);
            sendBroadcast(intent);
        }
    }

    public final static String INTENT_BUTTONID_TAG = "INTENT_BUTTONID_TAG";


    private class MySongStateReceiver extends BroadcastReceiver {
        //这里就是接受前两个界面点击事件时发送过来的广播
        @Override
        public void onReceive(Context context, Intent intent) {
            int states = intent.getIntExtra("state", -1);
            switch (states) {
                case 1:
                    state = 1;
                    break;
                case 2:
                    state = 2;
                    break;
                case 3:
                    state = 3;
                    break;
                case 4:
                    state = 4;
                    break;
            }
            String action = intent.getAction();
            //AppControl.LRC_SONG_CONTINUE这些数据是一个专门存数据的类。下了代码你们就可以看到了
            if (action.equals(AppControl.LRC_SONG_CONTINUE)) {
                currentSongPosition = intent.getIntExtra("position", -1);
                url = intent.getStringExtra("url");
                currentTime = intent.getIntExtra("mainCurrentTime", 0);
                initLrc();

            }
            if (action.equals(AppControl.LRC_DOWNLOAD)) {
                String songLrcId = intent.getStringExtra("songLrdId");
                downLoadLrc(songLrcId);

            }
            //这个是通知栏发送过来对歌曲播放控制的广播
            if (action.equals(AppControl.NOTIFICATION_ACTION)) {
                String buttonId = intent.getStringExtra(INTENT_BUTTONID_TAG);
                switch (buttonId) {
                    case "prev":
                        if (currentSongPosition - 1 >= 0) {
                            currentSongPosition = currentSongPosition - 1;
                            url = songListService.get(currentSongPosition).getData();
                            playMusic(0);
                            Toast.makeText(getApplicationContext(), "上一首", Toast.LENGTH_SHORT).show();
                        } else {
                            currentSongPosition = songListService.size() - 1;
                            url = songListService.get(currentSongPosition).getData();
                            playMusic(0);
                        }

                        break;
                    case "play":
                        if (isPause) {
                            mediaPlayer.start();
                            isPause = false;
                        } else {
                            mediaPlayer.pause();
                            isPause = true;
                        }

                        Toast.makeText(getApplicationContext(), "play", Toast.LENGTH_SHORT).show();
                        break;
                    case "next":
                        if (currentSongPosition + 1 <= songListService.size() - 1) {
                            currentSongPosition = currentSongPosition + 1;
                            url = songListService.get(currentSongPosition).getData();
                            playMusic(0);
                            Toast.makeText(getApplicationContext(), "上一首", Toast.LENGTH_SHORT).show();
                        } else {
                            currentSongPosition = 0;
                            url = songListService.get(currentSongPosition).getData();
                            playMusic(0);
                        }


                        break;
                    default:
                        break;
                }
            }
        }

    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        //ondestory中记得要反注册广播,关闭handler,关闭播放器,防止内存泄露
        handler.removeCallbacks(mRunnable);
        handler.removeMessages(1);
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }
        unregisterReceiver(mySongStateReceiver);
        AppControl.JUDGE_DESTORY_ONCREATE = -1;
        values.put("playing", false);
        //关闭程序时存下当前歌曲,再次进入程序时看到的是上次播放的那首
        DataSupport.update(PlaySong.class, values, 1);

    }

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

}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值