一. MediaPlayer:
MediaPlayer确实强大,提供了对音频播放的各种控制,生命周期:
1. MediaPlayer支持:AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式
2. 播放Raw下的元数据
mMediaPlayer=MediaPlayer.create(this, R.raw.audio);
mMediaPlayer.start();
3. MediaPlayer设置播放源的4中方式
- setDataSource (String path)
//从sd卡中加载音乐
mMediaPlayer.setDataSource("../music/samsara.mp3") ;
//从网路加载音乐
mMediaPlayer.setDataSource("http://..../xxx.mp3") ;
//需使用异步缓冲
mMediaPlayer.prepareAsync() ;
- setDataSource (FileDescriptor fd)
//需将资源文件放在assets文件夹
AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
mMediaPlayer.setDataSource(fd)
mMediaPlayer.prepare() ;
Ps:此方法系统需大于等于android
- setDataSource (Context context, Uri uri)
这个方法没什么好说的,一般通过ContentProvider获取Android系统提供的共享music获取uri,然后设置数据播放 - setDataSource (FileDescriptor fd, long offset, long length)
//需将资源文件放在assets文件夹
AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
mMediaPlayer.setDataSource(fd, fd.getStartOffset(), fd.getLength())
mMediaPlayer.prepare() ;
注意点
设置完数据源,不要忘记prepare(),尽量使用异步prepareAync(),这样不会阻塞UI线程。
播放完毕即使释放资源
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
不足:资源占用量较高、延迟时间较长、不支持多个音频同时播放等
demo:
//创建MediaPlayer和设置监听
mPlayer = new MediaPlayer() ;
mSeekBar.setOnSeekBarChangeListener(new MySeekBarChangeListener());
mPlayer.setOnPreparedListener(new MyOnPrepareListener());
mPlayer.setOnCompletionListener(new MyOnCompletionListener());
/**
* 从assets资源文件夹中播放
* @param name
*/
private void playSoundFromA(String name) {
if (mPlayer.isPlaying()) {
mPlayer.stop();
}
// 设置当前播放歌曲的名字
title.setText(names[current]);
mPlayer.reset();
AssetFileDescriptor afd = getAssetFileDescriptor(name);
try {
mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
hasResource = true;
mPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
二. SoundPool播放音频
SoundPool支持多个音频文件同时播放(组合音频也是有上限的),延时短,比较适合短促、密集的场景,是游戏开发中音效播放的福音。
1. SoundPool实例化方式
- new SoundPool(适用与5.0以下)
SoundPool(int maxStreams, int streamType, int srcQuality)
从android5.0开始此方法被标记为过时,稍微说以下几个参数。
1.maxStreams :允许同时播放的流的最大值
2.streamType :音频流的类型描述,
在Audiomanager中有种类型声明,游戏应用通常会使用流媒体音乐。
2. srcQuality:采样率转化质量
- SoundPool.Builder(从5.0开始支持)
//设置描述音频流信息的属性
AudioAttributes abs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build() ;
SoundPool mSoundPoll = new SoundPool.Builder()
.setMaxStreams(100) //设置允许同时播放的流的最大值
.setAudioAttributes(abs) //完全可以设置为null
.build() ;
- 重要的方法
// 几个load方法和上文提到的MediaPlayer基本一致,不做多的解释
//------------------------------------------------------------
int load(AssetFileDescriptor afd, int priority)
int load(Context context, int resId, int priority)
int load(String path, int priority)
int load(FileDescriptor fd, long offset, long length, int priority)
//-------------------------------------------------------------
// 通过流id暂停播放
final void pause(int streamID)
// 播放声音,soundID:音频id; left/rightVolume:左右声道(默认1,1);loop:循环次数(-1无限循环);rate:播放速率(1为标准)
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
//释放资源(很重要)
final void release()
//恢复播放
final void resume(int streamID)
//设置指定id的音频循环播放次数
final void setLoop(int streamID, int loop)
//设置加载监听(因为加载是异步的,需要监听加载,完成后再播放)
void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
//设置优先级(同时播放个数超过最大值时,优先级低的先被移除)
final void setPriority(int streamID, int priority)
//设置指定音频的播放速率,0.5~2.0(rate>1:加快播放,反之慢速播放)
final void setRate(int streamID, float rate)
//停止指定音频播放
final void stop(int streamID)
//卸载指定音频
final boolean unload(int soundID)
//暂停所有音频的播放
final void autoPause()
//恢复所有暂停的音频播放
final void autoResum()
以上方法基本上是SoundPool的所有方法了,也都很常用。
- 2个概念
看了Sounpool的api,是不是感觉对 streamID 和 soundID 一脸懵逼?- soundID:加载音乐资源时的返回值,int load(String path, int priority),这个int返回值就是soundID
- streamID:播放时返回的值,即play()方法的返回值
- demo
注:我把SoundPool做了简单封装,SoundPoolUtil,会在文末上传,
有兴趣可下载看一下,时间比较急,还有很多不足的地方
//初始化SoundPool
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
AudioAttributes aab = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build() ;
mSoundPool = new SoundPool.Builder()
.setMaxStreams(10)
.setAudioAttributes(aab)
.build() ;
}else{
mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
}
mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
//设置资源加载监听
mSoundPool.setOnLoadCompleteListener(new MyOnLoadCompleteListener());
//加载资源
/**
* 加载指定路径列表的资源
* @param map
*/
public void loadR(Map<String, String> map){
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String, String> entry : entries){
String key = entry.getKey() ;
if(checkSoundPool()){
if(!idCache.containsKey(key)){
idCache.put(key, mSoundPool.load(entry.getValue(),1)) ;
}
}
}
}
/**
* 播放指定音频,并返用于停止、暂停、恢复的StreamId
* @param name
* @param times
* @return
*/
public int play(String name, int times){
return this.play(name,1,1,1,times,1) ;
}
三. AudioTrack播放音频
AudioTrack属于更偏底层的音频播放,MediaPlayerService的内部就是使用了AudioTrack。
AudioTrack用于单个音频播放和管理,相比于MediaPlayer具有:精炼、高效的优点。
更适合实时产生播放数据的情况,如加密的音频,
MediaPlayer是束手无策的,AudioTrack却可以。
AudioTrack用于播放PCM(PCM无压缩的音频格式)音乐流的回放,
如果需要播放其它格式音频,需要响应的解码器,
这也是AudioTrack用的比较少的原因,需要自己解码音频。
1. AudioTreack的2种播放模式
静态模式—static
静态的言下之意就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作
流模式streaming
流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景,不过我们一般在以下情况下采用:
音频文件过大
音频属性要求高,比如采样率高、深度大的数据
音频数据是实时产生的,这种情况就只能用流模式了
通过write(byte[], int, int), write(short[], int, int)
write(float[], int, int, int)等方法推送解码数据到AudioTrack
Demo
private void jetPlayStream(){
new Thread(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void run() {
// 获取最小缓冲区
int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
// 实例化AudioTrack(设置缓冲区为最小缓冲区的2倍,至少要等于最小缓冲区)
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufSize*2, AudioTrack.MODE_STREAM);
// 设置音量
audioTrack.setVolume(2f) ;
// 设置播放频率
audioTrack.setPlaybackRate(10) ;
audioTrack.play();
// 获取音乐文件输入流
InputStream is = getResources().openRawResource(R.raw.zbc);
byte[] buffer = new byte[bufSize*2] ;
int len ;
try {
while((len=is.read(buffer,0,buffer.length)) != -1){
System.out.println("读取数据中...");
// 将读取的数据,写入Audiotrack
audioTrack.write(buffer,0,buffer.length) ;
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
四. AsyncPlayer播放音频
从名字就可看出AsyncPlayer属于异步播放器,官方给出的说明是:所有工作都在子线程进行,不影响调用线程任何操作。
AsyncPlayer就是对MediaPlayer的一次简单的封装,对MediaPlaer所有的操作都在新开线程中执行。
AsyncPlayer只适合简单的异步播放,不能控制进度,只能开始或停止播放。如果播放在此调用play()方法,AsyncPlayer会停止当前播放,开始新的播放。
五. JetPlayer播放音频
Jet是由OHA联盟成员SONiVOX开发的一个交互音乐引擎。其包括两部分:JET播放器和JET引擎。JET常用于控制游戏的声音特效,采用MIDI(Musical Instrument Digital Interface)格式。
获取实例
//获取JetPlayer播放器
JetPlayer mJet = JetPlayer.getJetPlayer() ;
重要方法
// 清空分段队列,并清除所有要进行播放的剪辑。
1. boolean clearQueue() //每次播放前,记得做一次清空操作
// 加载jet文件的方法
2. boolean loadJetFile(String path)
boolean loadJetFile(AssetFileDescriptor afd)
// 开始播放
3. boolean play()
// 暂停播放
4. boolean pause()
// 释放资源
5. void release()
// 指定jet队列的播放序列(调用play()前需要调用此方法)
6. boolean queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)
Demo
private void jetPlayer(){
// 获取JetPlayer播放器
JetPlayer mJet = JetPlayer.getJetPlayer() ;
//清空播放队列
mJet.clearQueue() ;
//绑定事件监听
mJet.setEventListener(new JetPlayer.OnJetEventListener() {
//播放次数记录
int playNum = 1 ;
@Override
public void onJetEvent(JetPlayer player, short segment, byte track, byte channel, byte controller, byte value) {
Log.i(TAG,"----->onJetEvent") ;
}
@Override
public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
Log.i(TAG,"----->onJetUserIdUpdate") ;
}
@Override
public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
Log.i(TAG,"----->onJetNumQueuedSegmentUpdate") ;
}
@Override
public void onJetPauseUpdate(JetPlayer player, int paused) {
Log.i(TAG,"----->onJetPauseUpdate") ;
if(playNum == 2){
playNum = -1 ;
//释放资源,并关闭jet文件
player.release();
player.closeJetFile() ;
}else{
playNum++ ;
}
}
});
//加载资源
mJet.loadJetFile(getResources().openRawResourceFd(R.raw.level1)) ;
byte sSegmentID = 0 ;
//指定播放序列
mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
mJet.queueJetSegment(1, 0, 1, 0, 0, sSegmentID);
//开始播放
mJet.play() ;
}
六. Ringtone
Ringtone为铃声、通知和其他类似声音提供快速播放的方法,这里还不得不提到一个管理类”RingtoneManager”,提供系统铃声列表检索方法,并且,Ringtone实例需要从RingtoneManager获取。
获取实例
获取实例方法,均为RingtoneManager类提供
//通过铃声uri获取
static Ringtone getRingtone(Context context, Uri ringtoneUri)
//通过铃声检索位置获取
Ringtone getRingtone(int position)
其实,Rington这个类比较简单,只需要掌握,播放、停止(paly(),stop())等方法就可以了,而RingtoneManager却是比较重要的。
重要方法
1. // 两个构造方法
RingtoneManager(Activity activity)
RingtoneManager(Context context)
2. // 获取指定声音类型(铃声、通知、闹铃等)的默认声音的Uri
static Uri getDefaultUri(int type)
3. // 获取系统所有Ringtone的cursor
Cursor getCursor()
4. // 获取cursor指定位置的Ringtone uri
Uri getRingtoneUri(int position)
5. // 判断指定Uri是否为默认铃声
static boolean isDefault(Uri ringtoneUri)
6. //获取指定uri的所属类型
static int getDefaultType(Uri defaultRingtoneUri)
7. //将指定Uri设置为指定声音类型的默认声音
static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)
从api看,Ringtone和RingtoneManager还是比较简单的,不多做解释了,直接放上一段使用代码。
/**
* 播放来电铃声的默认音乐
*/
private void playRingtoneDefault(){
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ;
Ringtone mRingtone = RingtoneManager.getRingtone(this,uri);
mRingtone.play();
}
/**
* 随机播放一个Ringtone(有可能是提示音、铃声等)
*/
private void ShufflePlayback(){
RingtoneManager manager = new RingtoneManager(this) ;
Cursor cursor = manager.getCursor();
int count = cursor.getCount() ;
int position = (int)(Math.random()*count) ;
Ringtone mRingtone = manager.getRingtone(position) ;
mRingtone.play();
}
//记得添加下面两个权限
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
筛选方案:
对于延迟度要求不高,并且希望能够更全面的控制音乐的播放,MediaPlayer比较适合
声音短小,延迟度小,并且需要几种声音同时播放的场景,适合使用SoundPool
对于简单的播放,不需要复杂控制的播放,可以给使用AsyncPlayer,所有操作均在子线程不阻塞UI
播放大文件音乐,如WAV无损音频和PCM无压缩音频,可使用更底层的播放方式AudioTrack。它支持流式播放,可以读取(可来自本地和网络)音频流,却播放延迟较小。
ps:据我测试AudioTrack直接支持WAV和PCM,其他音频需要解码成PCM格式才能播放。(其他无损格式没有尝试,有兴趣可以使本文提供的例子测试一下)
.jet的音频比较少见(有的游戏中在使用),可使用专门的播放器JetPlayer播放
对于系统类声音的播放和操作,Ringtone更适合(主要是掌握好RingtoneManager)
android.media包中提供的播放音频的方式,远不止这些,本文只是参考api和其他大牛的博客做一些研究和记录,android.media种还有很多只是等着我们探索……
MediaPlay demo
- 接口api
import androidx.annotation.RawRes;
public interface IPlayerApi {
/**
* 加载媒体资源
*
* @param musiUrl
*/
void loadMedia(String musiUrl, OnPrepareCompletedListener listener);
/**
* 加载元数据媒体资源
*
* @param musicRawId
*/
void loadMedia(@RawRes int musicRawId, OnPrepareCompletedListener listener);
/**
* 释放资源
*/
void release();
/**
* 判断是否在播放
*
* @return
*/
boolean isPlaying();
/**
* 开始播放
*/
void play();
/**
* 重置
*/
void reset();
/**
* 暂停
*/
void pause();
/**
* 滑动到某个位置
*/
void seekTo(int position);
}
- 资源准备完成
public interface OnPrepareCompletedListener {
void onComplete();
}
- 播放监听
public interface PlaybackInfoListener {
void onTotalDuration(int duration);//总时长
void onPositionChanged(int position);//当前时长进度
void onStateChanged(int state);//记录当前的状态
void onPlayCompleted();//播放完成回调
}
- mediaplay适配
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.RawRes;
import com.tuya.smart.api.MicroContext;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class MediaPlayerAdpater implements IPlayerApi {
public static int STATUS_PALYINGP = 0;//正在播放
public static int STATUS_STOP = 1;//暂停播放
public static int STATUS_RESET = 2;//重置
public static int STATUS_PLAY_COMPLETE = 3;//播放完成
public static int STATUS_PREPER_COMPLETE = 4;//媒体流装载完成
public static int STATUS_PREPER_ING = 5;//媒体流加载中
public static int STATUS_ERROR = -1;//错误
public int PLAYBACK_POSITION_REFRESH_INTERVAL_MS = 500;
private final String TAG = "MediaPlayerHolder";
private MediaPlayer mMediaPlayer;
/**
* 开启线程
*/
private ScheduledExecutorService mExecutor;
private PlaybackInfoListener mPlaybackInfoListener;
private Runnable mSeekbarPositionUpdateTask;
// private String musiUrl;//音乐地址,可以是本地的音乐,可以是网络的音乐
public void setmPlaybackInfoListener(PlaybackInfoListener mPlaybackInfoListener) {
this.mPlaybackInfoListener = mPlaybackInfoListener;
}
/**
* 初始化MediaPlayer
*/
private void initializeMediaPlayer() {
//注册,播放完成后的监听
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
stopUpdatingCallbackWithPosition(true);
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_PLAY_COMPLETE);
mPlaybackInfoListener.onPlayCompleted();
}
}
});
//监听媒体流是否装载完成
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
medisaPreparedCompled();
if (mOnPrepareCompletedListener != null) {
mOnPrepareCompletedListener.onComplete();
}
}
});
// 监听媒体错误信息
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_ERROR);
}
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;
}
});
}
private int mMusicRawId = 0;
private String mMusicUrl = null;
private OnPrepareCompletedListener mOnPrepareCompletedListener;
/**
* 加载媒体资源
*
* @param musiUrl String:音乐地址,可以是本地的音乐,可以是网络的音乐
**/
@Override
public void loadMedia(String musiUrl, OnPrepareCompletedListener listener) {
if (TextUtils.isEmpty(musiUrl)) {
Log.i(TAG, "地址为空");
return;
}
mOnPrepareCompletedListener = listener;
mMusicUrl = musiUrl;
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_PREPER_ING);
}
mMediaPlayer = new MediaPlayer();
initializeMediaPlayer();
try {
//防止再次添加进来出现崩溃信息
mMediaPlayer.reset();
mMediaPlayer.setDataSource(musiUrl);
mMediaPlayer.prepareAsync();
} catch (IOException e) {
Log.e(TAG, e.getMessage().toString());
}
}
/**
* 加载媒体资源
**/
@Override
public void loadMedia(@RawRes int musicRawId, OnPrepareCompletedListener listener) {
try {
AssetFileDescriptor afd = MicroContext.getApplication().getResources().openRawResourceFd(musicRawId);
if (afd == null) {
Log.e(TAG, "afd == null");
return;
}
mOnPrepareCompletedListener = listener;
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_PREPER_ING);
}
mMediaPlayer = new MediaPlayer();
initializeMediaPlayer();
//防止再次添加进来出现崩溃信息
mMediaPlayer.reset();
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mMediaPlayer.prepareAsync();
} catch (IOException e) {
Log.e(TAG, e.getMessage().toString());
}
}
/**
* 释放媒体资源
**/
@Override
public void release() {
if (mMediaPlayer != null) {
stopUpdatingCallbackWithPosition(false);
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
mMusicUrl = null;
mMusicRawId = 0;
}
}
/**
* 判断是否正在播放
**/
@Override
public boolean isPlaying() {
if (mMediaPlayer != null) {
return mMediaPlayer.isPlaying();
}
return false;
}
/**
* 播放开始
**/
@Override
public void play() {
if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
mMediaPlayer.start();
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_PALYINGP);
}
startUpdatingCallbackWithPosition();
}
}
/**
* 开启线程,获取当前播放的进度
**/
private void startUpdatingCallbackWithPosition() {
if (mExecutor == null) {
mExecutor = Executors.newSingleThreadScheduledExecutor();
}
if (mSeekbarPositionUpdateTask == null) {
mSeekbarPositionUpdateTask = new Runnable() {
@Override
public void run() {
updateProgressCallbackTask();
}
};
}
mExecutor.scheduleAtFixedRate(
mSeekbarPositionUpdateTask,
0,
PLAYBACK_POSITION_REFRESH_INTERVAL_MS,
TimeUnit.MILLISECONDS
);
}
@Override
public void reset() {
if (mMediaPlayer != null) {
if (mMusicUrl != null) {
loadMedia(mMusicUrl, mOnPrepareCompletedListener);
} else {
loadMedia(mMusicRawId, mOnPrepareCompletedListener);
}
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_RESET);
}
stopUpdatingCallbackWithPosition(true);
}
}
/**
* 暂停
**/
@Override
public void pause() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(STATUS_STOP);
}
stopUpdatingCallbackWithPosition(false);
}
}
/**
* 更新当前的进度
**/
private void updateProgressCallbackTask() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
try {
int currentPosition = mMediaPlayer.getCurrentPosition();
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onPositionChanged(currentPosition);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 加载完成回调:完成媒体流的装载
**/
private void medisaPreparedCompled() {
int duration = mMediaPlayer.getDuration();//获取总时长
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onTotalDuration(duration);
mPlaybackInfoListener.onPositionChanged(0);
mPlaybackInfoListener.onStateChanged(STATUS_PREPER_COMPLETE);
}
}
/**
* 滑动播放到某个位置
**/
@Override
public void seekTo(int position) {
if (mMediaPlayer != null) {
mMediaPlayer.seekTo(position);
}
}
/**
* 播放完成回调监听
**/
private void stopUpdatingCallbackWithPosition(boolean resetUIPlaybackPosition) {
if (mExecutor != null) {
mExecutor.shutdownNow();
mExecutor = null;
mSeekbarPositionUpdateTask = null;
if (resetUIPlaybackPosition && mPlaybackInfoListener != null) {
mPlaybackInfoListener.onPositionChanged(0);
}
}
}
}
参考: