标签:手机影音 第十五天 利用service实现后台播放音乐 在通知栏显示当前音乐信息。
代码已经托管到码云上,有兴趣的小伙伴可以下载看看
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(); } }
本文出自 “YuanGuShi” 博客,请务必保留此出处http://cm0425.blog.51cto.com/10819451/1951857
手机影音第十五天,利用service实现后台播放音乐,在通知栏显示当前音乐信息等
标签:手机影音 第十五天 利用service实现后台播放音乐 在通知栏显示当前音乐信息。
原文地址:http://cm0425.blog.51cto.com/10819451/1951857