说明:
- 音频控件持有类 - 单例实现
- 功能:
-
- 播放播放
-
- 暂停播放
-
- 停止播放
-
- 开始一段音频时,暂停其他所有正在播放
-
- 全局的进度回调定时器,
-
- 单独获取音频时长时应该先调用 getMp3Length 方法获取,结果会在回调中给出,单位是秒.假如此方法返回的结果是-1,则需要调用pendPlay方法再次获取一次音频时长
-
- 音频时长的获取需要一个第三放库 jaudiotagger-2.2.6-PATHRIK 作为基础.这是一个开源项目
-
- 播放速度调整-setSpeed
-
- 获得当前播放速度-getCurrentSpeed
- 10.是否正在播放-isPlaying
- 11.获得频繁的播放位置回调-getFrequentlyPosition,只需要在监听onPlaying方法中调用此方法即可无需取消。即可做到按您定制的时间进行进度回调。暂停时自动取消。
import android.media.AudioManager;
import android.os.Build;
import android.text.TextUtils;
import android.view.Gravity;
import com.koo.ielt.utils.function.Log;
import com.koo.ielt.utils.function.ToastUtils;
import com.koo.ielt.utils.function.rx.RxJavaCallBack;
import com.koo.ielt.utils.function.rx.RxJavaManager;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import tvgk.danmaku.ijk.media.player.IMediaPlayer;
import tvgk.danmaku.ijk.media.player.IjkMediaPlayer;
/**
* 作者: zhou
* 时间:
* 说明:
* 音频控件持有类 - 单例实现 用于替代 MediaPlayerHolder2
*
* 注意:
* 1. key在播放时的唯一性,如果是同一列表,key值应该唯一.这样在播放列表中下一个音频的时候,上一个音频才会被停止,以此避免创建多个MediaPlayer.
* 否则会创建一个新的MediaPlayer但是上一个播放依然会暂停.
* 2. 所有播放都需要在最后destory界面的时候release方法来进行销毁map中保存的MediaPlayer
*
* 功能:
* 1. 播放播放
* 2. 暂停播放
* 3. 停止播放
* 4. 开始一段音频时,暂停其他所有正在播放
* 5. 全局的进度回调定时器,
* 6. 单独获取音频时长时应该先调用 getMp3Length 方法获取,结果会在回调中给出,单位是秒.假如此方法返回的结果是-1,则需要调用pendPlay方法再次获取一次音频时长
* 7. 音频时长的获取需要一个第三放库 jaudiotagger-2.2.6-PATHRIK 作为基础.这是一个开源项目
* 8. 播放速度调整-setSpeed
* 9. 获得当前播放速度-getCurrentSpeed
* 10.是否正在播放-isPlaying
* 11.获得频繁的播放位置回调-getFrequentlyPosition,只需要在监听onPlaying方法中调用此方法即可无需取消。即可做到按您定制的时间进行进度回调。暂停时自动取消。
*/
public class AudioPlayHelper {
private String TAG = "AudioPlayHelper";
/**
* 更新进度的间隔
*/
private final int peridTime = 1000;
private Disposable mDisposable;
private Disposable mFrequentlyDisposable;
private static AudioPlayHelper audioPlayHelper = new AudioPlayHelper();
/**
* 装MediaPlayer的Map,播放的时候add进去,界面销毁的时候remove
* key1,tag
* key2,url
* value mediaHolder
*/
private HashMap<String, HashMap<String, MediaHolder>> mMediaPlayerMap = new HashMap<>();
// private AudioManager mAudioManager = (AudioManager) IeltsApp.getInstance().getBaseContext().getSystemService(AUDIO_SERVICE);
private AudioPlayHelper() {
}
public static AudioPlayHelper getInstance() {
return audioPlayHelper;
}
private void addMediaPlayer(String tag, String key, MediaHolder mediaHolder) {
if (mMediaPlayerMap.get(tag) != null) {
mMediaPlayerMap.get(tag).put(key, mediaHolder);
} else {
HashMap<String, MediaHolder> map = new HashMap<>();
map.put(key, mediaHolder);
mMediaPlayerMap.put(tag, map);
}
}
private void removeMediaPlayer(String tag, String key) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null){
mMediaPlayerMap.get(tag).remove(key);
}
}
private void removeMediaPlayerByTag(String tag) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null){
mMediaPlayerMap.remove(tag);
}
}
/**
* 释放媒体资源
**/
public void release(String tag, String key) {
if (isPlayerNotNull(tag, key)) {
mMediaPlayerMap.get(tag).get(key).mediaPlayer.stop();
mMediaPlayerMap.get(tag).get(key).mediaPlayer.release();
removeMediaPlayer(tag, key);
}
stopTimer();
}
/**
* 设置播放速度
**/
public boolean setSpeed(String tag, String key,float speed) {
if (isPlayerNotNull(tag, key)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mMediaPlayerMap.get(tag).get(key).mediaPlayer.setSpeed(speed);
return true;
} else {
ToastUtils.show("对不起请升级手机系统至Android6.0及以上");
return false;
}
}
return false;
}
/**
* 获得当前播放速度
**/
public float getCurrentSpeed(String tag, String key) {
if (isPlayerNotNull(tag, key)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return mMediaPlayerMap.get(tag).get(key).mediaPlayer.getSpeed(1);
}
}
return 1.0f;
}
/**
* 释放媒体资源
**/
public long getCurrentPosition(String tag, String key) {
if (isPlayerNotNull(tag, key)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return mMediaPlayerMap.get(tag).get(key).mediaPlayer.getCurrentPosition();
}
}
return 0;
}
/**
* 获得某个音频的快速进度回调,用于做拦截进度从而定制自己相对应的功能,比如说单句循环。
* @param tag
* @param key
* @param time 自定义每隔多久进行一次进度回调
* @param progress
*/
public void getFrequentlyPosition(String tag,String key,int time,AudioPlayPosition progress){
if(mFrequentlyDisposable==null){
Observable.interval(time, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable disposable) {
mFrequentlyDisposable = disposable;
}
@Override
public void onNext(Long number) {
if(isPlayerNotNull(tag,key)){
IjkMediaPlayer player = mMediaPlayerMap.get(tag).get(key).mediaPlayer;
if(player.isPlaying()){
progress.onPosition(player.getCurrentPosition());
}else{
mFrequentlyDisposable.dispose();
mFrequentlyDisposable = null;
}
}else{
mFrequentlyDisposable.dispose();
mFrequentlyDisposable = null;
}
Log.i("----->","AudioPlayHelper-timer-running");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
/**
* 释放指定tag的播放
*/
public void releaseByTag(String tag) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null){
for (MediaHolder mediaHolder : mMediaPlayerMap.get(tag).values()) {
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {
mediaHolder.mediaPlayer.stop();
mediaHolder.mediaPlayer.release();
mediaHolder.playListener.onRelease();
}
}
mMediaPlayerMap.remove(tag);
}
stopTimer();
}
/**
* 释放所有除了给定key以外的其他的播放
*/
public void releaseByTagExceptKey(String tag,String key) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null){
for (Iterator<Map.Entry<String,MediaHolder>> it = mMediaPlayerMap.get(tag).entrySet().iterator();it.hasNext();) {
Map.Entry<String,MediaHolder> entry = it.next();
if(entry.getKey()!=null && !entry.getKey().equals(key)){
if(entry.getValue()!=null && entry.getValue().mediaPlayer!=null && entry.getValue().playListener!=null){
entry.getValue().mediaPlayer.stop();
entry.getValue().mediaPlayer.release();
entry.getValue().playListener.onPause();
entry.getValue().playListener.onRelease();
it.remove();
}
}
}
}
stopTimer();
}
// /**
// * 暂停播放
// * @param key
// */
// public void pause(String key){
// if (isPlayerNotNull(key) && mMediaPlayerMap.get(key).mediaPlayer.isPlaying()) {
// mMediaPlayerMap.get(key).mediaPlayer.pause();
// }
// }
/**
* 滑动播放到某个位置
**/
public void seekTo(String tag,String key,int position) {
if (isPlayerNotNull(tag,key)) {
mMediaPlayerMap.get(tag).get(key).mediaPlayer.seekTo(position);
}
}
/**
* 是否正在播放
**/
public boolean isPlaying(String tag,String key) {
if (isPlayerNotNull(tag,key)) {
return mMediaPlayerMap.get(tag).get(key).mediaPlayer.isPlaying();
}
return false;
}
/**
* pos = -1 则不跳转到指定位置
*
* @param tag
* @param key
* @param url
* @param listener
* @param pos
*/
public void startPlay(String tag, String key, String url, AudioPlayListener listener, int pos) {
if (TextUtils.isEmpty(tag)) {
ToastUtils.show("tag不可以为空");
return;
}
if (TextUtils.isEmpty(key)) {
ToastUtils.show("key不可以为空");
return;
}
if (TextUtils.isEmpty(url)) {
ToastUtils.show("url不可以为空");
return;
}
if (listener == null) {
ToastUtils.show("listener不可以为空");
return;
}
if (!url.endsWith("mp3")) {//url为空,或者非Mp3格式,则提示return,此校检只在此处做一次,后面其他地方都不用检查
ToastUtils.show("此链接非mp3格式,不支持");
return;
}
MediaHolder mediaHolder = null;
if (mMediaPlayerMap.get(tag) != null && mMediaPlayerMap.get(tag).get(key) != null) {
mediaHolder = mMediaPlayerMap.get(tag).get(key);
}
//播放逻辑,没有播放过的,则创建,然后直接播放
if (mediaHolder == null) {//缓存的mediaPlayer为空则创建
//避免切换列表音频时的进度回跳
if(pos!=-1){
listener.onPositionChanged(pos);
}
//暂停其他所有的
pauseAll();
mediaHolder = new MediaHolder(newMediaPlayer(listener, pos), listener);
addMediaPlayer(tag, key, mediaHolder);
loadMedia(url, mediaHolder);
} else {
//正在播放的,如果pos==-1则暂停播放,否则调整位置
//未正在播放的,如果pos==-1.则开始从头播放,否则从pos位置开始播放
if (mediaHolder.mediaPlayer.isPlaying()) {
if (pos == -1) {//正在播放中重复点击播放,则暂停
mediaHolder.playListener.onPause();
mediaHolder.mediaPlayer.pause();
} else {//播放中改变播放位置
if (mediaHolder.mediaPlayer.getDuration() >= pos) {
mediaHolder.playListener.onPositionChanged(pos);
mediaHolder.mediaPlayer.seekTo(pos);
mediaHolder.playListener.onPlaying();
}
}
} else {
if (pos == -1) {//没有播放点击开始播放
pauseAll();
mediaHolder.mediaPlayer.start();
mediaHolder.playListener.onPlaying();
} else {//没有播放状态下改变位置
mediaHolder.playListener.onPositionChanged(pos);
pauseAll();
mediaHolder.mediaPlayer.seekTo(pos);
mediaHolder.mediaPlayer.start();
mediaHolder.playListener.onPlaying();
}
}
}
//每次start时检测一下是否进度更新timer
startTimer();
}
/**
* 提前加载数据获取一下音频时长,用于服务器无法给出音频时长的情况.
*
* @param url
* @param listener
*/
public void pendPlay(String url, AudioPlayListener listener) {
if (TextUtils.isEmpty(url)) {
return;
}
if (listener == null) {
return;
}
if (!url.endsWith("mp3")) {
return;
}
try {
IjkMediaPlayer mMediaPlayer = new IjkMediaPlayer();
mMediaPlayer.setDataSource(url);
mMediaPlayer.setOnPreparedListener(player -> {
player.setKeepInBackground(false);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
listener.allTimeInit(player.getDuration());
mMediaPlayer.pause();
mMediaPlayer.stop();
mMediaPlayer.release();
});
mMediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
public void getMp3Length(String url,AudioLength audioLength){
//解决低版本手机不支持使用此库获取音频时间的问题(低版本调用内部api file.toPath时会崩溃)
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.O){
RxJavaManager.rxJavaRun(new RxJavaCallBack<Integer>() {
@Override
public Integer onExecute() {
try {
MP3File file = new MP3File(url);
MP3AudioHeader audioHeader = (MP3AudioHeader) file.getAudioHeader();
return audioHeader.getTrackLength();
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
@Override
public void onExecuteFinish(Integer integer) {
audioLength.onTimeFetch(integer);
}
});
}else{
audioLength.onTimeFetch(-1);
}
}
/**
* 创建,并初始化MediaPlayer
*
* @return
*/
private IjkMediaPlayer newMediaPlayer(AudioPlayListener listener, int pos) {
IjkMediaPlayer mMediaPlayer = new IjkMediaPlayer();
//注册,播放完成后的监听
mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
mMediaPlayer.setOnCompletionListener(iMediaPlayer -> {
Log.d(TAG, "onCompletion");
listener.onCompletion(iMediaPlayer.getDuration());
});
// mMediaPlayer.setOnSeekCompleteListener(new IMediaPlayer.OnSeekCompleteListener(){
// @Override
// public void onSeekComplete(IMediaPlayer iMediaPlayer) {
// iMediaPlayer.start();
// }
// });
mMediaPlayer.setOnInfoListener((iMediaPlayer, i, i1) -> false);
//不能用这种方式进行进度调整,
// mMediaPlayer.setOnSeekCompleteListener(player->{
// Log.d(TAG, "OnSeekComplete");
// listener.onPositionChanged(player.getCurrentPosition());
// });
//监听媒体流是否装载完成
mMediaPlayer.setOnPreparedListener(new IjkMediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
Log.d(TAG, "onPrepared");
if (pos != -1 && mMediaPlayer.getDuration() > pos) {
mMediaPlayer.seekTo(pos);
mMediaPlayer.start();
}
listener.onPositionChanged(iMediaPlayer.getCurrentPosition());
listener.onPrepared();
listener.onPlaying();
listener.allTimeInit(iMediaPlayer.getDuration());
}
});
/**
* @date: 2019/6/21 0021
* @description:监听媒体错误信息
**/
mMediaPlayer.setOnErrorListener(new IjkMediaPlayer.OnErrorListener() {
@Override
public boolean onError(IMediaPlayer iMediaPlayer, int what, int extra) {
listener.onError();
Log.d(TAG, "OnError - Error code: " + what + " Extra code: " + extra);
switch (what) {
case -1004:
Log.d(TAG, "MEDIA_ERROR_IO");
break;
case -1007:
Log.d(TAG, "MEDIA_ERROR_MALFORMED");
break;
case 200:
Log.d(TAG, "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK");
break;
case 100:
Log.d(TAG, "MEDIA_ERROR_SERVER_DIED");
break;
case -110:
Log.d(TAG, "MEDIA_ERROR_TIMED_OUT");
break;
case 1:
Log.d(TAG, "MEDIA_ERROR_UNKNOWN");
break;
case -1010:
Log.d(TAG, "MEDIA_ERROR_UNSUPPORTED");
break;
}
switch (extra) {
case 800:
Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING");
break;
case 702:
Log.d(TAG, "MEDIA_INFO_BUFFERING_END");
break;
case 701:
Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE");
break;
case 802:
Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE");
break;
case 801:
Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE");
break;
case 1:
Log.d(TAG, "MEDIA_INFO_UNKNOWN");
break;
case 3:
Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START");
break;
case 700:
Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING");
break;
}
return false;
}
});
return mMediaPlayer;
}
/**
* @date: 2019/6/24 0024
* @description: 加载媒体资源
**/
private void loadMedia(String playUrl, MediaHolder mediaHolder) {
try {
mediaHolder.playListener.onPreparing();
mediaHolder.mediaPlayer.setKeepInBackground(false);
mediaHolder.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaHolder.mediaPlayer.setDataSource(playUrl);
mediaHolder.mediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 简化AudioPlayListener子类的实现,让子类用哪个实现哪个
*/
public abstract static class EasyAudioPlayListener implements AudioPlayListener {
@Override
public void onPreparing() {
}
@Override
public void onPrepared() {
}
@Override
public void onPlaying() {
}
@Override
public void onPositionChanged(long position) {
}
@Override
public void onPause() {
}
@Override
public void onError() {
}
@Override
public void onCompletion(long position) {
}
@Override
public void allTimeInit(long time) {
}
}
public interface AudioPlayListener {
/**
* 媒体流加载中
*/
void onPreparing();
/**
* 媒体流加载完成
*/
void onPrepared();
/**
* 播放中
*/
void onPlaying();
/**
* 暂停播放
*/
void onPause();
/**
* 控件释放时调用
*/
void onRelease();
/**
* 播放完成
*/
void onCompletion(long position);
/**
* 播放错误
*/
void onError();
/**
* 播放进度改变
*
* @param position
*/
void onPositionChanged(long position);
/**
* 总时长初始化
*
* @param position
*/
void allTimeInit(long position);
}
public interface AudioPlayPosition{
void onPosition(long position);
}
/**
* @date: 2019/6/21 0021
* @description:开启线程,获取当前播放的进度
**/
private void startTimer() {
if (mDisposable == null) {
Observable.interval(peridTime, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable disposable) {
mDisposable = disposable;
}
@Override
public void onNext(Long number) {
notifyPositionChange();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
/**
* 通知全局正在播放的listener改变当前播放进度
*/
private void notifyPositionChange() {
for (HashMap<String, MediaHolder> map : mMediaPlayerMap.values()) {
for (MediaHolder mediaHolder : map.values()) {
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {
mediaHolder.playListener.onPositionChanged(mediaHolder.mediaPlayer.getCurrentPosition());
}
}
}
}
/**
* 暂停所有其他的播放
*/
public void pauseAll() {
for (HashMap<String, MediaHolder> map : mMediaPlayerMap.values()) {
for (MediaHolder mediaHolder : map.values()) {
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {
mediaHolder.mediaPlayer.pause();
mediaHolder.playListener.onPause();
}
}
}
stopTimer();
}
/**
* 暂停所有其他的播放
*/
public void pauseByTag(String tag) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null){
for (MediaHolder mediaHolder : mMediaPlayerMap.get(tag).values()) {
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {
mediaHolder.mediaPlayer.pause();
mediaHolder.playListener.onPause();
}
}
}
stopTimer();
}
/**
* 增加根据tag和url继续播放的方法.
*/
public void startByTagKey(String tag,String key) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null && mMediaPlayerMap.get(tag).get(key)!=null){
MediaHolder mediaHolder = mMediaPlayerMap.get(tag).get(key);
if (mediaHolder != null && mediaHolder.mediaPlayer != null && !mediaHolder.mediaPlayer.isPlaying()) {
pauseAll();
mediaHolder.mediaPlayer.start();
mediaHolder.playListener.onPlaying();
startTimer();
}
}
}
/**
* 增加根据tag和url来判断是否正在播放的方法.
*/
public boolean isPlayingByTagKey(String tag,String key) {
if(mMediaPlayerMap!=null && mMediaPlayerMap.get(tag)!=null && mMediaPlayerMap.get(tag).get(key)!=null){
MediaHolder mediaHolder = mMediaPlayerMap.get(tag).get(key);
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {
return true;
}
}
return false;
}
/**
* 遍历 mMediaPlayerMap 如果没有任何在player正在播放,则停止定时器
*/
private void stopTimer() {
if (mMediaPlayerMap.size() > 0) {
for (HashMap<String, MediaHolder> map : mMediaPlayerMap.values()) {
for (MediaHolder mediaHolder : map.values()) {
if (mediaHolder != null && mediaHolder.mediaPlayer != null && mediaHolder.mediaPlayer.isPlaying()) {//有正在播放的,直接return
return;
}
}
}
//没有return表示没有正在播放的
if (mDisposable != null) {
mDisposable.dispose();
mDisposable = null;
}
}
}
private boolean isPlayerNotNull(String tag, String key) {
return mMediaPlayerMap.get(tag) != null && mMediaPlayerMap.get(tag).get(key) != null && mMediaPlayerMap.get(tag).get(key).mediaPlayer != null;
}
/**
* 保存MediaPlayer 和 listener的数据结构,因为设置定时器回调时间时会需要listener
*/
private class MediaHolder {
public IjkMediaPlayer mediaPlayer;
public AudioPlayListener playListener;
public MediaHolder(IjkMediaPlayer mediaPlayer, AudioPlayListener playListener) {
this.mediaPlayer = mediaPlayer;
this.playListener = playListener;
}
}
public interface AudioLength{
void onTimeFetch(int length);
}
}