代码已经托管到码云上,有兴趣的小伙伴可以下载看看
https://git.oschina.net/joy_yuan/MobilePlayer
先来一张目前的音乐播放器的效果图,当播放时,手机的状态通知栏也会有音乐信息显示。
这里可以看到有歌名、演唱者,还有歌曲的总时间,当前播放时间,当前播放进度,音乐暂停、下一首,上一首,音乐循环模式(单曲循环,顺序播放、循环播放)功能的实现。下一步就是把中间空白的部分填充歌词,然后做成根据进度显示歌词。
由于这次的内容有点多,是写了一天半的代码,讲的没那么细,具体的代码可以去下载源码看下。
一、要想做成即使推出播放器后(不是杀死进程),音乐播放器继续播放,那么就不能在activity里进行播放音乐,需要在service里播放音乐
1、aidl的使用,来连接activity和service。
这个aidl有点类似service的接口。
其实用来连接activity和service也可以用内部类IBindler 来做,具体的可以看我前面写的博客,四大组件之service,里面有详细写activity绑定service。
// IMusicPlayerService.aidl
package com.yuanlp.mobileplayer;
// Declare any non-default types here with import statements
interface IMusicPlayerService {
/**
* 根据对应位置打开音乐
* @param position
*/
void openAudio(int position);
/**
* 播放音乐
*/
void startAudio();
/**
* 暂停音乐
*/
void pauseAudio();
/**
* 停止音乐
*/
void stopAudio();
/**
* 得到当前播放进度
* @return
*/
int getCurrentPosition();
/**
* 得到总时长
* @return
*/
long getDuration();
/**
* 得到演唱者
* @return
*/
String getSinger();
/**
* 得到音乐大小
* @return
*/
long getSize();
/**
* 得到歌名
* @return
*/
String getSong();
/**
* 得到路径
* @return
*/
String getAudioPath();
/**
* 得到下一首
*/
void getNext();
/**
* 得到上一首
*/
void getPre();
/**
* 设置播放模式
* @param playMode
*/
void setPlayMode(int playMode);
/**
* 得到播放模式
*/
int getPlayMode();
/**
* 是否播放
*/
boolean isPlaying();
void seekTo(int progress);
}
2 、创建service,第一种可以直接写类,然后继承Service,需要手动重写oncreate()和onBind()方法,然后还要在AndroidManifext.xml里注册这个类;第二种,右键new一个service,这样就不用去重写方法,以及去注册了,因为IDE会自动帮我们做好。
package com.yuanlp.mobileplayer.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.RequiresApi;
import android.util.Log;
import com.yuanlp.mobileplayer.IMusicPlayerService;
import com.yuanlp.mobileplayer.R;
import com.yuanlp.mobileplayer.activity.AudioPlayerAcivity;
import com.yuanlp.mobileplayer.bean.MediaItem;
import com.yuanlp.mobileplayer.utils.CacheUtils;
import java.io.IOException;
import java.util.ArrayList;
public class MusicPlayerService extends Service {
private static final String TAG = "MusicPlayerService";
public static final String OPENAUDIOPLAYER = "com.yuanlp.mobilePlayer_openAudioPlayer";
private int position;
private ArrayList<MediaItem> medialist;
private MediaItem item;
private MediaPlayer mediaPlayer;
private NotificationManager manger; //状态栏通知管理器
public static final int REPEAT_NORMAL=1; //默认播放模式,即顺序播放
public static final int REPEAT_SINGLE=2; //单曲循环
public static final int REPEAT_ALL=3; //循环播放
private int playMode=REPEAT_NORMAL; //播放模式
public MusicPlayerService() {
}
@Override
public void onCreate() {
super.onCreate();
playMode=CacheUtils.getPlayMode(this,"playmode");
}
/**
* 在这里获取aidl的实例,需要在onBind()方法里返回这个实例。onBind()方法在activity
与service绑定时自动调用。
**/
private IMusicPlayerService.Stub stub=new IMusicPlayerService.Stub() {
MusicPlayerService service=MusicPlayerService.this;
@Override
public void openAudio(int position) throws RemoteException {
System.out.println("调取了内部的方法传递position为"+position);
service.openAudio(position);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void startAudio() throws RemoteException {
service.startAudio();
}
@Override
public void pauseAudio() throws RemoteException {
service.pauseAudio();
}
@Override
public void stopAudio() throws RemoteException {
service.stopAudio();
}
@Override
public int getCurrentPosition() throws RemoteException {
return service.getCurrentPosition();
}
@Override
public long getDuration() throws RemoteException {
return service.getDuration();
}
@Override
public String getSinger() throws RemoteException {
return service.getSinger();
}
@Override
public long getSize() throws RemoteException {
return service.getSize();
}
@Override
public String getSong() throws RemoteException {
return service.getSong();
}
@Override
public String getAudioPath() throws RemoteException {
return service.getAudioPath();
}
@Override
public void getNext() throws RemoteException {
service.getNext();
}
@Override
public void getPre() throws RemoteException {
service.getPre();
}
@Override
public void setPlayMode(int playMode) throws RemoteException {
service.setPlayMode(playMode);
}
@Override
public int getPlayMode() throws RemoteException {
return service.getPlayMode();
}
/**
* 是否播放
*/
@Override
public boolean isPlaying() throws RemoteException {
return service.isPlaying();
}
//把进度条调整到progress的位置
public void seekTo(int progress){
service.seekTo(progress);
}
};
// public class Mybinder extends Binder{
// public MusicPlayerService getService(){
// return MusicPlayerService.this;
// }
// }
//
// public Mybinder binder=new Mybinder();
// @Override
// public IBinder onBind(Intent intent) {
//
// return binder;
//
// }
@Override
public IBinder onBind(Intent intent) {
System.out.println("绑定了服务------------");
//当绑定时,获取activity通过intent传过来的序列化的list数据。
medialist= (ArrayList<MediaItem>) intent.getSerializableExtra("medialist");
System.out.println("获取服务里的数据。。。"+medialist.size());
return stub;
}
/**
* 根据对应位置打开音乐
* @param position
*/
private void openAudio(int position){
this.position=position;
Log.d(TAG, "openAudio:当前位置 "+position);
item=medialist.get(position);
Log.d(TAG, "openAudio: 当前位置对应的歌名"+item.getName());
if (mediaPlayer!=null){
mediaPlayer.reset();
mediaPlayer=null;
}
try {
mediaPlayer=new MediaPlayer();
mediaPlayer.setOnPreparedListener(new MyOnPreparedListener());
mediaPlayer.setOnCompletionListener(new MyOnCompletionListener());
mediaPlayer.setOnErrorListener(new MyOnErrorListener());
mediaPlayer.setDataSource(item.getData());
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 播放出错时回调
*/
class MyOnErrorListener implements MediaPlayer.OnErrorListener {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
getNext();
return true;
}
}
/**
* 播放完成时
*/
class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
@Override
public void onCompletion(MediaPlayer mp) {
getNext();
}
}
/**
* 准备好播放时回调
*/
class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onPrepared(MediaPlayer mp) {
startAudio();
//在这里发送广播,通知activity,播放的进度、音乐名称、歌唱家等信息
notifyChange(OPENAUDIOPLAYER);
}
}
/**
* 发送广播通知activity
* @param openaudioplayer
*/
private void notifyChange(String openaudioplayer) {
Intent intent=new Intent(openaudioplayer);
sendBroadcast(intent);
}
/**
* 播放音乐
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void startAudio(){
mediaPlayer.start();
manger= (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //获取系统的通知服务
//Notification notifaction= null;
Intent intent=new Intent(this, AudioPlayerAcivity.class); //点击通知栏后,打开activity音乐界面
Bundle bundle=new Bundle();
bundle.putSerializable("mediallist",medialist);
intent.putExtras(bundle); //把list数据回传给activity,不然会报错
intent.putExtra("Notifaction",true); //标识来自状态
PendingIntent pendingintent=PendingIntent.getActivity(this,1,intent,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notifaction = new Notification.Builder(this)
.setSmallIcon(R.drawable.notification_music_playing)
.setContentTitle("原立鹏音乐播放器") //状态通知栏标题
.setContentText("正在播放:"+getSong()) //状态通知栏内容
.setContentIntent(pendingintent)
.build(); //这个一定要写最后面,不然会出错
manger.notify(1,notifaction); //显示状态栏
}
/**
* 暂停音乐
*/
public void pauseAudio(){
mediaPlayer.pause();
//停止播放时取消状态栏
manger.cancel(1);
}
/**
* 停止音乐
*/
public void stopAudio(){
mediaPlayer.stop();
}
/**
* 得到当前播放进度
* @return
*/
public int getCurrentPosition(){
return mediaPlayer.getCurrentPosition();
}
/**
* 得到总时长
* @return
*/
public long getDuration(){
return mediaPlayer.getDuration();
}
/**
* 得到演唱者
* @return
*/
public String getSinger(){
return item.getArtist();
}
/**
* 得到音乐大小
* @return
*/
public long getSize(){
return item.getSize();
}
/**
* 得到歌名
* @return
*/
public String getSong(){
return item.getName();
}
/**
* 得到路径
* @return
*/
public String getAudioPath(){
return item.getData();
}
/**
* 得到下一首
*/
public void getNext(){
}
/**
* 得到上一首
*/
public void getPre(){
}
/**
* 设置播放模式,并将播放模式写到sharepreferences里,记忆用户播放模式
* @param playMode
*/
public void setPlayMode(int playMode){
this.playMode=playMode;
CacheUtils.putPlayMode(this,"playmode",playMode);
}
/**
* 得到播放模式
*/
public int getPlayMode(){
return playMode;
}
public boolean isPlaying(){
return mediaPlayer.isPlaying();
}
public void seekTo(int progress){
mediaPlayer.seekTo(progress);
}
}
二、在activity里,接收audiopager传过来的list数据,并绑定服务,在绑定服务时,通过intent将list数据传给service;在开始播放音乐时,接收service发出的广播,来更新页面显示的歌名、时间等信息,并在handler里每秒刷新下时间。
package com.yuanlp.mobileplayer.activity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.yuanlp.mobileplayer.IMusicPlayerService;
import com.yuanlp.mobileplayer.R;
import com.yuanlp.mobileplayer.bean.MediaItem;
import com.yuanlp.mobileplayer.service.MusicPlayerService;
import com.yuanlp.mobileplayer.utils.Utils;
import java.util.ArrayList;
import static android.content.ContentValues.TAG;
/**
* Created by 原立鹏 on 2017/7/27.
*/
public class AudioPlayerAcivity extends Activity implements View.OnClickListener {
private static final int UPDATE_SEEKBAR = 0; //更新进度条时的handler消息
private ImageView iv_play_icon;
private int position; //获取从audiopager中从intent传过来的位置信息
private ArrayList<MediaItem> medialist;
private IMusicPlayerService mservice;
private ImageView ivPlayIcon;
private TextView tvSigner;
private TextView tvSong;
private TextView tvTime;
private SeekBar seekbarAudio;
private Button btAudioMode;
private Button btAudioPre;
private Button btAudioStartPause;
private Button btAudioNext;
private Button btLyrc;
private boolean isPlaying;
private MyBroadcast broadcast; //广播类实例
private Utils utils;
private boolean isNotifaction; //判断是否是从notifaction那里传过来的intent
private int currentposition;
/**
* Find the Views in the layout<br />
* <br />
* Auto-created on 2017-07-28 08:54:55 by Android Layout Finder
* (http://www.buzzingandroid.com/tools/android-layout-finder)
*/
private void findViews() {
System.out.println("初始化数据");
ivPlayIcon = (ImageView)findViewById( R.id.iv_play_icon );
tvSigner = (TextView)findViewById( R.id.tv_signer );
tvSong = (TextView)findViewById( R.id.tv_song );
tvTime = (TextView)findViewById( R.id.tv_time );
seekbarAudio = (SeekBar)findViewById( R.id.seekbar_audio );
btAudioMode = (Button)findViewById( R.id.bt_audio_mode );
btAudioPre = (Button)findViewById( R.id.bt_audio_pre );
btAudioStartPause = (Button)findViewById( R.id.bt_audio_start_pause );
btAudioNext = (Button)findViewById( R.id.bt_audio_next );
btLyrc = (Button)findViewById( R.id.bt_lyrc );
btAudioMode.setOnClickListener( this );
btAudioPre.setOnClickListener( this );
btAudioStartPause.setOnClickListener( this );
btAudioNext.setOnClickListener( this );
btLyrc.setOnClickListener( this );
}
/**
* Handle button click events<br />
* <br />
* Auto-created on 2017-07-28 08:54:55 by Android Layout Finder
* (http://www.buzzingandroid.com/tools/android-layout-finder)
*/
@Override
public void onClick(View v) {
if ( v == btAudioMode ) {
// Handle clicks for btAudioMode
getPlayMode(); //设置播放模式
} else if ( v == btAudioPre ) {
// Handle clicks for btAudioPre
position-=1;
if (position<0){
position=medialist.size()-1;
}
try {
MediaItem item=medialist.get(position);
tvSigner.setText(item.getArtist());
tvSong.setText(item.getName());
mservice.openAudio(position);
} catch (RemoteException e) {
e.printStackTrace();
}
} else if ( v == btAudioStartPause ) {
try {
isPlaying=mservice.isPlaying();
if (isPlaying){ //表示此时在播放,应该把图片切换为播放界面,然后暂停播放
mservice.pauseAudio();
btAudioStartPause.setBackgroundResource(R.drawable.bt_audio_start_selector);
}else{
mservice.startAudio();
btAudioStartPause.setBackgroundResource(R.drawable.bt_audio_pause_selector);
}
} catch (RemoteException e) {
e.printStackTrace();
}
// Handle clicks for btAudioStartPause
} else if ( v == btAudioNext ) {
// Handle clicks for btAudioNext
position+=1;
if (position>=medialist.size()){
position=0;
}
try {
MediaItem item=medialist.get(position);
tvSigner.setText(item.getArtist());
tvSong.setText(item.getName());
mservice.openAudio(position);
} catch (RemoteException e) {
e.printStackTrace();
}
} else if ( v == btLyrc ) {
// Handle clicks for btLyrc
}
}
private void getPlayMode() {
try {
int playMode=mservice.getPlayMode();
if (playMode==MusicPlayerService.REPEAT_NORMAL){
playMode=MusicPlayerService.REPEAT_SINGLE; //切换为单曲循环,并把按钮图片切换
}else if (playMode==MusicPlayerService.REPEAT_SINGLE){
playMode=MusicPlayerService.REPEAT_ALL; //如果为单曲循环,切换为循环播放
}else if (playMode==MusicPlayerService.REPEAT_ALL){
playMode=MusicPlayerService.REPEAT_NORMAL; //如果为循环播放,则切换为顺序播放,播放全部后,停止播放
}else {
playMode=MusicPlayerService.REPEAT_NORMAL; //如果为循环播放,则切换为顺序播放,播放全部后,停止播放
}
mservice.setPlayMode(playMode);
setPlayMode();
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void setPlayMode() {
try {
int playMode=mservice.getPlayMode();
if (playMode==MusicPlayerService.REPEAT_NORMAL){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_normal_selector);
Toast.makeText(this,"顺序播放",Toast.LENGTH_SHORT).show();
}else if (playMode==MusicPlayerService.REPEAT_SINGLE){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_single_selector);
Toast.makeText(this,"单曲循环",Toast.LENGTH_SHORT).show();
}else if (playMode==MusicPlayerService.REPEAT_ALL){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_all_selector);
Toast.makeText(this,"循环播放",Toast.LENGTH_SHORT).show();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void checkPlayMode(){
try {
int playMode=mservice.getPlayMode();
if (playMode==MusicPlayerService.REPEAT_NORMAL){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_normal_selector);
}else if (playMode==MusicPlayerService.REPEAT_SINGLE){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_single_selector);
}else if (playMode==MusicPlayerService.REPEAT_ALL){
btAudioMode.setBackgroundResource(R.drawable.bt_audio_mode_all_selector);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_SEEKBAR:
//1 得到当前进度
try {
int currentPosition=mservice.getCurrentPosition();
seekbarAudio.setProgress(currentPosition);
String time=utils.stringForTime(currentPosition)+"/"+utils.stringForTime((int) mservice.getDuration());
tvTime.setText(time);
handler.removeMessages(UPDATE_SEEKBAR);
/**
* 每秒刷新下进度条
*/
handler.sendEmptyMessageDelayed(UPDATE_SEEKBAR,1000);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: 音乐播放activity被创建了");
System.out.println("音乐播放activity被创建了");
setContentView(R.layout.activity_audioplayer);
utils=new Utils();
/**
* 初始化音乐界面的图片变化的布局
*/
initView();
/**
* 实例化布局里的各个控件
*/
findViews();
//注册广播
registerBorader();
/**
* 根据位置获取音乐
*/
getData();
/**
* 绑定服务并启动服务
*/
bindAndStartService();
seekbarAudio.setOnSeekBarChangeListener(new MyOnSeekBarChangeListener());
}
class MyOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser){
try {
mservice.seekTo(progress);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
private void bindAndStartService() {
System.out.println("绑定服务 bindService。。。。。。");
Intent intent=new Intent(this, MusicPlayerService.class);
//intent.setAction("com.yuanlp.mobilePlayer");
Bundle bundle=new Bundle();
bundle.putSerializable("medialist",medialist);
intent.putExtras(bundle);
startService(intent); //不至于多次实例化服务对象
//System.out.println("数量是多少"+medialist.size());
bindService(intent,conn,BIND_AUTO_CREATE);
System.out.println("绑定后,再启动服务");
// Bundle bundle=new Bundle();
startService(intent); //不至于多次实例化服务对象
// bundle.putSerializable("medialist",medialist);
// intent.putExtras(bundle);
}
/**
* 与服务创建连接
*/
private ServiceConnection conn=new ServiceConnection() {
/**
* 当连接成功时,回调方法
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("成功连接。。。");
mservice=IMusicPlayerService.Stub.asInterface(service);
if (mservice!=null){
try {
if (!isNotifaction){ //不是 从状态栏里过来的,是从列表过来的,那么重新播放
System.out.println("成功连接后,service不为空");
mservice.openAudio(position);
}else{
showViewData();
}
// MediaItem item=medialist.get(position);
// String artist=item.getArtist();
// String song=item.getName();
// tvSigner.setText(artist);
// tvSong.setText(song);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 当连接失败时回调方法
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("连接失败。。。");
try {
if (mservice!=null){
mservice.stopAudio();
mservice=null;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
private void getData() {
System.out.println("获取数据------");
medialist=new ArrayList<MediaItem>();
medialist= (ArrayList<MediaItem>) getIntent().getSerializableExtra("mediallist");
System.out.println("当前的list数量------------------------"+medialist.size());
//得到intent的数据
isNotifaction = getIntent().getBooleanExtra("Notifaction",false);
if (!isNotifaction){
position = getIntent().getIntExtra("position", 0);
}
//currentposition = getIntent().getIntExtra("currentPosition",0);
System.out.println("传到activity里获取的当前进度isNotifaction是------------"+isNotifaction);
}
private void initView() {
System.out.println("初始化跳动的图片");
iv_play_icon= (ImageView) findViewById(R.id.iv_play_icon);
iv_play_icon.setBackgroundResource(R.drawable.animation_list);
AnimationDrawable rocketAnimation= (AnimationDrawable) iv_play_icon.getBackground();
rocketAnimation.start();
}
/**
* 广播接收,接受来自service里开始播放后发送的广播
*/
class MyBroadcast extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
/**
* 接收到广播后,显示歌曲名、歌曲时间等信息
*/
showViewData();
checkPlayMode();
}
}
/**
* 接收到广播后,显示歌曲名、歌曲时间等信息
*/
private void showViewData() {
try {
tvSigner.setText(mservice.getSinger());
tvSong.setText(mservice.getSong());
//设置进度条
seekbarAudio.setMax((int) mservice.getDuration());
handler.sendEmptyMessage(UPDATE_SEEKBAR);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 注册广播,接收service发送的广播
*/
private void registerBorader() {
System.out.println("注册广播");
broadcast=new MyBroadcast();
IntentFilter intentfilter=new IntentFilter();
intentfilter.addAction(MusicPlayerService.OPENAUDIOPLAYER);
registerReceiver(broadcast,intentfilter);
}
/**
* 在onDestroy里取消注册广播
*/
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null); //推出activity时,移除所有消息
if (broadcast!=null){
unregisterReceiver(broadcast);
broadcast=null; //这里为什么把广播设置为null,因为取消注册后,垃圾回收器不一定马上回收广播,如果设置为null,那么会优先回收这个
}
super.onDestroy();
}
}
转载于:https://blog.51cto.com/cm0425/1951857