Android音乐播放器---实现Notification控制音乐播放

时间:2021-08-19 10:14:30

最近一直在学习Android服务(Service)、广播接收者(BroadcastReceiver)、通知(Notification)的用法,趁着刚学完的热乎劲儿,做了个音乐播放器来练手。在此,我就把自己在编写过程中对服务、广播接收者、通知的理解及通知栏实现音乐播放功能的实现,和各位喜欢Android编程的基友们一起分享一下。(第一次写技术文章,想想还真有点小紧张呢!以下内容均为个人理解,所以说的不到位的地方,还请基友们指出哦)。废话不多说,直接进入主题。

为什么选择音乐播放器来作为服务、广播接收者、通知的练习项目呢?

想必各位初学Android的基友都有这样的经历,在我们接触服务之前,应用程序的运行都是依托于一个操作界面(即Activity),一旦离开这个界面,程序的操作也就随着终止了。然而,有时我们还有这样的需求,当离开一个操作界面时,程序任然需要继续执行(即后台运行),这时,我们就需要Android的另一大组件Service来实现这样的操作了。Service组件区别与Activity的一个最主要的特征他的运行不依赖界面,他就像一个幕后工作者,默默的运行在应用程序的背后,当应用程序界面关闭时,程序的运行就要靠他了,例如音乐的后台播放。

通知(Notification)为音乐播放器实现了那些功能?

后台播放的音乐,要实现对其播放的控制,可以通过通知栏工具来实现,且看下图:     Android音乐播放器---实现Notification控制音乐播放

广播接收者(BroadCastReceiver)在音乐播放器中有什么作用?

老实说,对这个组件的理解,之前我一直都有点稀里糊涂,很多基友初接触时,可能和我一样,用他来实现了短信监听的功能,即接受系统内置的广播。音乐播放器的编写让我重新认识了广播接收者的运作机制。在Android应用中,游离着各种各样的由不同应用发出广播,广播的标记信息一般附着在Intent对象上(说白了就是在新建Intent对象时,给他一个字符串作为参数,这个参数可以作为广播接收的标记),随着Intent对象的传递,广播也就被发出了。此时,要想接收这个广播,我们只需要新建一个广播类,并定制带有这个标记的广播,在这个广播被发出时,我们就能接收这条广播,并完成相应的操作了。

服务、广播接收者、通知共同实现通知栏音乐播放控制: 准备工作: 定义接口,用于传递音乐的播放信息: [java] view plaincopy
  1. public interface IMusic {  
  2.     public void moveon();//继续  
  3.     public void pause();//暂停  
  4.     public void stop();//停止  
  5.     public void nextSong();//下一曲  
  6.     public void lastSong();//上一曲  
  7. }  

定义Application类,用于传递全局变量:

[java] view plaincopy
  1. public class Mp3Application extends Application {  
  2.     public List<Song> songsList;//当前播放列表  
  3.     public int songItemPos;//当前播放音乐在列表中的位置  
  4.     public NotificationManager notManager;  
  5.     public IMusic music;  
  6. }  


定义Service类,用于控制音乐播放: [java] view plaincopy
  1. public class PlayerService extends Service {  
  2.     private MediaPlayer mp;  
  3.     private Mp3Application application;  
  4.     private List<Song> songs;  
  5.     private int songItemPos;  
  6.   
  7.     @Override  
  8.     public void onCreate() {  
  9.         super.onCreate();  
  10.         application = (Mp3Application) getApplication();  
  11.         songs = application.songsList;  
  12.     }  
  13.   
  14.     @Override  
  15.     public IBinder onBind(Intent intent) {  
  16.         play(songItemPos);  
  17.         return new MusicListener();  
  18.     }  
  19.   
  20.     public void play(int position) {  
  21. <span style="white-space:pre">        </span>//传入播放位置在列表中的边界  
  22.         if(position>=0){  
  23.             position=position % application.songsList.size();  
  24.         }  
  25.         else{  
  26.             position = application.songsList.size()+ position;  
  27.         }  
  28.   
  29.         application.songItemPos = position;//在全局变量中标记当前播放位置  
  30.         Song currentSong = songs.get(position);//获取当前播放音乐  
  31.         Uri musicTableForSD = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  
  32.         Uri uri = Uri.withAppendedPath(musicTableForSD,  
  33.                 "/" + currentSong.getId());//初始化mp对象  
  34.         mp = MediaPlayer.create(this, uri);  
  35.   
  36.         System.out.println(currentSong.getName());  
  37.   
  38.         try {  
  39.             mp.start();  
  40.   
  41.         } catch (Exception e) {  
  42.             e.printStackTrace();  
  43.         }  
  44.     }  
  45.   
  46.     @Override  
  47.     public boolean onUnbind(Intent intent) {  
  48.         mp.stop();  
  49.         mp.release();  
  50.         mp = null;  
  51.         return super.onUnbind(intent);  
  52.     }  
  53.   
  54.     @Override  
  55.     public void onDestroy() {  
  56.         super.onDestroy();  
  57.     }  
  58. <span style="white-space:pre">    </span>//实现IMusic接口,用于接收播放信息  
  59.     private class MusicListener extends Binder implements IMusic {  
  60.   
  61.         @Override  
  62.         public void moveon() {  
  63.             if (mp != null) {  
  64.                 mp.start();  
  65.             }  
  66.         }  
  67.   
  68.         @Override  
  69.         public void pause() {  
  70.             if (mp != null) {  
  71.                 mp.pause();  
  72.             }  
  73.         }  
  74.   
  75.         @Override  
  76.         public void stop() {  
  77.             if (mp != null) {  
  78.                 mp.stop();  
  79.                 mp.release();  
  80.                 mp = null;  
  81.             }  
  82.         }  
  83.   
  84.         @Override  
  85.         public void nextSong() {  
  86.             if (mp != null) {  
  87.                 mp.stop();  
  88.                 play(++application.songItemPos);  
  89.             }  
  90.         }  
  91.   
  92.         @Override  
  93.         public void lastSong() {  
  94.             if (mp != null) {  
  95.                 mp.stop();  
  96.                 play(--application.songItemPos);  
  97.             }  
  98.         }  
  99.     }  
  100.   
  101. }  

初始化Notification,该部分主要在Activity中完成: Notification的布局效果(即contentView): Android音乐播放器---实现Notification控制音乐播放

[java] view plaincopy
  1. public class MainActivity extends Activity {  
  2.     .....  
  3.     private RemoteViews contentView;  
  4.   
  5.     private Notification notification;  
  6.     private Mp3Application application;  
  7.     private IMusic music;  
  8.     .....  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.   
  15.         ......         
  16.   
  17.         // 初始化通知栏播放控件  
  18.         initNotificationBar();  
  19.   
  20.         ......  
  21.     }  
  22.   
  23.     public void initNotificationBar() {  
  24.         notification = new Notification();  
  25.         //初始化通知  
  26.         notification.icon = R.drawable.ic_launcher;  
  27.         contentView = new RemoteViews(getPackageName(),  
  28.                 R.layout.notification_control);  
  29.         notification.contentView = contentView;  
  30.   
  31.         Intent intentPlay = new Intent("play");//新建意图,并设置action标记为"play",用于接收广播时过滤意图信息  
  32.         PendingIntent pIntentPlay = PendingIntent.getBroadcast(this0,  
  33.                 intentPlay, 0);  
  34.         contentView.setOnClickPendingIntent(R.id.bt_notic_play, pIntentPlay);//为play控件注册事件  
  35.   
  36.         Intent intentPause = new Intent("pause");  
  37.         PendingIntent pIntentPause = PendingIntent.getBroadcast(this0,  
  38.                 intentPause, 0);  
  39.         contentView.setOnClickPendingIntent(R.id.bt_notic_pause, pIntentPause);  
  40.   
  41.         Intent intentNext = new Intent("next");  
  42.         PendingIntent pIntentNext = PendingIntent.getBroadcast(this0,  
  43.                 intentNext, 0);  
  44.         contentView.setOnClickPendingIntent(R.id.bt_notic_next, pIntentNext);  
  45.   
  46.         Intent intentLast = new Intent("last");  
  47.         PendingIntent pIntentLast = PendingIntent.getBroadcast(this0,  
  48.                 intentLast, 0);  
  49.         contentView.setOnClickPendingIntent(R.id.bt_notic_last, pIntentLast);  
  50.   
  51.         Intent intentCancel = new Intent("cancel");  
  52.         PendingIntent pIntentCancel = PendingIntent.getBroadcast(this0,  
  53.                 intentCancel, 0);  
  54.         contentView  
  55.                 .setOnClickPendingIntent(R.id.bt_notic_cancel, pIntentCancel);  
  56.         notification.flags = notification.FLAG_NO_CLEAR;//设置通知点击或滑动时不被清除  
  57.         application.notManager.notify(Const.NOTI_CTRL_ID, notification);//开启通知  
  58.           
  59.     }  
  60.   
  61. }  

定义BroadcastReceiver类,用于接收来自Notification的广播并控制音乐播放:
[java] view plaincopy
  1. public class Mp3Receiver extends BroadcastReceiver {  
  2.     private Mp3Application application;  
  3.     private IMusic music;  
  4.   
  5.     @Override  
  6.     public void onReceive(Context context, Intent intent) {  
  7.           
  8.         application = (Mp3Application) context.getApplicationContext();  
  9.         String ctrl_code = intent.getAction();//获取action标记,用户区分点击事件  
  10.           
  11.         music = application.music;//获取全局播放控制对象,该对象已在Activity中初始化  
  12.         if (music != null) {  
  13.             if ("play".equals(ctrl_code)) {  
  14.                 music.moveon();  
  15.                 System.out.println("play");  
  16.             } else if ("pause".equals(ctrl_code)) {  
  17.                 music.pause();  
  18.                 System.out.println("pause");  
  19.             } else if ("next".equals(ctrl_code)) {  
  20.                 music.nextSong();  
  21.                 System.out.println("next");  
  22.             } else if ("last".equals(ctrl_code)) {  
  23.                 music.lastSong();  
  24.                 System.out.println("last");  
  25.             }  
  26.         }  
  27.   
  28.         if ("cancel".equals(ctrl_code)) {  
  29.             application.notManager.cancel(Const.NOTI_CTRL_ID);  
  30.             System.exit(0);  
  31.         }  
  32.     }  
  33.   
  34. }  

最后别忘了在清单文件中声明广播类并定制广播哦:
[html] view plaincopy
  1. <application  
  2.     android:name="com.cxc.Mp3Player.util.Mp3Application"  
  3.     android:allowBackup="true"  
  4.     android:icon="@drawable/ic_launcher"  
  5.     android:label="@string/app_name"  
  6.     android:theme="@style/AppTheme" >  
  7.     <uses-library android:name="android.test.runner" >  
  8.     </uses-library>  
  9.   
  10.     ......  
  11.   
  12.     <receiver  
  13.         android:name="com.cxc.Mp3Player.receiver.Mp3Receiver"  
  14.         android:exported="true" >  
  15.         <intent-filter>  
  16.             <action android:name="play" /><!-- 定制play广播 -->  
  17.             <action android:name="last" /><!-- 定制last广播 -->  
  18.             <action android:name="next" /><!-- ...... -->  
  19.             <action android:name="pause" />  
  20.             <action android:name="cancel" />  
  21.         </intent-filter>  
  22.     </receiver>  
  23. </application>  
 大功告成!
为区分通知栏点击的是哪个控件,刚开始我是想通过在意图中通过putExtra()方法添加参数,来区分控件,但后来实现发现行不通。之后我便转换思路用action来区分,成功为不同控件实现了不同的点击响应。不知道区分点击事件还有没有其他方法,还请贴心的基友指出来哦!
另外,还有好基友可能还会碰见动态更新Notification布局内容的需求,各位可以试试NotificationManager类对象的notify()方法,参数可根据需要来添加。 以上博文希望能给各位正在学服务、广播、通知以及还在纠结通知栏实现音乐播放控制的好基友一些帮助。