android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频

Neoma ·
更新时间:2024-11-13
· 864 次阅读

首先先给大家看下效果

接下来我们看下具体如何实施

1、首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java)

package xxx; import android.media.AudioManager; import android.media.MediaPlayer; import android.util.Log; import xx.activity.couxrse.SoundPlayerActivity; import xx.model.bo.AudioBo; import java.util.List; /** * * 音频播放器 */ public class MediaPlayerUtil { private int index=0; // 记录音频重新被加载时上次选中的条目下标,对应集合的下标 private String path; // 对应集合下标index在数据中的音频 private String title; private int duration; // 时长 private List audioBoList; private MediaPlayer mPlayer; // 类初始化的时候立即加载该对象 private static MediaPlayerUtil mediaPlayerInstance; // 私有化构造器 public MediaPlayerUtil() { } // 提供该对象的获取方法(单例模式) public synchronized static MediaPlayerUtil getInstance() { if(mediaPlayerInstance == null) { mediaPlayerInstance = new MediaPlayerUtil(); } return mediaPlayerInstance; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getDuration() { return duration; } public void setDuration(int duration) { this.duration = duration; } public List getAudioBoList() { return audioBoList; } public void setAudioBoList(List audioBoList) { this.audioBoList = audioBoList; } public MediaPlayer getmPlayer() { return mPlayer; } public void setmPlayer(MediaPlayer mPlayer) { this.mPlayer = mPlayer; } //播放方法 public void play() { if(mPlayer!=null){ mPlayer.stop(); mPlayer.release(); // 释放相关的资源。 mPlayer=null; } try { mPlayer = new MediaPlayer(); // 设置指定的流媒体地址 mPlayer.setDataSource(path); // 设置音频流的类型 mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); /* 准备播放 */ mPlayer.prepare(); mPlayer.start(); mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { System.out.println(title+"播放完毕"); Log.d("tag", title+"播放完毕"); index=index+1; path=audioBoList.get(index).getUrl(); title=audioBoList.get(index).getTitle(); System.out.println("准备播放"+title); Log.d("tag", "准备播放"+title); play(); //根据需要添加自己的代码。。。 if(NotifityActivity.getInstace()!=null){ NotifityActivity.getInstace().updateUI(); } } }); } catch (Exception e) { e.printStackTrace(); } } public void replay(String path){ if (mPlayer != null && mPlayer.isPlaying()) { mPlayer.seekTo(0); return; } play(); } //暂停 public void pause() { mPlayer.pause(); } //判断是否正在播放中 public boolean isplay() { return mPlayer.isPlaying(); } //获取播放时长 public long getduring() { // TODO Auto-generated method stub return mPlayer.getDuration(); } //停止 public void stop() { if(mPlayer!=null){ mPlayer.stop(); } } public void setPosition (int position) { mPlayer.seekTo(position);//重新设定播放进度 } /** * 关闭播放器 */ public void closeMedia() { if (mPlayer != null ) { mPlayer.stop(); mPlayer.release(); // 释放相关的资源。 mPlayer=null; } } }

2、音频实体类

private Long id; private String catalog; // 类别 private String title; // 标题 private int count; // 播放次数 private int duration; // 时长 private String publishTime; // 发布时间 private String url; // 地址 private String createTime; // 创建时间 private String updateTime; // 更新时间 private int delFlag;// 是否删除 private String imgUrl; // 相对应的背景图路径

 3、添加一个service服务类,增加音频后台播放功能

package xx.activity.course.audio; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class MediaService extends Service { public class MusicController extends Binder { } /** * 当绑定服务的时候,自动回调这个方法 * 返回的对象可以直接操作Service内部的内容 * @param intent * @return */ @Override public IBinder onBind(Intent intent) { return new MusicController(); } @Override public void onCreate() { super.onCreate(); } /** * 任意一次unbindService()方法,都会触发这个方法 * 用于释放一些绑定时使用的资源 * @param intent * @return */ @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } }

4、在AndroidManifest.xml 加入以下代码

5、创建广播类(BroadcastReceiver) 实现对通知栏点击事件的实现以及回调(静态注册)

package xx.activity.course.audio; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.MediaPlayer; import android.widget.Toast; import xx.activity.course.SoundPlayerActivity; import xx.fragment.classroom.holder.HomeAudioHolder; import xx.media.MediaPlayerUtil; /* * 通知栏--点击事件之调用广播实现相对应点击事件的逻辑处理 * */ public class NotificationClickReceiver extends BroadcastReceiver { public static final String TYPE = "type"; //这个type是为了Notification更新信息的 private MediaPlayerUtil mediaPlayerUtil; private MediaPlayer mediaPlayer; private int postion; private int endIndex; private Context context; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); this.context = context; mediaPlayerUtil = MediaPlayerUtil.getInstance(); endIndex = mediaPlayerUtil.getAudioBoList().size() - 1; int type = intent.getIntExtra(TYPE, -1); if (type != -1) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(type); } if (action.equals("stop")) { //处理停止或播放点击事件 mediaPlayerUtil = MediaPlayerUtil.getInstance(); if (mediaPlayerUtil.getmPlayer()!=null && mediaPlayerUtil.isplay()) { mediaPlayerUtil.stop(); } else { mediaPlayerUtil.setPath(mediaPlayerUtil.getAudioBoList().get(mediaPlayerUtil.getIndex()).getUrl()); mediaPlayerUtil.play(); } } if (action.equals("next")) { mediaPlayerUtil.stop(); mediaPlayerUtil = MediaPlayerUtil.getInstance(); int index = mediaPlayerUtil.getIndex(); if ((index + 1) > endIndex) { Toast.makeText(context, "已经是最后一首了", Toast.LENGTH_SHORT).show(); return; } postion = index + 1; mediaPlayerUtil.setIndex(postion); mediaPlayerUtil.setPath(mediaPlayerUtil.getAudioBoList().get(postion).getUrl()); mediaPlayerUtil.setTitle(mediaPlayerUtil.getAudioBoList().get(postion).getTitle()); mediaPlayerUtil.play(); } if (action.equals("last")) { mediaPlayerUtil.stop(); mediaPlayerUtil = MediaPlayerUtil.getInstance(); //处理上一首 int index = mediaPlayerUtil.getIndex(); if ((index - 1) < 0) { Toast.makeText(context, "已经是第一首了", Toast.LENGTH_SHORT).show(); return; } postion = index - 1; mediaPlayerUtil.setIndex(postion); mediaPlayerUtil.setPath(mediaPlayerUtil.getAudioBoList().get(postion).getUrl()); mediaPlayerUtil.setTitle(mediaPlayerUtil.getAudioBoList().get(postion).getTitle()); mediaPlayerUtil.play(); } // 关闭通知栏 if (action.equals("notification_cancelled")) { NotifityActivity.getInstace().closeNotifi(); return; } // 更新音频页通知栏 if (NotifityActivity.getInstace() != null) { NotifityActivity.getInstace().updateUI(); } } }

5、在AndroidManifest.xml 加入以下代码

 6、创建通知栏远程视图(RemoteViews)

<!-- --> />

7、对应的activity中的设置

package com.lidou.quke.activity.course.audio; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.NotificationCompat; import android.view.View; import android.widget.Button; import android.widget.RemoteViews; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.NotificationTarget; import com.xx.R; import xx.activity.BusinessActivity; import com.xx.media.MediaPlayerUtil; import com.xx.model.bo.AudioBo; import java.util.ArrayList; import java.util.List; /* * 音频列表activity * */ public class NotifityActivity extends BusinessActivity implements ServiceConnection { private static final int SDK_PAY_FLAG = 1; private final static String TAG = "加载SoundPlayerActivity类"; private Button mbutton; private MediaPlayer mPlayer; // 这里如果不设置为-1 的话,默认是0,会造成进入列表第一条数据就显示为播放状态 private int thisPosition; String channelId = "音频会话";//渠道id private MediaPlayerUtil mediaPlayerUtil; private Notification notification; private RemoteViews contentView; private NotificationTarget notificationTarget; private AudioManager mAudioManager; // 检索了NotifityActivity的运行实例,以方便广播接收成功时处理activity相对应的逻辑处理(如:更新UI) private static NotifityActivity notifityActivity; public static NotifityActivity getInstace() { return notifityActivity; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); notifityActivity = this; setContentView(R.layout.acs); Intent intent = new Intent(this, MediaService.class); //增加StartService,来增加后台播放功能 startService(intent); bindService(intent, this, BIND_AUTO_CREATE); // 这里只是用来做测试数据 List audioBoList=new ArrayList(); AudioBo audioBo=new AudioBo(); int a1 = 0;long b1 = (int)a1; audioBo.setId(b1); audioBo.setTitle("音频1"); audioBo.setUrl("音频路径地址"); audioBo.setImgUrl("网络图片地址"); audioBoList.add(audioBo); AudioBo audioBo2=new AudioBo(); int a = 1;long b = (int)a; audioBo2.setId(b); audioBo2.setTitle("音频2"); audioBo2.setUrl("音频路径地址"); audioBo2.setImgUrl("网络图片地址"); audioBoList.add(audioBo2); mediaPlayerUtil=MediaPlayerUtil.getInstance(); mediaPlayerUtil.setAudioBoList(audioBoList); initView(); } private void initView() { // 我这里是在一个页面中的button事件唤起通知栏 mbutton=findViewById(R.id.notifyButton); mbutton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mediaPlayerUtil=MediaPlayerUtil.getInstance(); // 调用通知栏加载方法 initNotificationBar(); // 1.上下文 2.图标控件的ResID 2.RemoteView ,4 Notification 5 Notification_ID notificationTarget = new NotificationTarget(mContext, R.id.bgmmMusicImageView, contentView, notification, R.string.app_name); Glide.with(mContext).asBitmap().load(mediaPlayerUtil.getAudioBoList().get(0).getImgUrl()) .placeholder(R.drawable.test).into(notificationTarget); } }); //1 初始化AudioManager对象 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); //2 申请焦点(关闭其它音频通道) mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); mediaPlayerUtil = MediaPlayerUtil.getInstance(); if (mediaPlayerUtil != null) { thisPosition = mediaPlayerUtil.getIndex(); } } @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onPointerCaptureChanged(boolean hasCapture) { } /* * 创建自定义远程视图RemoteView * */ private void initRemoteView() { contentView = new RemoteViews(getPackageName(), R.layout.audio_music_notifi);// 远程视图 mediaPlayerUtil = MediaPlayerUtil.getInstance(); contentView.setTextViewText(R.id.musicTitleTextView, mediaPlayerUtil.getAudioBoList().get(mediaPlayerUtil.getIndex()).getTitle()); contentView.setImageViewResource(R.id.audio_close_btn, R.drawable.player_icon_close); contentView.setImageViewResource(R.id.lastImageView, R.drawable.player_icon_last); contentView.setImageViewResource(R.id.nextImageView, R.drawable.player_icon_next); contentView.setImageViewResource(R.id.stopImageView, R.drawable.player_icon_stop); if (mediaPlayerUtil.getmPlayer()!=null && mediaPlayerUtil.isplay()) { contentView.setImageViewResource(R.id.stopImageView, R.drawable.player_icon_plays); } else { contentView.setImageViewResource(R.id.stopImageView, R.drawable.player_icon_stop); } // 实现停止/播放 Intent intentStop = new Intent("stop"); PendingIntent pIntentStop = PendingIntent.getBroadcast(this, 0, intentStop, 0); contentView.setOnClickPendingIntent(R.id.stopImageView, pIntentStop); //下一首事件 Intent intentNext = new Intent("next");//发送播放下一曲的通知 PendingIntent pIntentNext = PendingIntent.getBroadcast(this, 0, intentNext, 0); contentView.setOnClickPendingIntent(R.id.nextImageView, pIntentNext); //上一首事件 Intent intentLast = new Intent("last");//发送播放上一曲的通知 PendingIntent pIntentLast = PendingIntent.getBroadcast(this, 0, intentLast, 0); contentView.setOnClickPendingIntent(R.id.lastImageView, pIntentLast); // 关闭通知栏 Intent intentCancelled = new Intent("notification_cancelled"); PendingIntent pIntentCancelled = PendingIntent.getBroadcast(this, 0, intentCancelled, 0); contentView.setOnClickPendingIntent(R.id.audio_close_btn, pIntentCancelled); } /* * 创建通知栏 * */ private void initNotificationBar() { initRemoteView(); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notification=new Notification(); Intent intent = new Intent(mContext, NotificationClickReceiver.class); // 创建一个跳转到活动页面的意图 PendingIntent pendingIntent = PendingIntent.getActivity(NotifityActivity.this, R.string.app_name, intent, PendingIntent.FLAG_CANCEL_CURRENT); Notification.Builder builder = null; // 创建一个通知消息的构造器 // 判断是否是android 8以上的版本,因为从8.0版本后唤起通知栏必须要填写渠道id和渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String description = "有趣,有用,有未来"; int importance = NotificationManager.IMPORTANCE_LOW;//重要性级别 // getPackageName() 获取APP渠道名称 NotificationChannel mChannel = new NotificationChannel(channelId, "音频", importance); mChannel.setDescription(description);//渠道描述 manager.createNotificationChannel(mChannel);//创建通知渠道 mChannel.setSound(null,null); builder = new Notification.Builder(this, channelId); builder.setContentIntent(pendingIntent) .setCustomContentView(contentView) .setCustomBigContentView(contentView) .setShowWhen(false) .setSmallIcon(R.drawable.notification_icon) // 必填 .build(); notification = builder.build(); }else{ // 这里要兼顾android 8以下的版本 NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext,channelId); notificationBuilder.setCustomBigContentView(contentView) .setCustomContentView(contentView) .setContentIntent(pendingIntent) .setSmallIcon(R.drawable.notification_icon) .build(); notification = notificationBuilder.build(); } notification.flags = notification.FLAG_NO_CLEAR;//设置通知点击或滑动时不被清除*/ manager.notify(R.string.app_name, notification); } /** * [获取应用程序版本名称信息] * * @param context * @return 当前应用的版本名称 */ public static synchronized String getPackageName(Context context) { try { PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo( context.getPackageName(), 0); return packageInfo.packageName; } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onDestroy() { unbindService(this); super.onDestroy(); } // 调用广播回调来更新activity中相关事件 public void updateUI() { NotifityActivity.this.runOnUiThread(new Runnable() { public void run() { initView(); initNotificationBar(); mediaPlayerUtil = MediaPlayerUtil.getInstance(); boolean isdestory; if (mActivity== null || mActivity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mActivity.isDestroyed())) { isdestory=true; } else { isdestory=false; } // 由于图片使用Glide异步加载图片activity已销毁时时发生的崩溃 if(isdestory==false){ // 此处为Glide的notificationTarget回调,当网络图片加载成功更新通知栏相对应控件的图像 notificationTarget = new NotificationTarget(mContext, R.id.bgmmMusicImageView, contentView, notification, R.string.app_name); // 加载网络图片 Glide.with(mContext).asBitmap().load(mediaPlayerUtil.getAudioBoList().get(mediaPlayerUtil.getIndex()).getImgUrl()).placeholder(R.drawable.test).into(notificationTarget); } mAudioManager.abandonAudioFocus(mAudioFocusChange); } }); } /* * 关闭、清楚通知栏 * */ public void closeNotifi() { NotifityActivity.this.runOnUiThread(new Runnable() { public void run() { mediaPlayerUtil = MediaPlayerUtil.getInstance(); mediaPlayerUtil.closeMedia(); mAudioManager.abandonAudioFocus(mAudioFocusChange); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.cancel(R.string.app_name); } }); } // 焦点监听 AudioManager.OnAudioFocusChangeListener mAudioFocusChange = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { mediaPlayerUtil=MediaPlayerUtil.getInstance(); if(focusChange==AudioManager.AUDIOFOCUS_LOSS) { //长时间丢失焦点,当其他应用如播放QQ音乐,网易云音乐播放时 if (mediaPlayerUtil.isplay()) { closeNotifi(); } mAudioManager.abandonAudioFocus(mAudioFocusChange); }else if(focusChange== AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { if (mediaPlayerUtil.isplay()) { mediaPlayerUtil.stop(); updateUI(); } } } }; }

再次捋下思路,

1、通知栏实现音频播放,主要使用到的是MediaPlayer播放器,

2、切歌我这里主要用到的是 静态广播来实现对通知栏点击事件的逻辑处理

3、加载网络图片主要用到的是Glide的NotificationTarget对象来及时更新相对应的图像,这里需要注意的是由于图片使用Glide异步加载图片,一定要在合适的地方去判断activity是否已销毁,不然会发生崩溃报错

4、在广播里触发不同的事件时,有时需要通知栏的图标或样式发生改变,那如何保证及时更新呢,我这里是在activity里创建一个它的实例,并写一个更新通知栏UI的方法,在广播需要用到的地方调用即可

5,说到音频就不得不说如何在播放自己项目的时候或接听电话、视频时关闭音频,或关闭第三方的音频了,这里主要要到的是AudioManager(音频管理器)

 至此一个完整的通知栏实现音频播放示例已完成,我也是第一次写,关于中间的service服务还不是很了解,代码还有很大的优化空间,具体到大家的项目中可根据需要灵活放入改变就好,如果您有更好的更简洁的方法,也欢迎分享,如果代码有哪里写的不好,更欢迎大家指正,


作者:。度



网络图 图片 关闭 app Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号