android播放音频
程序员文章站
2022-06-18 20:18:29
...
最近需要项目涉及到了音频播放功能,有可能需要在后台播放,并且在通知栏显示状态。通过查阅资料,自己模拟了一个demo。样式如下:
在运行中,界面内的控件更新使用的是eventBus发送的,在Notification中,是通过广播发送操作,目前只加入了播放和暂停的功能。
首先是在MainActivity注册的广播和Notification,全部设置为静态的,广播是写在了播放界面
public BPlayActivity.MusicServiceReceiver mr;
public static NotificationManager manager;
public static RemoteViews remoteViews;
public static Notification notify;
// 广播监听的动作
public static final String ACTION_BF = "ACTION_BF";
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
showStartNotification();
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void showStartNotification() {
// 注册receiver
mr = new BPlayActivity().getReceiver();
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(ACTION_BF);
registerReceiver(mr, intentfilter);
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
notify = builder.build();
remoteViews = new RemoteViews(this.getPackageName(), R.layout.layout_view_title);
notify.contentView = remoteViews; // 设置下拉图标
notify.bigContentView = remoteViews; // 防止显示不完全,需要添加apisupport
notify.flags = Notification.FLAG_ONGOING_EVENT;
notify.icon = R.drawable.img_reward_bg_down;
Intent intent1 = new Intent();
intent1.setAction(ACTION_BF);
PendingIntent contentIntent = PendingIntent.getBroadcast(this, 0,
intent1, 0);
remoteViews.setOnClickPendingIntent(R.id.iv_notify_play, contentIntent);
}
在BPlayActivity.class 播放界面,使用了SeekBar和自定义的MediaPlayer。
public class MyPlay implements MediaPlayer.OnBufferingUpdateListener,
MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener {
public static SeekBar skbProgress;
private Timer mTimer = new Timer();
public static MediaPlayer mediaPlayer;
private static onShowTimeListener listener;
/**
* 0未播放
* 1播放
* 2暂停
*/
public static int playState = 0;
public interface onShowTimeListener {
public void showTime(long time, long endtime);
}
public MyPlay(SeekBar skbProgress, onShowTimeListener showTimeListener) {
skbProgress.setClickable(false);
listener = showTimeListener;
setSkbProgress(skbProgress);
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
mTimer.schedule(mTimerTask, 0, 300);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
// skbProgress.setOnSeekBarChangeListener(this);
}
public void play() {
playState = 1;
mediaPlayer.start();
}
public static void setSkbProgress(SeekBar skbProgre) {
skbProgress = skbProgre;
skbProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (mediaPlayer != null) {
progress = i * mediaPlayer.getDuration()
/ seekBar.getMax();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.i("msg", "onStartTrackingTouch");
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null) {
mediaPlayer.seekTo(progress);
Log.i("msg", "progress==" + progress);
if (progress == mediaPlayer.getDuration()) {
playState = 0;
if (listener != null)
listener.showTime(progress, progress);
}
}
}
});
}
public void playUrl(String videoUrl) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();//prepare之后自动播放
play();
skbProgress.setClickable(true);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
ToastBean bean = new ToastBean();
bean.setMsg("音频地址错误");
EventBus.getDefault().post(bean);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void pause() {
playState = 2;
mediaPlayer.pause();
}
public void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
playState = 0;
mediaPlayer.release();
mediaPlayer = null;
}
}
/*******************************************************
* 通过定时器和Handler来更新进度条
******************************************************/
TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
if (mediaPlayer == null)
return;
try {
if (mediaPlayer.isPlaying() && skbProgress.isPressed() == false) {
handleProgress.sendEmptyMessage(0);
}
} catch (Exception e) {
}
}
};
Handler handleProgress = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
if (mediaPlayer != null) {
int position = mediaPlayer.getCurrentPosition();
int duration = mediaPlayer.getDuration();
if (duration > 0) {
long pos = skbProgress.getMax() * position / duration;
TimeBean bean = new TimeBean();
bean.setPos(pos);
EventBus.getDefault().post(bean);
}
if (listener != null)
listener.showTime(position, duration);
}
}
};
@Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
skbProgress.setSecondaryProgress(i);
if (mediaPlayer.getDuration() != 0) {
int currentProgress = skbProgress.getMax() * mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration();
}
}
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
}
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
playState = 1;
}
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
return false;
}
//返回播放状态
public int isPalyState() {
return playState;
}
static int progress;
}
在播放界面首相注册EventBus
private static ItemData intentBean;
private ImageView iv_play;
private TextView tv_starttime;
private TextView tv_endtime;
private static SeekBar sb_play;
private ImageView iv_roat;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bplay);
EventBus.getDefault().register(this);
intentBean = (ItemData) getIntent().getSerializableExtra("selList");
initView();
initData();
}
//因为是用过列表点击进入的,这里判断了进入的时候是否在播放,如果是相同的就同步时间,如果不同,就将seekBar设置为0,
//音频继续播,知道点击了播放按钮,在请求另一个地址的音频进行播放,时间重置
private void initData() {
if (PlayService.mPlay != null) {
if (PlayService.playId == intentBean.getId()) {
MyPlay.setSkbProgress(sb_play);
//初始进入,判断是不是当前播放的Id
if (PlayService.mPlay.mediaPlayer.isPlaying()) {
iv_play.setSelected(true);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
manager.notify(0, notify);
}
setAnim(true);
}
}
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.iv_play:
//显示通知栏Notification
manager.notify(0, notify);
startService();
break;
}
}
//这里的context使用的是Application的
public void startService() {
PlayService.sb_play = sb_play;
Intent intent = null;
intent = new Intent(MyApplication.getContext(), PlayService.class);
intent.putExtra("intentBean", intentBean);
MyApplication.getContext().startService(intent);
}
//设置中间的原图播放旋转效果
private boolean isAnimStart = false;
ObjectAnimator rotate = null;
Float currentValue = 0f;
public void setAnim(boolean state) {
if (state) {
if (!isAnimStart) {
isAnimStart = true;
rotate = ObjectAnimator.ofFloat(iv_roat, "Rotation",
currentValue - 360, currentValue);
// 设置持续时间
rotate.setDuration(3000);
// 设置循环播放
rotate.setRepeatCount(ObjectAnimator.INFINITE);
rotate.setInterpolator(new LinearInterpolator());//not stop
rotate.setRepeatCount(-1);//set repeat time forever
rotate.start();
rotate.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 监听动画执行的位置,以便下次开始时,从当前位置开始
currentValue = (Float) animation.getAnimatedValue();
}
});
}
} else {
isAnimStart = false;
if (rotate != null)
rotate.cancel();
}
}
/**
* 在主线程中显示当前的控件状态
*/
@Subscribe(threadMode = ThreadMode.MainThread)
public void EventUpdateTime(UpdateTimeBean bean) {
if (tv_starttime == null || tv_endtime == null) {
return;
}
if (PlayService.playId == intentBean.getId()) {
tv_starttime.setText(DateUtil.setHmsQian(bean.getStartTime()));
tv_endtime.setText(DateUtil.setHmsQian(bean.getEndTime()));
if (DateUtil.setHmsQian(bean.getStartTime()).equals(DateUtil.setHmsQian(bean.getEndTime()))) {
//说明是播放完成了
iv_play.setSelected(false);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
manager.notify(0, notify);
}
setAnim(false);
}
}
}
/**
* 在主线程中显示当前的控件状态
*/
@Subscribe(threadMode = ThreadMode.MainThread)
public void EventPlayState(BPlayStateBean bean) {
if (iv_play == null) {
return;
}
if (bean.isSelect()) {
iv_play.setSelected(true);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
manager.notify(0, notify);
}
setAnim(true);
} else {
iv_play.setSelected(false);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
manager.notify(0, notify);
}
setAnim(false);
}
}
/**
* 设置seekVar的显示状态
*
* @param bean
*/
@Subscribe(threadMode = ThreadMode.MainThread)
public void EventPlayState(TimeBean bean) {
if (PlayService.playId == intentBean.getId() && sb_play != null) {
sb_play.setProgress((int) bean.getPos());
}
}
/**
* 返回错误提示
*
* @param bean
*/
@Subscribe(threadMode = ThreadMode.MainThread)
public void EventMsg(ToastBean bean) {
Toast.makeText(this, bean.getMsg(), Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public MusicServiceReceiver getReceiver() {
return new MusicServiceReceiver();
}
class MusicServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
manager.notify(0, notify);
startService();
}
}
再通过一个Service播放音频
public class PlayService extends IntentService {
public static MyPlay mPlay;
public static SeekBar sb_play;
private ItemData intentBean;
public static int playId = -1;
public static int intentId = -1;
public PlayService() {
super("PlayService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
intentBean = (ItemData) intent.getSerializableExtra("intentBean");
intentId = intentBean.getId();
if (intentId != playId && mPlay != null) {
//说明是不同的列表,将当前的清空,再进行播放,相同的话,就设置当前的Post
mPlay.stop();
mPlay = null;
}
if (mPlay == null) {
mPlay = new MyPlay(sb_play, new MyPlay.onShowTimeListener() {
@Override
public void showTime(long time, long endtime) {
if (DateUtil.setHmsQian(time).equals(DateUtil.setHmsQian(endtime))) {
//说明是播放完成了
mPlay.playState = 0;
}
UpdateTimeBean updateTimeBean = new UpdateTimeBean();
updateTimeBean.setStartTime(time);
updateTimeBean.setEndTime(endtime);
EventBus.getDefault().post(updateTimeBean);
}
});
}
playId = intentBean.getId();
if (mPlay.isPalyState() == 0) {
thread.start();
} else if (mPlay.isPalyState() == 1) {
mPlay.pause();
setState(false);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play);
manager.notify(0, notify);
}
} else {
mPlay.play();
setState(true);
if (notify != null) {
notify.contentView.setImageViewResource(R.id.iv_notify_play, R.drawable.img_play_stop);
manager.notify(0, notify);
}
}
}
}
public void setState(boolean state) {
BPlayStateBean bean = new BPlayStateBean();
bean.setSelect(state);
EventBus.getDefault().post(bean);
}
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
if (!TextUtils.isEmpty(intentBean.getUrl()) && mPlay != null) {
mPlay.playUrl(intentBean.getUrl());
if (mPlay.isPalyState() == 1) {
setState(true);
} else {
setState(false);
}
} else {
showMessage("该音频为空");
}
}
});
private void showMessage(String msg) {
ToastBean bean = new ToastBean();
bean.setMsg(msg);
EventBus.getDefault().post(bean);
}
}
//在DateUtil中转换时间
/**
* 给一个时间,然后算出总长度hms/1000
**/
public static String setHmsQian(long time) {
long hour = (time / (60 * 60 * 1000));
long min = ((time / (60 * 1000)) - hour * 60);
long s = (time / 1000 - hour * 60 * 60 - min * 60);
String str_s = s + "";
String str_m = min + "";
String str_h = hour + "";
if (min < 10) {
str_m = "0" + min;
}
if (s < 10) {
str_s = "0" + s;
}
if (hour < 10) {
str_h = "0" + hour;
}
if (hour <= 0) {
return str_m + ":" + str_s;
} else {
return str_h + ":" + str_m + ":" + str_s;
}
}
播放界面的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#008888"
android:gravity="center_horizontal"
android:orientation="vertical">
<SeekBar
android:id="@+id/sb_play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:maxHeight="1dp"
android:progressDrawable="@color/white"
android:thumbTint="@color/c_ff7a7a" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:paddingLeft="12dp"
android:paddingRight="12dp">
<TextView
android:id="@+id/tv_starttime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="00:00"
android:textColor="@color/white"
android:textSize="13sp" />
<TextView
android:id="@+id/tv_endtime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:text="00:00"
android:textColor="@color/white"
android:textSize="13sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:src="@drawable/select_playvideo" />
<ImageView
android:id="@+id/iv_roat"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:layout_marginTop="20dp"
android:src="@drawable/img_share_friend" />
</LinearLayout>
通知栏的布局文件
<?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="match_parent"
android:gravity="center_vertical">
<ImageView
android:id="@+id/iv_notify_head"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/img_default_head" />
<TextView
android:id="@+id/tv_notify_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/iv_notify_head"
android:layout_toEndOf="@+id/iv_notify_head"
android:layout_toRightOf="@+id/iv_notify_head"
android:paddingBottom="10dp"
android:paddingRight="20dp"
android:text="标题1"
android:textSize="15sp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/iv_notify_head"
android:layout_below="@id/tv_notify_title"
android:layout_toRightOf="@id/iv_notify_head">
<ImageView
android:id="@+id/tv_notify_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingRight="20dp"
android:src="@drawable/img_left" />
<ImageView
android:id="@+id/iv_notify_play"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/tv_notify_left"
android:src="@drawable/img_play" />
<ImageView
android:id="@+id/tv_notify_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv_notify_play"
android:paddingLeft="20dp"
android:paddingRight="10dp"
android:src="@drawable/img_right" />
</RelativeLayout>
</RelativeLayout>
在就是在AndroidManifest中启动服务和网络请求的权限
<activity android:name=".BPlayActivity" />
<uses-permission android:name="android.permission.INTERNET" />
public class MyApplication extends Application {
public static Context mContext = null;
@Override
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext() {
return mContext;
}
}
在build.gradle中添加引用,里面有些其他引用不用管
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
//compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
compile 'de.greenrobot:eventbus:3.0.0-beta1'
testCompile 'junit:junit:4.12'
}
一个demo就基本完成了。
上一篇: Ubuntu下安装glad
下一篇: 树莓派:DIY电视盒子