项目中需要用到播放音频的功能,想做一个类似酷狗、酷我这样的音频播放功能,在通知栏和锁屏时都可以操控音乐,开发中发现oppo reno手机在锁屏时不显示通知栏,研究了整整一天终于解决,特作记录,给遇到同样问题的童鞋做个参考。
整体功能实现思路如下:
1.播放音乐时启动前台服务,在服务中创建播放器和通知并且创建广播,音频的播控通过广播来操控,实现跨模块;
2.在切换音频源、播放、暂停的时候通过重新创建通知来实现通知的界面更新;
3.在关闭音频功能时关掉通知,释放播放器。
细节,以下只做简单说明,具体实现方式请百度,本文只是为了解决锁屏不显示通知的问题,已测试正常使用的机型有华为、荣耀、oppo:
1.通知的自定义view为remoteView;
2.通知创建时需要判断8.0渠道配置:
private fun createChannel(context: Context, channel_id: String, channel_name: CharSequence, description: String) {
//8.0以上版本通知适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_HIGH)
notificationChannel.setSound(null, null)
notificationChannel.enableLights(false) //通知灯
notificationChannel.lightColor = Color.RED // 通知灯颜色
notificationChannel.setShowBadge(false) //角标
notificationChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC // 所有情况下显示,包括锁屏
notificationChannel.description = description
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(notificationChannel)
}
}
/**
* 返回一个前台通知
*
* @param channel_id 通知渠道id,注意8.0创建通知的时候渠道id与此要匹配
* @param remoteViews 自定义通知view
* @return
*/
fun createForeNotification(context: Context, channel_id: String, channel_name: CharSequence, description: String, remoteViews: RemoteViews): Notification {
CreateChannel(context, channel_id, channel_name, description)
val builder = NotificationCompat.Builder(context, channel_id)
.setSmallIcon(R.mipmap.ic_launcher) //最顶部的小图标
.setContent(remoteViews)
.setAutoCancel(true) // 允许自动清除
.setPriority(NotificationCompat.PRIORITY_MAX)
.setSound(Uri.EMPTY) //声音
.setDefaults(NotificationCompat.DEFAULT_ALL) //统一消除声音和震动
.setVisibility(Notification.VISIBILITY_PUBLIC) // 所有情况下显示,包括锁屏
return builder.build()
}
3.对通知设置点击事件,切换、暂停、播放的原理一样,如果别的模块需要调用播控的话直接发送广播即可:
// 暂停
val pauseIntent = Intent(AudioBroadCastReceiver.PAUSE)
val pausePendingIntent = PendingIntent.getBroadcast(this, 0, pauseIntent, 0)
notifyLayout.setOnClickPendingIntent(R.id.iv_music_pause, pausePendingIntent)
// 播放
val playIntent = Intent(AudioBroadCastReceiver.PLAY)
val playPendingIntent = PendingIntent.getBroadcast(this, 0, playIntent, 0)
notifyLayout.setOnClickPendingIntent(R.id.iv_music_play, playPendingIntent)
object AudioBroadCastReceiver : BroadcastReceiver() {
private val TAG = javaClass.simpleName
const val PLAY_PRE = "com.audio.PLAY_PRE"
const val PLAY_NEXT = "com.audio.PLAY_NEXT"
const val PLAY = "com.audio..PLAY"
const val PAUSE = "com.audio.PAUSE"
// 停止播放器、关闭通知、关闭服务、关掉播放界面
const val CLOSE = "com.audio.CLOSE"
var receiveListener: ((Context, Intent?) -> Unit)? = null
override fun onReceive(context: Context, intent: Intent?) {
Logger.d(TAG, "onReceive: ${intent?.action}")
val audioPlayerHelper = App.get().audioPlayerHelper
intent?.let {
when (it.action) {
PLAY_PRE -> {
Logger.d(TAG, "onReceive: 上一节")
audioPlayerHelper.playPre()
}
PLAY_NEXT -> {
Logger.d(TAG, "onReceive: 下一节")
audioPlayerHelper.playNext()
}
PLAY -> {
Logger.d(TAG, "onReceive: 播放")
audioPlayerHelper.play()
}
PAUSE -> {
Logger.d(TAG, "onReceive: 暂停")
audioPlayerHelper.pause()
}
CLOSE -> {
Logger.d(TAG, "onReceive: 停止")
audioPlayerHelper.stop()
}
else -> {
}
}
receiveListener?.invoke(context,intent)
}
}
}
4.服务中启动通知:
startForeground(AudioNotifyHelper.NOTIFY_ID, notification)
5.监听锁屏,我遇到的问题就是在oppo手机上锁屏不显示通知,其实解决办法就是监听锁屏,在锁屏后再次发送一次通知即可:
public class HomeWatcherReceiver extends BroadcastReceiver {
private final String TAG = getClass().getSimpleName();
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
private static final String SYSTEM_DIALOG_REASON_LOCK = "lock";
private static final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
private PhysicalKeyListener listener;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Logger.i(TAG, "onReceive: action: " + action);
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
// android.intent.action.CLOSE_SYSTEM_DIALOGS
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
Logger.i(TAG, "reason: " + reason);
if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
// 短按Home键
Logger.i(TAG, "homekey");
if(listener != null){
listener.onHome();
}
} else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
// 长按Home键 或者 activity切换键
Logger.i(TAG, "long press home key or activity switch");
if(listener != null){
listener.onRecentApp();
}
} else if (SYSTEM_DIALOG_REASON_LOCK.equals(reason)) {
// 锁屏
Logger.i(TAG, "lock");
if(listener != null){
listener.onLock();
}
} else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {
// samsung 长按Home键
Logger.i(TAG, "assist");
if(listener != null){
listener.onAssist();
}
}
} else if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
Logger.i(TAG, "onReceive: 开屏");
if(listener != null){
listener.onScreenOn();
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
Logger.i(TAG, "onReceive: 锁屏");
if(listener != null){
listener.onScreenOff();
}
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁
Logger.i(TAG, "onReceive: 解锁");
if(listener != null){
listener.onUserPresent();
}
}
}
public void setListener(PhysicalKeyListener listener) {
this.listener = listener;
}
}
6.在服务中注册锁屏广播监听,监听到锁屏再发一次通知即可。
至此,仿音乐播放器的通知栏和锁屏操控音乐就OK了,如果有特殊机型或者其他情况不管用的,评论区咱们互相讨论、学习。
共勉!
效果如下: