Android实战 – 音心播放器 (通知实现音乐的播放/暂停/下一曲控制)
最后更新于:2022-04-01 10:52:45
## 1.背景
**通知 -> Service :**
上一篇的MusicService 中提高了通知是Service的前台显示,这篇将介绍通知(MusicNotification).通知在这里有四个作用:
(1)显示当前音乐的信息
(2)播放/暂停音乐
(3)下一曲播放音乐
(4)关闭通知栏(实际上也是停止音乐播放并关闭Service)
**Service -> 通知 :**
通知和Service是紧密相连的,当Service结束的时候,取消通知;当Service启动的时候,初始化通知/创建通知;当音乐状态发生改变的时候,更新通知;如下图所示 :
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764c1ff57.jpg)
## 2.通知实现
在这里单独的将通知单独的类中,方便使用;没有使用到通知的震动提醒/铃声提醒/闪光灯提醒,如果需要使用,可以看博客 :
[Android-Notification (通知实现)](http://blog.csdn.net/lablenet/article/details/47999961).
### (1)通知布局实现
~~~
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp" >
<ImageView
android:id="@+id/iv_notification_logo"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_margin="5dp"
android:src="@drawable/logo" />
<TextView
android:id="@+id/tv_nofitication_singname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_toRightOf="@+id/iv_notification_logo"
android:maxLines="1"
android:textSize="12sp"
android:text="@string/item_notification_singname"
android:textColor="@color/text_color_whrit" />
<TextView
android:id="@+id/tv_nofitication_singer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:textSize="10sp"
android:layout_toRightOf="@+id/iv_notification_logo"
android:text="@string/item_notifiaction_singer"
android:textColor="@color/bg_color" />
<ImageView
android:id="@+id/iv_nofitication_kzhi_play"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:layout_toLeftOf="@+id/iv_nofitication_kzhi_next"
android:src="@android:drawable/ic_media_play" />
<ImageView
android:id="@+id/iv_nofitication_kzhi_next"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:layout_toLeftOf="@+id/iv_nofitication_kzhi_colse"
android:src="@android:drawable/ic_media_next" />
<ImageView
android:id="@+id/iv_nofitication_kzhi_colse"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:textColor="@color/text_color_main" />
</RelativeLayout>
~~~
效果 :
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764c30864.jpg)
### (2)代码实现
说明: 1)通知的实现是单例模式实现(恶汉式)(
[设计模式之单例模式(Singleton pattern)](http://blog.csdn.net/lablenet/article/details/50014001)
);
2)通知中通过使用RemoteViews ,进行自定义布局实现;
3)通过PendingIntent来封装响应点击事件的Intent ;
4)第3条中的Intent,经过测试,只能每个响应事件,填充参数的Intent,只能对应一个点击事件 , 否则是得不到Intent中的参数的。但如果不需要区分的话,一个Intent就可以实现点击事件的响应;
5)通过PendingInetnt.getBroadcast() 方法来通知MusicService,进行响应事件的本质作用;
6)广播:如果MusicService想要接收到通知来的点击事件,那么需要在MusicServiceBroadcast中 注册(IntentFiliter/addAction(xxx)),同时通知中的Inent需 要setAction(xxx), xxx 要一样;
**实现的通知源码分享 :**
~~~
package cn.labelnet.framework;
import com.android.volley.toolbox.ImageLoader.ImageListener;
import android.R.anim;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.widget.RemoteViews;
import cn.labelnet.maskmusic.R;
import cn.labelnet.model.MusicModel;
import cn.labelnet.net.VolleyHttpRequest;
public class MusicNotification extends Notification {
/**
* Music播放控制 的 Notification
* 动态的显示后台的MusicService的前台展示
*/
/**
* 恶汉式实现单例模式加载
*/
private static MusicNotification notifyInstance = null;
// 通知id
private final int NOTIFICATION_ID = 10001;
// 通知
private Notification musicNotifi = null;
// 管理通知
private NotificationManager manager = null;
// 界面实现
private Builder builder = null;
// 上下文
private Context context;
// 布局
private RemoteViews remoteViews;
private final int REQUEST_CODE = 30000;
// 给Service 发送广播
private final String MUSIC_NOTIFICATION_ACTION_PLAY = "musicnotificaion.To.PLAY";
private final String MUSIC_NOTIFICATION_ACTION_NEXT = "musicnotificaion.To.NEXT";
private final String MUSIC_NOTIFICATION_ACTION_CLOSE = "musicnotificaion.To.CLOSE";
private final String MUSIC_NOTIFICAION_INTENT_KEY = "type";
private final int MUSIC_NOTIFICATION_VALUE_PLAY = 30001;
private final int MUSIC_NOTIFICATION_VALUE_NEXT = 30002;
private final int MUSIC_NOTIFICATION_VALUE_CLOSE =30003;
private Intent play=null,next=null,close = null;
private PendingIntent musicPendIntent = null;
// 给进度条页面广播
// 待实现
// 网络 : 加载图片实现
private ImageListener imageListener = null;
public void setManager(NotificationManager manager) {
this.manager = manager;
}
public void setContext(Context context) {
this.context = context;
}
private MusicNotification() {
// 初始化操作
remoteViews = new RemoteViews("cn.labelnet.maskmusic",
R.layout.list_item_notification);
builder = new Builder(context);
// 初始化控制的Intent
play = new Intent();
play.setAction(MUSIC_NOTIFICATION_ACTION_PLAY);
next = new Intent();
next.setAction(MUSIC_NOTIFICATION_ACTION_NEXT);
close = new Intent();
close.setAction(MUSIC_NOTIFICATION_ACTION_CLOSE);
}
/**
* 恶汉式实现 通知
*
* @return
*/
public static MusicNotification getMusicNotification() {
if (notifyInstance == null) {
notifyInstance = new MusicNotification();
}
return notifyInstance;
}
/**
* 创建通知
* 初始化通知
*/
@SuppressLint("NewApi")
public void onCreateMusicNotifi() {
// 设置点击事件
// 1.注册控制点击事件
play.putExtra("type",
MUSIC_NOTIFICATION_VALUE_PLAY);
PendingIntent pplay = PendingIntent.getBroadcast(context, REQUEST_CODE,
play, NOTIFICATION_ID);
remoteViews.setOnClickPendingIntent(R.id.iv_nofitication_kzhi_play,
pplay);
// 2.注册下一首点击事件
next.putExtra("type",
MUSIC_NOTIFICATION_VALUE_NEXT);
PendingIntent pnext = PendingIntent.getBroadcast(context, REQUEST_CODE,
next, NOTIFICATION_ID);
remoteViews.setOnClickPendingIntent(R.id.iv_nofitication_kzhi_next,
pnext);
// 3.注册关闭点击事件
close.putExtra("type",
MUSIC_NOTIFICATION_VALUE_CLOSE);
PendingIntent pclose = PendingIntent.getBroadcast(context, REQUEST_CODE,
close, NOTIFICATION_ID);
remoteViews.setOnClickPendingIntent(R.id.iv_nofitication_kzhi_colse,
pclose);
builder.setContent(remoteViews).setWhen(System.currentTimeMillis())
// 通知产生的时间,会在通知信息里显示
// .setPriority(Notification.PRIORITY_DEFAULT)
// 设置该通知优先级
.setOngoing(true).setTicker("播放新的一首歌")
.setSmallIcon(R.drawable.logo);
// 兼容性实现
musicNotifi = builder.getNotification();
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
// musicNotifi = builder.getNotification();
// } else {
// musicNotifi = builder.build();
// }
musicNotifi.flags = Notification.FLAG_ONGOING_EVENT;
manager.notify(NOTIFICATION_ID, musicNotifi);
}
/**
* 更新通知
*/
public void onUpdataMusicNotifi(MusicModel mm, boolean isplay) {
// 设置添加内容
remoteViews.setTextViewText(R.id.tv_nofitication_singname,
(mm.getSongname()!=null?mm.getSongname():"什么东东") + "");
remoteViews.setTextViewText(R.id.tv_nofitication_singer,
(mm.getSingername()!=null?mm.getSingername():"未知") + "");
//判断是否播放
if (isplay) {
remoteViews.setImageViewResource(R.id.iv_nofitication_kzhi_play,
android.R.drawable.ic_media_pause);
} else {
remoteViews.setImageViewResource(R.id.iv_nofitication_kzhi_play,
android.R.drawable.ic_media_play);
}
onCreateMusicNotifi();
}
/**
* 取消通知栏
*/
public void onCancelMusicNotifi(){
manager.cancel(NOTIFICATION_ID);
}
}
~~~
### (3)广播的基本使用图解
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764c432dd.jpg)
## 3.总结
通知的更新控制是在音乐状态发生改变的时候,就会更新通知的内容;相反通知也可以控制音乐当前的状态;后面还有音乐播放界面的控制,省去了很多事,简单的在Service 中调用就可以实现。
MusicService 与 MusicNotification 的基本控制图 ,如下图所示 :
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764c5c679.jpg)
音乐的播放/暂停/上一曲/下一曲/停止 基本控制 是在MusicService中实现的,所以请看
[Android实战 - 音心播放器 (Music Service 实现)](http://blog.csdn.net/lablenet/article/details/50322487);