公司主要跟音频视频杠上了,视频还好说,有完美的第三方框架,傻瓜式的拉过来修改一下就能用,视频用的gsyVideoPlayer,这个框架非常不错,链接:https://blog.csdn.net/qq_24531461/article/details/73456794,音频我也没经验啊,逼得紧,拿着mediaplayer就开整。中间的曲折就不说了,直接上图:
技术点:
1:单例封装,这个单例是当前页面和服务持有的,主要是mediaplayer的持有方法。
2:service,主要播放要放在服务里,在当前页面绑定回调服务
3:定时播放采用的是线程1秒发一次,这个主要是要回调数据显示,所以只能这么搞
4:倍速,神坑,不同手机不一样
5:关闭播放器,就是后台正在播放,游客再看其他页面,这个时候退出了或者更换账号
一:单例
public class Voice_Utils {
private Voice_Utils() {
}
private static Voice_Utils lei = new Voice_Utils();
//单例模式
public synchronized static Voice_Utils getInstance() {
return lei;
}
//播放方法
public void play(MediaPlayer mediaplayer) {
mediaplayer.start();
}
//暂停
public void pause(MediaPlayer mediaplayer) {
mediaplayer.pause();
}
//判断是否正在播放中
public boolean isplay(MediaPlayer mediaplayer) {
return mediaplayer.isPlaying();
}
//获取播放时长
public long getduring(MediaPlayer mediaplayer) {
// TODO Auto-generated method stub
return mediaplayer.getDuration();
}
//获取当前的播放进度
public long getcurrentduring(MediaPlayer mediaplayer) {
// TODO Auto-generated method stub
return mediaplayer.getCurrentPosition();
}
//获取位置
public int position(int current) {
// TODO Auto-generated method stub
return current;
}
//更上进度,设置进度..
public void curento(int position, MediaPlayer mediaplayer) {
mediaplayer.seekTo(position);
}
/**
* 关闭播放器
*/
public void closeMedia(MediaPlayer mediaplayer) {
if (mediaplayer != null) {
if (mediaplayer.isPlaying()) {
mediaplayer.stop();
}
mediaplayer.release();
}
}
}
二:服务封装,因人而异,我写的也不一定对,很多地方待优化
//TODO 开启了一个异步任务
class MyTask extends AsyncTask<Void, Void, Void> {
@Override //操作之前-- MainThread
protected void onPreExecute() {
super.onPreExecute();
instance = Voice_Utils.getInstance();
}
@Override //耗时操作在这里
protected Void doInBackground(Void... voids) {
//缓存
if (!ClassPathResource.isEmptyOrNull(urls)) {
HttpProxyCacheServer proxy = MyApplication.getProxy(Services.this);
proxyUrl = proxy.getProxyUrl(urls);
Share.d("proxyUrl" + proxyUrl);
if (urls != null && mMediaPlayer == null) {//判断执行如下操作-创建媒体播放类
mMediaPlayer = MediaPlayer.create(Services.this, Uri.parse(proxyUrl));
} else if (urls != null && mMediaPlayer != null) {
String mp3 = (String) SharePrenfencesUtil.get(Services.this, "mp3", urls);
if (mp3.equals(urls)) {
Log.e("The", "Same");//同步播放进度
} else {
try {
mMediaPlayer.reset();//重置
mMediaPlayer.setDataSource(Services.this, Uri.parse(proxyUrl));
try {
mMediaPlayer.prepare();
} catch (IOException e) {
Log.e("media_player", "-prepare-");
} catch (IllegalStateException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
//此方法为随时更新函数,使用需在上面调用 publicProgress();
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
@Override //操作完成-- MainThread
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (mMediaPlayer != null) {
//如果他ture说明没有创建播放器 让手动点击创建播放
if (type.equals("auto")) {
instance.play(mMediaPlayer);//播放响应1
sendBroadcast(new Intent("playing"));
}
}
if (urls != null) {
SharePrenfencesUtil.put(Services.this, "mp3", urls);
}
try { //回调
Share_utils.getInstance().getMedia_playerCallBack().ComingPlayer(mMediaPlayer);
} catch (Exception e) {
Log.e("TAG", "ZhiXin ComingPlayer is null");
}
}
}
三:定时关闭回显
package club.modernedu.lovebook.ui;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.view.KeyEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import com.bigkoo.pickerview.builder.TimePickerBuilder;
import com.bigkoo.pickerview.listener.OnTimeSelectListener;
import com.bigkoo.pickerview.view.TimePickerView;
import org.greenrobot.eventbus.EventBus;
import java.text.SimpleDateFormat;
import java.util.Date;
import club.modernedu.lovebook.R;
import club.modernedu.lovebook.base.BaseActivity;
import club.modernedu.lovebook.contants.CountdownTimerEvent;
import club.modernedu.lovebook.contants.PlayerEvent;
import club.modernedu.lovebook.receivers.AlarmReceiver;
import club.modernedu.lovebook.utils.SPUtils;
import club.modernedu.lovebook.utils.Share;
import club.modernedu.lovebook.utils.StatusBarUtil;
import static android.view.KeyEvent.KEYCODE_BACK;
/**
* Created by jiangbin on 2018/7/21 17:51
* 定时关闭
*/
public class TimeOffActivity extends BaseActivity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener {
private LinearLayout back;
private TextView title;
private RadioGroup speed_rg;
private RadioButton speed_rb1;
private RadioButton speed_rb2;
private RadioButton speed_rb3;
private RadioButton speed_rb4;
private RadioButton speed_rb5;
private RadioButton speed_rb6;
private RadioButton speed_rb7;
private RadioButton speed_rb8;
private TextView time_tv;
private Context context;
private int triggerAtTime;
private int countdownTime;
private String eventType = "ok";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置状态栏字体颜色
StatusBarUtil.setColor(TimeOffActivity.this, getResources().getColor(R.color.main_color), 0);
//底部虚拟按键的颜色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.virtual_buttons));
}
setContentView(R.layout.activity_timeoff);
context = TimeOffActivity.this;
initView();
}
private void initView() {
back = (LinearLayout) findViewById(R.id.back);
title = (TextView) findViewById(R.id.title);
speed_rg = (RadioGroup) findViewById(R.id.speed_rg);
speed_rb1 = (RadioButton) findViewById(R.id.speed_rb1);
speed_rb2 = (RadioButton) findViewById(R.id.speed_rb2);
speed_rb3 = (RadioButton) findViewById(R.id.speed_rb3);
speed_rb4 = (RadioButton) findViewById(R.id.speed_rb4);
speed_rb5 = (RadioButton) findViewById(R.id.speed_rb5);
speed_rb6 = (RadioButton) findViewById(R.id.speed_rb6);
speed_rb7 = (RadioButton) findViewById(R.id.speed_rb7);
speed_rb8 = (RadioButton) findViewById(R.id.speed_rb8);
//time_tv = (TextView)findViewById(R.id.time_tv);
speed_rg.setOnCheckedChangeListener(this);
speed_rg.check(R.id.speed_rb1);
title.setText("定时关闭");
back.setOnClickListener(this);
//判断选择哪个
String offtime = (String) SPUtils.get(TimeOffActivity.this, "offtime", "");
if (offtime.equals("10")) {
speed_rb2.setChecked(true);
} else if (offtime.equals("20")) {
speed_rb3.setChecked(true);
} else if (offtime.equals("30")) {
speed_rb4.setChecked(true);
} else if (offtime.equals("60")) {
speed_rb5.setChecked(true);
} else if (offtime.equals("90")) {
speed_rb6.setChecked(true);
} else if (offtime.length() > 0) {
speed_rb7.setChecked(true);
speed_rb7.setText(offtime);
} else {
speed_rb8.setChecked(true);
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.back:
TimeOff();
finish();
break;
default:
break;
}
}
/**
* @time 2018/7/19 15:33
* @Description 切换标签
*/
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
switch (i) {
case R.id.speed_rb1:
//播放完当前默认关闭
//ToastUtils.showToast(SettingSpeedActivity.this, speed_rb1.getText().toString());
break;
case R.id.speed_rb2:
//10分钟
SPUtils.put(TimeOffActivity.this, "offtime", "10");
triggerAtTime = (int) (SystemClock.elapsedRealtime() + 10 * 60 * 1000);
countdownTime = 10 * 60 * 1000;
eventType = "ok";
//ToastUtils.showToast(SettingSpeedActivity.this, speed_rb2.getText().toString());
break;
case R.id.speed_rb3:
//20分钟
SPUtils.put(TimeOffActivity.this, "offtime", "20");
triggerAtTime = (int) (SystemClock.elapsedRealtime() + 20 * 60 * 1000);
countdownTime = 20 * 60 * 1000;
eventType = "ok";
// ToastUtils.showToast(SettingSpeedActivity.this, speed_rb3.getText().toString());
break;
case R.id.speed_rb4:
//30分钟
SPUtils.put(TimeOffActivity.this, "offtime", "30");
triggerAtTime = (int) (SystemClock.elapsedRealtime() + 30 * 60 * 1000);
countdownTime = 30 * 60 * 1000;
eventType = "ok";
//ToastUtils.showToast(SettingSpeedActivity.this, speed_rb4.getText().toString());
break;
case R.id.speed_rb5:
//60分钟
SPUtils.put(TimeOffActivity.this, "offtime", "60");
triggerAtTime = (int) (SystemClock.elapsedRealtime() + 60 * 60 * 1000);
countdownTime = 60 * 60 * 1000;
eventType = "ok";
// ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
break;
case R.id.speed_rb6:
//90分钟
SPUtils.put(TimeOffActivity.this, "offtime", "90");
triggerAtTime = (int) (SystemClock.elapsedRealtime() + 90 * 60 * 1000);
countdownTime = 90 * 60 * 1000;
eventType = "ok";
// ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
break;
case R.id.speed_rb7:
//自定义
//时间选择器
// ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
break;
case R.id.speed_rb8:
//不开启 直接取消现在定时的请求
AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
int requestCode = 0;
PendingIntent pendIntent = PendingIntent.getBroadcast(getApplicationContext(),
requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//先取消一下之前没执行的定时
alarmMgr.cancel(pendIntent);
// ToastUtils.showToast(SettingSpeedActivity.this, speed_rb5.getText().toString());
SPUtils.put(TimeOffActivity.this, "offtime", "");
eventType = "no";
break;
}
speed_rb7.setOnClickListener(new View.OnClickListener() {
//时间选择器 ,在rudiogroup 中只能单击一次,重新这个点击事件让日期控件出来
@Override
public void onClick(View view) {
TimePickerView pvTime = new TimePickerBuilder(TimeOffActivity.this, new OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date, View v) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
String format = simpleDateFormat.format(date);
speed_rb7.setText(format);
SimpleDateFormat simpleDateFormatHours = new SimpleDateFormat("HH");
int hours = Integer.valueOf(simpleDateFormatHours.format(date));
SimpleDateFormat simpleDateFormatMinutes = new SimpleDateFormat("mm");
int minutes = Integer.valueOf(simpleDateFormatMinutes.format(date));
countdownTime = ((hours * 60 + minutes)) * 60 * 1000;
eventType = "ok";
triggerAtTime = (int) (SystemClock.elapsedRealtime() + ((hours * 60 + minutes)) * 60 * 1000);
SPUtils.put(TimeOffActivity.this, "offtime", "自定义");
}
}).setType(new boolean[]{false, false, false, true, true, false}).setLabel("年", "月", "日", "时", "分", "秒").build();
pvTime.show();
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KEYCODE_BACK)) {
TimeOff();
return true;
}
return super.onKeyDown(keyCode, event);
}
private void TimeOff() {
//发送通知到baseactivity里开始倒计时发通知给播放界面显示
CountdownTimerEvent timerEvent = new CountdownTimerEvent();
timerEvent.setEventType(eventType);
timerEvent.setObject(countdownTime);
EventBus.getDefault().post(timerEvent);
Share.d("秒数" + countdownTime);
//广播有差异
// AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
// int requestCode = 0;
// PendingIntent pendIntent = PendingIntent.getBroadcast(getApplicationContext(),
// requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//
// //先取消一下之前没执行的定时
// alarmMgr.cancel(pendIntent);
// // 5秒后发送广播,只发送一次
// //triggerAtTime = (int) (SystemClock.elapsedRealtime() + 10 * 1000);
// alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendIntent);
}
}
四:倍速,我一定要说一说
你以为这样
mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(1.5f));
一开始我也是这样,好多手机都没问题,前提得是6.0以后得啊,但是华为手机就不行,我想了很久,考虑到音频焦点,第二天来公司,打印发现跟音频焦点关系不大,前天晚上查阅了资料有两个细节:
MediaPlayer.setPlaybackParams(PlaybackParams params) throws IllegalStateException, IllegalArgumentException
接口说明:
(1) 使用这个接口可以进行播放速率的设置。
(2) 播放器prepared状态之前调用这个方法不会更改播放器的状态。
(3) prepared状态之后设置速率0等同于调用pause(),当调用start恢复播放以后,将以原来的速率进行播放。
(4) prepared状态之后设置非0的速率等同于调用start()。
(5) 当播放器还未初始化或者已经被释放的时候设置会抛IllegalStateException的异常。
(6) 当参数不支持的时候会抛IllegalArgumentException的异常。
设置时机要求:
合法的时机:Initialized, Prepared, Started, Paused, PlaybackCompleted, Error
非法的时机:Idle, Stopped
啥意思呢:主要是设置倍速相当于先pause,在start,抱着试试的心态,重新了这两个方法结果是OK的,如果直接设置大部分手机可以,但是华为手机会没音,这样设置相当于暂停又开始一下就可以了。具体写法:
if (mediaplayer != null && instance.isplay(mediaplayer)) {
//倍速设置,必须在23以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Share.d("aaaaa"+mediaplayer.isPlaying());
mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(1.5f));
mediaplayer.pause();
mediaplayer.start();
} else {
ToastUtils.showToast(getActivity(), "对不起请升级手机系统至Android6.0及以上");
}
}
五:关闭播放器,这个两个方面,一个是服务,一个是单例持有,服务绑定两种方法,用户退出你是不是要销毁,销毁了单例还持有就报错,这个地方也是朋友商量的在退出的时候发通知到常驻服务里,stopmyself,在ondstory方法调用持有单例销毁播放器吗,这样就不内存溢出了
我说的不一定对,有指正的地方请留言,我及时修改,这个mediaplayer也不是长久之计
网友评论
case R.id.play_speed:
//倍速
// bottomIn();
// intent = new Intent(getActivity(), SetSpeedActivity.class);
// startActivity(intent);
//获取本地设置的倍速
if (mediaplayer != null && instance.isplay(mediaplayer)) {
//倍速设置,必须在23以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2f));
try {
mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2.0f));
} catch (Exception e) {
e.printStackTrace();
Log.e("Tag", "该设备暂不支持设置速率");
HttpProxyCacheServer proxy = MyApplication.getProxy(getActivity());
String proxyUrl = proxy.getProxyUrl(source);
// initPlayer("noauto");
int position = mediaplayer.getCurrentPosition();
mediaplayer.reset();
try {
mediaplayer.setDataSource(getActivity(), Uri.parse(proxyUrl));
mediaplayer.prepare();
mediaplayer.setPlaybackParams(mediaplayer.getPlaybackParams().setSpeed(2.0f));
mediaplayer.seekTo(position);
mediaplayer.start();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} else {
ToastUtils.showToast(getActivity(), "对不起请升级手机系统至Android6.0及以上");
}
}
break;