作者:邹峰立,微博:zrunker,邮箱:[email protected],微信公众号:书客创作,个人平台:www.ibooker.cc。
本文选自书客创作平台第47篇文章。阅读原文 。
在实际应用程序当中,往往会把播放音频的操作放在Service中,这样即使前台页面显示,后台依旧可以完成播放相关功能。而前台服务是实现即使前台页面不存在了,依旧可以完成播放相关功能,简单一点来说就是杀不死的服务,即使在内存不足的情况下也会存在。
之前两篇文章已经详细的说明了MediaPlayer的生命周期和使用技巧,而这篇文章讲述前台服务播放音乐的实现。
【Android】MediaPlayer生命周期分析
【Android】MediaPlayer之音频播放
在实现前台服务之前,首先要明白什么是Notification?什么是Service?
Notification通知
Notification是显示在通知栏上的框架,一般用于展示通知类的消息。
一般情况下是通过NotificationCompat.Builder进行创建。通过NotificationManager进行管理,如取消通知,更新通知等。但是要注意在创建Notification三要素不能少,否则将会创建失败或者运行异常,Notification三要素:小图标、标题、内容。不同的机型显示的效果也会有所不同。
显示一个Notification:
Intent intent = new Intent(this, MainActivity.class);
PendingIntent contentPendingIntent = PendingIntent.getActivity(this, CONTENT_PENDINGINTENT_REQUESTCODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent delIntent = new Intent(this, MediaPlayerService.class);
PendingIntent delPendingIntent = PendingIntent.getService(this, DELETE_PENDINGINTENT_REQUESTCODE, delIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.test_bg);
// 初始化Notification,Notification三要素:小图标、标题、内容
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
// 设置小图标,不展开时显示,当展开时现在在左侧
.setSmallIcon(R.mipmap.ic_launcher)
// 设置状态栏的显示的信息
.setTicker("这是一个音频播放器")
// 设置标题
.setContentTitle("ZMediaPlayer")
// 设置内容
.setContentText("内容")
// 设置通知时间,默认为系统发出通知的时间,通常不用设置
.setWhen(System.currentTimeMillis())
// 设置是否显示时间
.setShowWhen(true)
// 设置大图标-展开时一些手机上大图标会替换掉小图标
.setLargeIcon(largeIcon)
// 点击通知后自动清除
.setAutoCancel(false)
// 设置点击通知效果
.setContentIntent(contentPendingIntent)
// 设置删除时候出发的动作
.setDeleteIntent(delPendingIntent)
// 自定义视图
.setContent(RemoteViews views)
// 设置额外信息,一般显示在右下角
.setContentInfo("额外信息")
// 向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
.setDefaults(Notification.DEFAULT_VIBRATE);
Notification notification = builder.build();
// 获取NotificationManager实例
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 发送通知,并设置id
notificationManager.notify(id, notification);
修改Notification:
// 通过id进行更新Notification
notification = builder.setContentTitle("ZMediaPlayer111")
// 设置内容
.setContentText("内容111")
.build();
notificationManager.notify(id, notification);
取消Notification:
// 通过id进行取消Notification
if (notificationManager != null)
notificationManager.cancel(id);
Service服务
Service为Android四大组件之一,所以要使用Service,必须添加到清单文件中。Service一般于运行于后台。
Service启动方式有两种:
- startService(Intent)
- bindService(Intent,ServiceConnection,flags)
startService(Intent)
生命周期: onCreate()- >onStartCommand()->startService()->onDestroy()
该方法启动service,会执行一个onStartCommand()的方法,所以一些操作可以放在 onStartCommand() 中进行处理。并且会随着应用程序的退出而销毁。
Intent intent = new Intent(MainActivity.this, MediaPlayerService.class);
intent.putExtra("playing", isPlay);
startService(intent);
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 得到键值对
boolean playing = intent.getBooleanExtra("playing", false);
if (playing)
......
return super.onStartCommand(intent, flags, startId);
}
bindService(Intent,ServiceConnection,flags)
生命周期:onCreate()->onBind()->onUnbind()->onDestroy()
可由多个页面进行绑定,不随应用程序的退出而销毁。
Intent intent = new Intent(MainActivity.this, MediaplayerBinderService.class);
/*
* Service:Service的桥梁
* ServiceConnection:处理链接状态
* flags:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.
*/
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
前台服务
前台服务要利用到以下两个方法:
startForeground(int id, Notification notification)
这个方法是启动Notification到前台,一般写在onCreate方法当中。
topForeground(boolean removeNotification)
这个方法是停止Notification,一般写在onDestroy方法当中。
实例-前台服务播放音乐
首先看一下效果图:
代码实现:(部分代码不给出,稍后会提供Github地址)
首先定义MediaplayerBinderService类让其继承Service,并实现其onCreate()->onBind()->onDestroy()。
在onCreate()方法当中,需要实现初始化MediaPlayer,定义并显示Notification。
@Override
public void onCreate() {
super.onCreate();
Log.d("MediaPlayerBService", "OnCreate");
// 初始化MediaPlayer
initMediaPlayer();
// 设置点击通知结果
Intent intent = new Intent(this, MainActivity.class);
PendingIntent contentPendingIntent = PendingIntent.getActivity(this, CONTENT_PENDINGINTENT_REQUESTCODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent delIntent = new Intent(this, MediaPlayerService.class);
PendingIntent delPendingIntent = PendingIntent.getService(this, DELETE_PENDINGINTENT_REQUESTCODE, delIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// 自定义布局
views = new RemoteViews(getPackageName(), R.layout.layout_mediaplayer);
// 下一首
Intent intentNext = new Intent("nextMusic1");
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, NEXT_PENDINGINTENT_REQUESTCODE, intentNext, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.tv_next, nextPendingIntent);
// 暂停/播放
Intent intentPlay = new Intent("playMusic1");
PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, PLAY_PENDINGINTENT_REQUESTCODE, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.tv_pause, playPendingIntent);
// 停止
Intent intentStop = new Intent("stopMusic1");
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(this, STOP_PENDINGINTENT_REQUESTCODE, intentStop, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.tv_cancel, stopPendingIntent);
builder = new NotificationCompat.Builder(this)
// 设置小图标
.setSmallIcon(R.drawable.test_bg)
// 设置标题
.setContentTitle("ZMediaPlayer")
// 设置内容
.setContentText("内容")
// 点击通知后自动清除
.setAutoCancel(false)
// 设置点击通知效果
.setContentIntent(contentPendingIntent)
// 设置删除时候出发的动作
.setDeleteIntent(delPendingIntent)
// 自定义视图
.setContent(views);
// 获取NotificationManager实例
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 前台服务
startForeground(NOTIFICATION_PENDINGINTENT_ID, builder.build());
}
在onBind()方法中,返回一个已经实例化好的Binder类,这里可以通过定义一个内部类来实现,并该内部类中实现获取当前服务的方法。
// 定义Binder类-当然也可以写成外部类
private MediaplayerBinder mediaplayerBinder = new MediaplayerBinder();
public class MediaplayerBinder extends Binder {
public Service getService() {
return MediaplayerBinderService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("MediaPlayerBService", "onBind");
return mediaplayerBinder;
}
在onDestroy()方法当中实现MediaPlayer销毁,Notification的销毁等。
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MediaPlayerBService", "onDestroy");
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
if (wifiLock != null && wifiLock.isHeld())
wifiLock.release();
// 取消Notification
if (notificationManager != null)
notificationManager.cancel(NOTIFICATION_PENDINGINTENT_ID);
stopForeground(true);
// 停止服务
stopSelf();
}
最后在相应的Activity当中实现Service的绑定,并通过Binder获取服务实例,通过服务实例调用服务的效果方法。
// 定义两个全局变量
private MediaplayerBinderService bindService;
private boolean isBindService = false;
// 绑定服务
private void bindService() {
if (!isBindService) {
Intent intent = new Intent(MainActivity.this, MediaplayerBinderService.class);
/*
* Service:Service的桥梁
* ServiceConnection:处理链接状态
* flags:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.
*/
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
// 解除绑定
private void unBind() {
if (isBindService) {
unbindService(serviceConnection);
isBindService = false;
}
}
/**
* serviceConnection是一个ServiceConnection类型的对象,它是一个接口,用于监听所绑定服务的状态
*/
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 该方法用于处理与服务已连接时的情况。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaplayerBinderService.MediaplayerBinder binder = (MediaplayerBinderService.MediaplayerBinder) service;
bindService = (MediaplayerBinderService) binder.getService();
isBindService = true;
}
/**
* 该方法用于处理与服务断开连接时的情况。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
bindService = null;
}
};
Github地址
阅读原文