【Android】MediaPlayer之音频播放(二)前台服务播放音乐

2017-11-29 16:06:15来源:http://www.jianshu.com/p/a02b473f5f06作者:zrunker人点击

分享
第七城市th7cn


三十岁之前努力犯错,拼命尝试,三十岁之后开始靠岸,学着靠谱。 有些事年轻的时候不做,就真的没有机会再去做了。 我从不怕失败和跌倒了重新再来,我只怕把一些期望一直埋在心里,最后变成了内心时常叨扰的痛。 --小川叔 《扛得住,世界就是你的》
三十岁之前努力犯错,拼命尝试,三十岁之后开始靠岸,学着靠谱。 有些事年轻的时候不做,就真的没有机会再去做了。 我从不怕失败和跌倒了重新再来,我只怕把一些期望一直埋在心里,最后变成了内心时常叨扰的痛。 --小川叔 《扛得住,世界就是你的》

在实际应用程序当中,往往会把播放音频的操作放在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地址
阅读原文



微信公众号:ibooker爱书客
微信公众号:ibooker爱书客



第七城市th7cn

微信扫一扫

第七城市微信公众平台