Android实现音乐播放器,Service后台播放&Notification控制播放状态

本音乐播放器的功能:

1.实现访问手机本地的.mp3音乐文件;
2.在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以);
3.在播放音乐页面实现上一首、下一首、播放/暂停、进度条随音乐播放滑动、动态显示播放时间,拖动进度条,当前播放时间动态改变并且播放进度也会改变、自动切换下一首歌;
4.从在Activity中播放音乐过渡到在Service中播放音乐;
5.改进该项目,使用广播的方式播放音乐并能够在Notification中控制音乐的播放状态。

由于我将全部代码都贴在了本博客,所以文章篇幅较长。

我的最终效果图:

本项目效果图:

Android实现音乐播放器,Service后台播放&Notification控制播放状态_第1张图片

----------------------------------------以下就开始分别实现----------------------------------------
第一步:实现访问手机本地的.mp3音乐文件
  1. 编写一个音乐类Music(以 周杰伦 - 最长的电影.mp3 为例):编写四个成员变量,并在其中提供对应的set()和get()方法

//音乐的名字(周杰伦 - 最长的电影.mp3),截取后缀获得.mp3的文件,subString("-")截取歌曲的名字
private String name;
//音乐文件的作者
private String artist;
//音乐文件的路径
private String url;
//音乐播放的时间
private int time;

  1. 编写一个专门访问本地音乐文件的类MusicList,这里用到读取内存的权限,因此别忘了要在AndroidManifest.xml中添加权限
    
    

实现步骤:
①编写静态方法getMusicData(),返回值就是查询到的音乐数据集合;
②在该方法中创建ContentRecolver实例,通过上下文的getContentResolver()方法;
③判断获取到的ContentResolver是否为空,如果不为空,调用contentResolver.query (Uri uri, String[] projection,String selection,String[] selectionArgs, String sortOrder)方法查询本地的音乐文件,返回一个Cursor对象。如果Cursor为空,说明没有数据,直接返回null;
④如果有数据,利用Cursor对象的moveToFirst()查询第一条数据,如果不为空,就循环调用moveToNext()方法查询下一条数据直到没有数据为止;
⑤在查询中,通过cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.××))获取到相应的数据,该数据就对应等于Music中的成员变量;’
⑥将查询到的数据赋值到Music对象,完成查询数据的存储,并把该Music对象添加到Music集合,这样就获取到了本地音乐数据集合。
对上面的补充说明(对ContentResolver和ContentProvider了解的可直接跳过)
ContentProvider在android中的作用是对外共享数据,也就是说你可以通过 ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。
ContentResolver:提供访问ContentProvider的能力,可以使用ContentResolver读取和操作其他应用程序已经通过ContentProvider暴露出来的数据。

/**
 * 从内部存储中读取下载好的后缀名为.mp3的音乐文件,返回值为Music集合
 */
public class MusicList {

    public static ArrayList getMusicData(Context context){
        ArrayList musicList = new ArrayList();
        ContentResolver contentResolver = context.getContentResolver();
        if(contentResolver != null){
            Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
            if(cursor == null){
                return null;
            }
            if(cursor.moveToFirst()) {
                do {
                    Music music = new Music();
                    String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
                    String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
                    if ("".equals(artist)) {
                        String[] split = name.split("-");
                        artist =  split[0];
                    }
                    int time = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
                    String url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
                    String isMp3 = name.substring(name.length() - 3, name.length());
                    if (isMp3.equals("mp3")) {
                        music.setArtist(artist);
                        music.setTime(time);
                        music.setUrl(url);
                        music.setName(name.substring(0,name.length()-4));
                        musicList.add(music);
                    }
                } while (cursor.moveToNext());
            }
        }
        return musicList;
    }
}

第二步:在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以)

实现步骤:

1.请求运行时权限(Android 6.0及以上系统在使用危险权限时都必须进行运行时权限的处理,这里如果不进行处理,会出现闪退的现象)
	//自定义方法requestPermission()
    private void requestPermission() {
    	//创建允许权限的集合
        List permissionList = new ArrayList();
        //如果该权限没同意,就加入到允许权限的集合
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        //如果允许权限不为空,说明有权限还没申请,就一起申请
        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1);
        }
		//权限申请完成之后,才去初始化视图
		 else {
			 //适配器的适配工作
            initView();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        int grantResult = grantResults[i];
                        if (grantResult == PackageManager.PERMISSION_DENIED) {
                            String s = permissions[i];
                            Toast.makeText(this, s + "权限被拒绝了", Toast.LENGTH_SHORT).show();
                        } else {
                            initView();
                        }
                    }
                }
        }
    }
2.设置适配器显示数据

①编写主页的xml(activity_main.xml)



    


②在主页中获取到该控件,并给该控件设置适配器进行显示

MusicAdapter
public class MusicAdapter extends BaseAdapter {
    private Context mContext;
    private List mMusicList;

    public MusicAdapter(Context context,List musicList){
        mContext = context;
        mMusicList = musicList;
    }

    @Override
    public int getCount() {
        return mMusicList.size();
    }

    @Override
    public Object getItem(int position) {
        return mMusicList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        Music music = mMusicList.get(position);
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.music_item, parent,false);
            viewHolder = new ViewHolder();
            viewHolder.music_item_name = convertView.findViewById(R.id.music_item_name);
            viewHolder.music_item_artist = convertView.findViewById(R.id.music_item_artist);
            viewHolder.music_item_time = convertView.findViewById(R.id.music_item_time);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
            viewHolder.music_item_name.setText(music.getName());
            viewHolder.music_item_artist.setText(music.getArtist());
            viewHolder.music_item_time.setText(formatTime(music.getTime()));
        return convertView;
    }

    class ViewHolder {
        TextView music_item_name;
        TextView music_item_artist;
        TextView music_item_time;
    }
	//将时间转换为××:××格式
    private String formatTime(int time) {
        int ms2s = (time / 1000);
        int minute = ms2s / 60;
        int second = ms2s % 60;
        return String.format("%02d:%02d", minute, second);
    }
}

music.item.xml


        

        

            

            
        


MainActivity
public class MainActivity extends AppCompatActivity {
    private ArrayList arrayList;
    private MusicAdapter mMusicAdapter;
    private ListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        requestPermission();
    }

    private void initView() {
        arrayList = MusicList.getMusicData(this);
        mListView = findViewById(R.id.listView_main);
        mMusicAdapter = new MusicAdapter(this, arrayList);
        mListView.setAdapter(mMusicAdapter);
        //实现页面跳转,将当前点击的音乐条目传过去给音乐播放页面,这样它才知道播放哪条音乐
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
//                Intent intent = new Intent(MainActivity.this,DetailActivity.class);
//                Bundle bundle =  new Bundle();
//                bundle.putInt("position",position);
//                intent.putExtras(bundle);
//                startActivity(intent);
            }
        });
   	}

    private void requestPermission() {
        ...
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
       ...
    }
}

到此为止,就能实现将本地音乐显示到主页的效果了。接着,开始实现音乐播放页面(DetailActivity)

第三步:在播放音乐页面实现上一首、下一首、播放/暂停、进度条随音乐播放滑动、用手拖动进度条,当前播放时间动态改变并且播放进度也会改变、自动切换下一首歌
①编写播放页面的布局(activity_detail.xml)


    

        
    

    

        

        

            

            
        

        

            
②初始化各个控件
public class DetailActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_pre, btn_play, btn_next;
    private TextView tv_cur_time, tv_total_time;
    private ImageView btn_return;
    private SeekBar seekBar;
    private ArrayList mMusicList;
    private int mPosition;
    private MediaPlayer mMediaPlayer;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        initView();
        initData();
    }
    private void initData() {
        mMediaPlayer = new MediaPlayer();
        //拿到播放音乐的位置
        mPosition = getIntent().getIntExtra("position", -1);
        //拿到音乐数据集合
        mMusicList = MusicList.getMusicData(this);
    }

    private void initView() {
        btn_pre = findViewById(R.id.btn_pre);
        btn_play = findViewById(R.id.btn_play);
        btn_next = findViewById(R.id.btn_next);
        btn_return = findViewById(R.id.btn_return);
        tv_cur_time = findViewById(R.id.tv_cur_time);
        tv_total_time = findViewById(R.id.tv_total_time);
        seekBar = findViewById(R.id.seekBar);
        btn_pre.setOnClickListener(this);
        btn_play.setOnClickListener(this);
        btn_next.setOnClickListener(this);
        btn_return.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_pre:
                break;
            case R.id.btn_play:
                break;
            case R.id.btn_next:
                break;
            case R.id.btn_return:
                onBackPressed();
                break;
        }
    }
}

③开始编写方法
1.播放音乐的方法
public void playMusic(int position) {
    try {
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(mMusicList.get(position).getUrl());
        mMediaPlayer.prepare();
        mMediaPlayer.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
2.播放/暂停的方法
public void play() {
    if (mMediaPlayer.isPlaying()) {
        mMediaPlayer.pause();
        btn_play.setText("播放");
    } else {
        mMediaPlayer.start();
        btn_play.setText("暂停");
    }
}
3.下一曲/上一曲的方法
public void next(int offset) {
    mPosition += offset;
    mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
    playMusic(mPosition);
}

在按钮点击事件中调用,就可以播放,暂停,下一曲,上一曲。为了让界面一进入就能够播放音乐,可以在onCreate()方法中调用playMusic(mPosition),不过记得要在initData()之后,也就是获取到mPosition之后,mPosition就是在MainActivity中通过ListView的ItemClickListener将位置传过来。
别忘了DetailActivity要在AndroidManifest中注册!!

④进度条随音乐播放滑动、动态显示播放时间

更新操作需要单独开一个线程执行,将工作线程需操作UI的消息传递到主线程,使得主线程可根据工作线程的需求更新UI。这是由于在Android开发中,为了UI操作是线程安全的,规定了只允许在主线程中更新UI。因此,在主线程中编写Handler。
实现步骤较简单,直接看代码。
消息应该在什么时候发送呢?
应该在播放音乐playMusic()中调用handler.sendEmptyMessage(0x01),也就是一播放音乐就开启调用。

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        if(msg.what == 0x01){
            tv_cur_time.setText("00:00");
            //这里的格式化时间在前面已有编写
            tv_cur_time.setText(formatTime(mMediaPlayer.getCurrentPosition()));
            tv_total_time.setText(formatTime(mMusicList.get(mPosition).getTime()));
            seekBar.setProgress(mMediaPlayer.getCurrentPosition());
            seekBar.setMax(mMusicList.get(mPosition).getTime());
            handler.sendEmptyMessage(0x01);
        }
    }
};
⑤拖动进度条,当前播放时间动态改变并且播放进度也会改变

需要用到seekbar的setOnSeekBarChangeListener()方法

  • 当调用其中的onStartTrackingTouch()方法时,表示seekbar开始滑动了,将自定义的变量isSeekBarChanging设为true;
  • 当调用其中的onStopTrackingTouch()方法时,表示seekbar结束滑动了,将自定义的变量 isSeekBarChanging设为false。既然结束滑动了,那就要开始更改当前播放进度,通过调用mMediaPlayer.seekTo(seekBar.getProgress())方法完成;
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
       
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        isSeekBarChanging = true;
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        isSeekBarChanging = false;
        mMediaPlayer.seekTo(seekBar.getProgress());
    }
});

添加到handleMessage()方法中即可。

⑦自动切换下一首歌
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
        next(1);
        Toast.makeText(DetailActivity.this, "自动为您切换下一首:"+mMusicList.get(mPosition).getName(), Toast.LENGTH_SHORT).show();
    }
});

同样,将其添加到handleMessage()方法中。到此,播放功能已全部实现。

==不要着急,我们试着退出播放音乐的页面,再重新选择歌曲播放,会发现可以同时播放多条音乐,且他们之间互不影响,为什么呢?
这是因为我们的MediaPlayer每次使用完后都没有释放资源,每点击一个音乐条目进来就重新创建了一个MediaPlayer。那么如何做呢?

思路:根据两次传进来的position的异同,决定是否释放上次的MediaPlayer资源

创建一个静态的MediaPlayer,保存上次的MediaPlayer
创建一个静态的position,保存上次的position

每次播放音乐的时候,如果第一次传进来的position和第二次传进来的position不一样,说明不是同一个音乐文件,那么MediaPlayer就要进行一系列操作。首先创建一个新的MediaPlayer,把上一次的MediaPlayer资源释放掉,将本次的MediaPlayer赋值为上一次的MediaPlayer,将本次的position赋值为上次的position。
每次播放音乐的时候,如果第一次传进来的position和第二次传进来的position一样,说明是同一个音乐文件,那么MediaPlayer就不需要做任何操作,直接将上次的MediaPlayer赋值给本次的MediaPlayer,上次的position赋值给本次的position,这样就不用去创建新的MediaPlayer,直接拿到上次的position。
这里有一点需要注意,现在MediaPlayer的创建时放在了playMusic()方法中,因为每次播放音乐都要判断是否需要创建新的MediaPlayer,而且用handler处理message也加了个判断,具体更改见代码:

public class DetailActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_pre, btn_play, btn_next;
    private TextView tv_cur_time, tv_total_time;
    private ImageView btn_return;
    private SeekBar seekBar;
    private ArrayList mMusicList;
    private int mPosition;
    static int savePosition;
    private MediaPlayer mMediaPlayer;
    static MediaPlayer mPreMediaPlayer;
    private boolean isSeekBarChanging;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x01 && mPosition == savePosition) {
                tv_cur_time.setText("00:00");
                tv_cur_time.setText(formatTime(mMediaPlayer.getCurrentPosition()));
                tv_total_time.setText(formatTime(mMusicList.get(mPosition).getTime()));
                seekBar.setProgress(mMediaPlayer.getCurrentPosition());
                seekBar.setMax(mMusicList.get(mPosition).getTime()); 
                seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = true;
                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = false;
                        mMediaPlayer.seekTo(seekBar.getProgress());
                    }
                });
                mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        next(1);
                        Toast.makeText(DetailActivity.this, "自动为您切换下一首:" + mMusicList.get(mPosition).getName(), Toast.LENGTH_SHORT).show();
                    }
                });
                handler.sendEmptyMessage(0x01);
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        initView();
        initData();
        if (mPreMediaPlayer == null || mPosition != savePosition) {
            playMusic(mPosition);
        } else {
            mMediaPlayer = mPreMediaPlayer;
            mPosition = savePosition;
            handler.sendEmptyMessage(0x01);
        }
        // playMusic(mPosition);
    }

    private void initData() {
        //拿到播放音乐的位置
        mPosition = getIntent().getIntExtra("position", -1);
        //拿到音乐数据集合
        mMusicList = MusicList.getMusicData(this);
    }

    private void initView() {
        btn_pre = findViewById(R.id.btn_pre);
        btn_play = findViewById(R.id.btn_play);
        btn_next = findViewById(R.id.btn_next);
        btn_return = findViewById(R.id.btn_return);
        tv_cur_time = findViewById(R.id.tv_cur_time);
        tv_total_time = findViewById(R.id.tv_total_time);
        seekBar = findViewById(R.id.seekBar);
        btn_pre.setOnClickListener(this);
        btn_play.setOnClickListener(this);
        btn_next.setOnClickListener(this);
        btn_return.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_pre:
                next(-1);
                break;
            case R.id.btn_play:
                play();
                break;
            case R.id.btn_next:
                next(1);
                break;
            case R.id.btn_return:
                onBackPressed();
                break;
        }
    }

    public void playMusic(int position) {
        mMediaPlayer = new MediaPlayer();
        if (mPreMediaPlayer != null) {
            mPreMediaPlayer.stop();
            mPreMediaPlayer.release();
        }
        mPreMediaPlayer = mMediaPlayer;
        savePosition = mPosition;
        try {
            mMediaPlayer.reset();
            mMediaPlayer.setDataSource(mMusicList.get(position).getUrl());
            mMediaPlayer.prepare();
            mMediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        handler.sendEmptyMessage(0x01);
    }

    public void play() {
        if (mMediaPlayer.isPlaying()) {
            mMediaPlayer.pause();
            btn_play.setText("播放");
        } else {
            mMediaPlayer.start();
            btn_play.setText("暂停");
        }
    }

    public void next(int offset) {
        mPosition += offset;
        mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
        playMusic(mPosition);
    }

    private String formatTime(int time) {
        int ms2s = (time / 1000);
        int minute = ms2s / 60;
        int second = ms2s % 60;
        return String.format("%02d:%02d", minute, second);
    }
}

到此为止,前三个功能已经全部实现,下面开始实现 在Service中播放本地音乐

大体思路:
①创建自定义Service类继承自Service,因为需要用到绑定服务,所以要自定义MusicBinder类继承自Binder,然后在onBind()方法中返回我们自定义的MusicBinder类的实例,该实例定义了Activity可以与Service交互的程序接口
②在MusicBinder类中开始编写一系列操作音乐播放的代码,也就是把原来Activity中与MediaPlayer播放音乐有关的方法和与MediaPlayer操作有关的变量全部移植过来;
③在Activity中开启服务并绑定服务,这样就能访问到Service中程序接口定义的方法;
④如果Activity中需要用到MediaPlayer的方法接口中却没有,那么就在接口中添加自定义方法然后通过调用该接口即可。
⑤记得要在不使用Service的地方解绑该服务。
具体细节看更改后的代码:

MusicService
public class MusicService extends Service {
    private int mPosition;
    //静态存储上次音乐条目的位置
    static int savePosition;
    private MediaPlayer mMediaPlayer;
    //静态存储上次音乐条目的MediaPlayer
    static MediaPlayer mPreMediaPlayer;
    private ArrayList mMusicList;
    //静态存储当前音乐是否在播放
    static boolean isPlaying;

    public MusicService() {
    }

    //onCreate()只被执行一次,因此用来做初始化
    @Override
    public void onCreate() {
        super.onCreate();
        mMusicList = MusicList.getMusicData(this);
    }

    class MusicBinder extends Binder {
        //创建一个绑定服务时,必须提供一个客户端与Service交互的IBinder,有三种方法
        //我使用的方法:返回当前Service实例,它具有一些客户端可以公开调用的公开方法
        public MusicService getService() {
            return MusicService.this;
        }
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mPosition = intent.getExtras().getInt("position", -1);
        if (mPreMediaPlayer == null || mPosition != savePosition) {
            playMusic(mPosition);
        } else {
            mMediaPlayer = mPreMediaPlayer;
            mPosition = savePosition;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 播放音乐
     */
    public void playMusic(int position) {
        mMediaPlayer = new MediaPlayer();
        if (mPreMediaPlayer != null) {
            mPreMediaPlayer.stop();
            mPreMediaPlayer.release();
        }
        mPreMediaPlayer = mMediaPlayer;
        savePosition = position;
        try {
            mMediaPlayer.reset();
            mMediaPlayer.setDataSource(mMusicList.get(position).getUrl());
            mMediaPlayer.prepare();
            mMediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mPosition += 1;
                mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
                playMusic(mPosition);
                Toast.makeText(getApplicationContext(), "自动为您切换下一首:" + mMusicList.get(mPosition).getName(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 按钮点击:播放音乐
     */
    public void play() {
        if (mMediaPlayer.isPlaying()) {
            mMediaPlayer.pause();
            isPlaying = false;
        } else {
            mMediaPlayer.start();
            isPlaying = true;
        }
    }

    /**
     * 按钮点击:下一首
     */
    public void next(int offset) {
        mPosition += offset;
        mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
        playMusic(mPosition);
    }

    /**
     * 获取当前音乐的名字
     */
    public String getName() {
        return mMusicList.get(mPosition).getName();
    }

    /**
     * 获取当前音乐的播放时间
     */
    public int getTime() {
        return mMusicList.get(mPosition).getTime();
    }

    /**
     * 获取当前播放位置
     */
    public int getCurrent() {
        return mMediaPlayer.getCurrentPosition();
    }

    /**
     * 设置音乐播放的进度
     */
    public void seekTo(int progress) {
        mMediaPlayer.seekTo(progress);
    }
}
DetailActivity
public class DetailActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_pre, btn_play, btn_next;
    private TextView tv_cur_time, tv_total_time;
    private SeekBar seekBar;
    private ImageView btn_return;
    //seekBar是否被拖动
    private boolean isSeekBarChanging;
    private MusicService musicService;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x01) {
                tv_cur_time.setText("00:00");
                tv_cur_time.setText(formatTime(musicService.getCurrent()));
                tv_total_time.setText(formatTime(musicService.getTime()));
                seekBar.setProgress(musicService.getCurrent());
                seekBar.setMax(musicService.getTime());
                seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = true;
                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = false;
                        musicService.seekTo(seekBar.getProgress());
                    }
                });
                handler.sendEmptyMessage(0x01);
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
        handler.removeCallbacksAndMessages(null);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        initView();
        Intent intent = new Intent(this, MusicService.class);
        //获取MainActivity传过来的数据
        Bundle bundle = getIntent().getExtras();
        //再把这些包装好的数据重新装入Intent,发送给Service
        intent.putExtras(bundle);
        startService(intent);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            musicService = ((MusicService.MusicBinder) service).getService();
            handler.sendEmptyMessage(0x01);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 按钮点击事件
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_pre:
                musicService.next(-1);
                break;
            case R.id.btn_play:
                musicService.play();
                break;
            case R.id.btn_next:
                musicService.next(1);
                break;
            case R.id.btn_return:
                onBackPressed();
                break;
        }
        updateBtnPlayOrPause();
    }

    private void updateBtnPlayOrPause() {
        if (MusicService.isPlaying) {
            btn_play.setText("暂停");
        } else {
            btn_play.setText("播放");
        }
    }

    private void initView() {
        ...
    }

    private String formatTime(int time) {
        ...
    }
}

这里别忘了如果service不是通过Android Studio自动生成的,记得在AndroidManifest中声明。

第五步:使用广播的方式播放音乐并能够在Notification中控制音乐的播放状态

实现步骤:
①创建播放/暂停、上一曲、下一曲的动态广播;
②在按钮点击事件中发送对应的广播;
③编写广播接收器,对广播进行过滤。拿到想要的广播后,根据广播的类型进行操作;
④在服务开启的时候就开启广播接收器。

创建播放/暂停、上一曲、下一曲的动态广播

在DetailActivity中创建动态广播

/**
 * 按钮:播放音乐的广播
 */
public void playMusic(){
    Intent intent = new Intent();
    intent.setAction(MusicService.ACTION);
    Bundle bundle = new Bundle();
    bundle.putInt(MusicService.BTN_STATE,MusicService.PLAY_STATE);
    intent.putExtras(bundle);
    sendBroadcast(intent);
}

/**
 * 按钮:下一首音乐的广播
 */
public void nextMusic(){
    Intent intent = new Intent();
    intent.setAction(MusicService.ACTION);
    Bundle bundle = new Bundle();
    bundle.putInt(MusicService.BTN_STATE,MusicService.NEXT_MUSIC_STATE);
    intent.putExtras(bundle);
    sendBroadcast(intent);
}

/**
 * 按钮:上一首音乐的广播
 */
public void preMusic(){
    Intent intent = new Intent();
    intent.setAction(MusicService.ACTION);
    Bundle bundle = new Bundle();
    bundle.putInt(MusicService.BTN_STATE,MusicService.PRE_MUSIC_STATE);
    intent.putExtras(bundle);
    sendBroadcast(intent);
}
编写广播接收器,过滤广播,拿到想要的广播并根据传过来的值进行相应的操作

在MusicService中编写广播接收器

private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(ACTION)) {
            switch (intent.getIntExtra(BTN_STATE, -1)) {
                case PLAY_STATE:
                    play();
                    break;
                case PRE_MUSIC_STATE:
                    next(-1);
                    break;
                case NEXT_MUSIC_STATE:
                    next(1);
                    break;
                default:
                    Toast.makeText(context, "系统出错,请稍后重试!", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
};
在服务开启的时候就开启广播接收器

在MusicService刚刚启动的时候(onCreate())就注册了一个广播,这样他就能够接收其他页面点击了上一曲、下一曲、暂停/播放按钮发出的广播。因此,其他页面中点击上一曲、下一曲、暂停、播放按钮时都需要向MusicService发送广播通知,让它来更新音乐。

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION);
registerReceiver(receiver,intentFilter);

同时我们还要更改一下之前的代码,在按钮点击事件中,之前我们是通过服务的方法去响应事件,我们都将其改为用广播的方式。也就是把musicService.next(-1)改为nextMusic(),musicService.play()改为playMusic()、musicService.next(1)改为nextMusic()。

这里用到的常量我也贴出代码来:

//动态广播的Action
public static String ACTION = "action";
//按钮点击状态标识符
public static String BTN_STATE = "btn_state";
public static final int PLAY_STATE = 0, NEXT_MUSIC_STATE = 1, PRE_MUSIC_STATE = 2;
这样就可以实现以广播的方式播放音乐
紧接着实现通过Notification控制音乐的播放状态这一功能。

功能剖析:
首先需要先有Notification,所以在开启服务之后也就是onStartCommand()方法中,就先创建出Notification并进行一系列的初始化操作。
播放页面中按钮一点击就发送广播,广播接收器接收到广播,对音乐状态进行控制。并且播放页面中按钮的点击能够更改Notification中播放/暂停按钮显示的图片和当前播放的状态。
Notification中的按钮一点击就发送广播,广播接收器接收到广播,对音乐状态进行控制,并且Notification中按钮的点击能够更改播放页面中播放/暂停按钮显示的文字和当前播放的状态。

实现步骤:
①由于Notification布局本项目采用自定义,因此需要编写自定义的Notification布局RemoteViews;
②编写Notification初始化方法,这里需要在获取到position之后调用,因为RemoteViews需要根据position显示歌曲名字(这里我把Notification和Button的广播做成两个,方便大家逐块进行理解,所以代码比较累赘,可以考虑封装合并,读者有条件自行完成;)
③编写Notification的更新的方法并在每一次接收到按钮发出的广播的时候对Notification进行更新,这样按钮发出广播既实现了对音乐状态的控制,又实现了对Notification的更新;
④编写按钮的更新方法并在每一次接收到Notification发出的广播的时候对按钮进行更新,这样Notification发出的广播既实现了对音乐状态的控制,又实现了对按钮的更新。

以下是最终代码:

RemoteVeiws布局



    

    

        


        

            

            

            
        
    

public class DetailActivity extends AppCompatActivity implements View.OnClickListener {
    private ImageView iv_pre, iv_next, iv_play, btn_return;
    private TextView tv_cur_time, tv_total_time;
    private SeekBar seekbar;
    //seekBar是否被拖动
    private boolean isSeekBarChanging;
    private MusicService musicService;

    //按钮点击状态标识符
    public static String NOTIFICATION_ACTION = "notification_action";
    public static String NOTIFICATION_BTN_STATE = "notification_btn_state";
    public static final int NOTIFICATION_PLAY = 0, NOTIFICATION_NEXT_MUSIC = 1, NOTIFICATION_PRE_MUSIC = 2;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x01) {
                tv_cur_time.setText("00:00");
                tv_cur_time.setText(formatTime(musicService.getCurrent()));
                tv_total_time.setText(formatTime(musicService.getTime()));
                seekbar.setMax(musicService.getTime());
                seekbar.setProgress(musicService.getCurrent());
                seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = true;
                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                        isSeekBarChanging = false;
                        musicService.seekTo(seekBar.getProgress());
                    }
                });
                handler.sendEmptyMessage(0x01);
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        initView();
        initService();
        initBroadcastReceiver();
    }

    private void initBroadcastReceiver() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(NOTIFICATION_ACTION);
        registerReceiver(receiver,intentFilter);
    }

    private void initService() {
        Intent intent = new Intent(this, MusicService.class);
        //获取MainActivity传过来的数据
        Bundle bundle = getIntent().getExtras();
        //再把这些包装好的数据重新装入Intent,发送给Service
        intent.putExtras(bundle);
        startService(intent);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
        handler.removeCallbacksAndMessages(null);
    }

    /**
     * 广播接收器,接收来自Notification的广播
     */
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(NOTIFICATION_ACTION)) {
                switch (intent.getIntExtra(NOTIFICATION_BTN_STATE, -1)) {
                    case NOTIFICATION_PRE_MUSIC:
                        musicService.next(-1);
                        break;
                    case NOTIFICATION_PLAY:
                        musicService.play();
                        break;
                    case NOTIFICATION_NEXT_MUSIC:
                        musicService.next(1);
                        break;
                    default:
                        Toast.makeText(context, "系统出错,请稍后重试!", Toast.LENGTH_SHORT).show();
                        break;
                }
                updateNotificationBtnPlayOrPause();
                musicService.updateNotification();
            }
        }
    };

    private void updateNotificationBtnPlayOrPause() {
        if (MusicService.isPlaying) {
            iv_play.setImageResource(R.drawable.pause);
        } else {
            iv_play.setImageResource(R.drawable.play);
        }
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            musicService = ((MusicService.MusicBinder) service).getService();
            handler.sendEmptyMessage(0x01);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 按钮点击事件
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_pre:
                preMusic();
                MusicService.isPlaying = false;
                break;
            case R.id.iv_play:
                playMusic();
                break;
            case R.id.iv_next:
                nextMusic();
                MusicService.isPlaying = false;
                break;
            case R.id.btn_return:
                onBackPressed();
                break;
        }
        updateBtnPlayOrPause();
    }

    private void updateBtnPlayOrPause() {
        if (MusicService.isPlaying) {
            iv_play.setImageResource(R.drawable.play);
        } else {
            iv_play.setImageResource(R.drawable.pause);
        }
    }

    /**
     * 初始化视图
     */
    private void initView() {
        iv_pre = findViewById(R.id.iv_pre);
        iv_play = findViewById(R.id.iv_play);
        iv_next = findViewById(R.id.iv_next);
        btn_return = findViewById(R.id.btn_return);
        tv_cur_time = findViewById(R.id.tv_cur_time);
        tv_total_time = findViewById(R.id.tv_total_time);
        seekbar = findViewById(R.id.seekbar);
        iv_pre.setOnClickListener(this);
        iv_play.setOnClickListener(this);
        iv_next.setOnClickListener(this);
        btn_return.setOnClickListener(this);
    }

    /**
     * 格式播放时间
     */
    private String formatTime(int time) {
        int miao = (time /= 1000);
        int minute = miao / 60;
        int second = miao % 60;
        return String.format("%02d:%02d", minute, second);
    }

    /**
     * 按钮:播放音乐的广播
     */
    public void playMusic() {
        Intent intent = new Intent();
        intent.setAction(MusicService.ACTION);
        Bundle bundle = new Bundle();
        bundle.putInt(MusicService.BTN_STATE, MusicService.PLAY_STATE);
        intent.putExtras(bundle);
        sendBroadcast(intent);
    }

    /**
     * 按钮:下一首音乐的广播
     */
    public void nextMusic() {
        Intent intent = new Intent();
        intent.setAction(MusicService.ACTION);
        Bundle bundle = new Bundle();
        bundle.putInt(MusicService.BTN_STATE, MusicService.NEXT_MUSIC_STATE);
        intent.putExtras(bundle);
        sendBroadcast(intent);
    }

    /**
     * 按钮:上一首音乐的广播
     */
    public void preMusic() {
        Intent intent = new Intent();
        intent.setAction(MusicService.ACTION);
        Bundle bundle = new Bundle();
        bundle.putInt(MusicService.BTN_STATE, MusicService.PRE_MUSIC_STATE);
        intent.putExtras(bundle);
        sendBroadcast(intent);
    }
}
public class MusicService extends Service {

    private int mPosition;
    //静态存储上次音乐条目的位置
    static int savePosition;
    private MediaPlayer mMediaPlayer;
    //静态存储上次音乐条目的MediaPlayer
    static MediaPlayer mPreMediaPlayer;
    private ArrayList mMusicList;
    static boolean isPlaying = true;

    //动态广播的Action
    public static String ACTION = "action";
    //按钮点击状态标识符
    public static String BTN_STATE = "btn_state";
    public static final int PLAY_STATE = 0, NEXT_MUSIC_STATE = 1, PRE_MUSIC_STATE = 2;

    private static final String CHANNEL_ID = "1";
    private static final String CHANNEL_NAME = "MyChannel";
    private static final int NOTIFICATION_ID = 2;

    private RemoteViews remoteViews;
    private Notification notification;

    public MusicService() {
    }

    //onCreate()只被执行一次,因此用来做初始化
    @Override
    public void onCreate() {
        super.onCreate();
        mMusicList = MusicList.getMusicData(this);
        //在MusicService刚刚启动的时候就注册了一个广播,为的是让它来接收到在其他页面点击了上一曲、下一曲、暂停/播放等按钮时,来做相应的处理
        //因此,其他页面中点击上一曲、下一曲、暂停、播放按钮时都需要向MusicService发送广播通知,让它来更新音乐
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION);
        registerReceiver(receiver, intentFilter);
    }

    class MusicBinder extends Binder {

        public MusicService getService() {
            return MusicService.this;
        }
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mPosition = intent.getExtras().getInt("position", -1);
        initNotification();
        if (mPreMediaPlayer == null || mPosition != savePosition) {
            playMusic(mPosition);
        } else {
            mMediaPlayer = mPreMediaPlayer;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 初始化Notification(系统默认UI)
     * 这里是谷歌官方使用步骤:
     * A:要开始,您需要使用notificationCompat.builder对象设置通知的内容和通道。
     * B: 在Android 8.0及更高版本上传递通知之前,必须通过将NotificationChannel实例传递给CreateNotificationChannel(),在系统中注册应用程序的通知通道。
     * C: 每个通知都应该响应tap,通常是为了在应用程序中打开与通知对应的活动。为此,必须指定用PendingIntent对象定义的内容意图,并将其传递给setContentIntent()。
     * D: 若要显示通知,请调用notificationManagerCompat.notify(),为通知传递唯一的ID以及notificationCompat.builder.build()的结果。
     */
    private void initNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        remoteViews = new RemoteViews(getPackageName(), R.layout.notification);
        remoteViews.setTextViewText(R.id.tv_music_name, mMusicList.get(mPosition).getName());
        remoteViews.setOnClickPendingIntent(R.id.iv_pre_music, getPendingIntent(this, DetailActivity.NOTIFICATION_PRE_MUSIC));
        remoteViews.setOnClickPendingIntent(R.id.iv_play_pause, getPendingIntent(this, DetailActivity.NOTIFICATION_PLAY));
        remoteViews.setOnClickPendingIntent(R.id.iv_next_music, getPendingIntent(this, DetailActivity.NOTIFICATION_NEXT_MUSIC));
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.drawable.music)
                .setContent(remoteViews)
                .setOngoing(true)
                .setContentIntent(pendingIntent);
        notification = builder.build();
        //两种方法都可以,这里使用兼容的NotificationManager
        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
        notificationManagerCompat.notify(NOTIFICATION_ID, notification);
//        NotificationManager manager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
//        manager.notify(NOTIFICATION_ID,builder.build());
    }

    private PendingIntent getPendingIntent(Context context, int state) {
        Intent intent = new Intent();
        intent.setAction(DetailActivity.NOTIFICATION_ACTION);
        Bundle bundle = new Bundle();
        bundle.putInt(DetailActivity.NOTIFICATION_BTN_STATE, state);
        intent.putExtras(bundle);
        PendingIntent pendingIntent = null;
        switch (state) {
            case DetailActivity.NOTIFICATION_PRE_MUSIC:
                pendingIntent = PendingIntent.getBroadcast(context,1,intent,PendingIntent.FLAG_UPDATE_CURRENT);
                break;
            case DetailActivity.NOTIFICATION_PLAY:
                pendingIntent = PendingIntent.getBroadcast(context,2,intent,PendingIntent.FLAG_UPDATE_CURRENT);
                break;
            case DetailActivity.NOTIFICATION_NEXT_MUSIC:
                pendingIntent = PendingIntent.getBroadcast(context,3,intent,PendingIntent.FLAG_UPDATE_CURRENT);
                break;
        }
        return pendingIntent;
    }

    /**
     * 更新Notification
     */
    public void updateNotification() {
        remoteViews.setImageViewResource(R.id.iv_play_pause, isPlaying ? R.drawable.pause : R.drawable.play);
        remoteViews.setTextViewText(R.id.tv_music_name, mMusicList.get(mPosition).getName());
        NotificationManagerCompat manager = NotificationManagerCompat.from(this);
        manager.notify(NOTIFICATION_ID, notification);
    }

    /**
     * 广播接收器:接收playMusic()、nextMusic()、preMusic()的动态广播
     */
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(ACTION)) {
                switch (intent.getIntExtra(BTN_STATE, -1)) {
                    case PLAY_STATE:
                        play();
                        break;
                    case PRE_MUSIC_STATE:
                        next(-1);
                        break;
                    case NEXT_MUSIC_STATE:
                        next(1);
                        break;
                    default:
                        Toast.makeText(context, "系统出错,请稍后重试!", Toast.LENGTH_SHORT).show();
                        break;
                }
                updateNotification();
            }
        }
    };

    /**
     * 播放音乐
     */
    public void playMusic(int position) {
        mMediaPlayer = new MediaPlayer();
        if (mPreMediaPlayer != null) {
            mPreMediaPlayer.stop();
            mPreMediaPlayer.release();
        }
        mPreMediaPlayer = mMediaPlayer;
        savePosition = position;
        try {
            mMediaPlayer.reset();
            mMediaPlayer.setDataSource(mMusicList.get(position).getUrl());
            mMediaPlayer.prepare();
            mMediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mPosition += 1;
                mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
                playMusic(mPosition);
                Toast.makeText(getApplicationContext(), "自动为您切换下一首:" + mMusicList.get(mPosition).getName(), Toast.LENGTH_SHORT).show();
                updateNotification();
            }
        });

    }

    /**
     * 按钮点击:播放音乐
     */
    public void play() {
        if (mMediaPlayer.isPlaying()) {
            mMediaPlayer.pause();
            isPlaying = false;
        } else {
            mMediaPlayer.start();
            isPlaying = true;
        }
    }

    /**
     * 按钮点击:下一首
     */
    public void next(int offset) {
        mPosition += offset;
        mPosition = (mMusicList.size() + mPosition) % mMusicList.size();
        playMusic(mPosition);
        isPlaying = true;
    }

    /**
     * 设置音乐播放的进度
     */
    public void seekTo(int progress) {
        mMediaPlayer.seekTo(progress);
    }

    /**
     * 获取当前音乐的名字
     */
    public String getName() {
        return mMusicList.get(mPosition).getName();
    }

    /**
     * 获取当前音乐的播放时间
     */
    public int getTime() {
        return mMusicList.get(mPosition).getTime();
    }

    /**
     * 获取当前播放位置
     */
    public int getCurrent() {
        return mMediaPlayer.getCurrentPosition();
    }
}

这里有个巨坑,就是remoteViews.setOnClickPendingIntent的第二个参数传入的是PendingIntent,因为PendingIntent中需要传入一个Intent,我们就是通过这个Intent来发送按钮的广播。我尝试着用三个不同的Intnet,每一个PendingIntent包含一个Intent,这样三个PendingIntent就不一样了。原理确实是这样的,但是按照我们常规的,对PendingIntent的参数设置就是(this,0,intent,0),发现效果出不来。于是我百度了一些资料,基本都说是第四个参数的原因,于是我将第四个参数改为 PendingIntent.FLAG_UPDATE_CURRENT,发现还是不行。经过一系列的注释、Log,还是没发现问题。最后突然想到,PendingIendingInten的第二个参数我们还没用到,一直传入0是什么原因,于是我试着更改下三个PendingIntent用不同的reqestCode,结果就成了。第二个参数的翻译过来的意思就是请求码,请求码不一样系统才认为是不一样的PendingIntent。

有任何疑问欢迎留言提问。

你可能感兴趣的:(Android实现音乐播放器,Service后台播放&Notification控制播放状态)