Kevin Su bio photo

Kevin Su

patience, persevere, and enjoy

Email Github Stackoverflow

###MediaPlayer 关于音频、视频播放,是Andorid应用中常见的功能。音频只需要设置源即可播放,视频还需要View来播放。
MediaPlayer类是用来播放音频/视频文件和流的。
状态图
Alt text
为了能够正常的播放,必须搞清楚各种状态。不然就会抛各种的异常。 图中,
* 椭圆表示MediaPlayer对象的状态。
* 线条代表状态的转换操作。带单箭头的表示同步方法调用的;两个箭头的表示异步方法调用。
通过图,我们看到一个MediaPlayer对象有以下几种状态:
1. Idle 初始状态
2. Initialized 已经初始化状态
3. Preparing 准备中状态
3. Prepared 已经准备好状态
4. Started 已经开始播放状态
5. Paused 已经暂停状态——就像我们mp3的暂停,可以通过开始,接着播放
6. Stopped 已经停止状态——就像我们mp3的停止,可以通过开始,重头播放
7. PlaybackCompleted 已经播放完成状态
8. End 结束状态
9. Error 出错状态

在MediaPlayer刚刚通过new或者reset()后,它处于Idle状态;而在release()后,它处于End状态。在这两个状态之间,就是MediaPlayer的生命周期。 * 上面我们说到Idle的状态可以通过新构建new或者重置reset()得到,但是,两者还是有细微的差别。 处于Idle状态下,调用以下方法都会出错:
* getCurrentPosition()
* getDuration()
* getVideoHeight()
* getVideoWidth()
* setAudioStreamType(int)
* setLooping(boolean)
* setVolume(float, float)
* pause()
* start()
* stop()
* seekTo(int)
* prepare()
* prepareAsync()
如果这些方法在MediaPlayer刚刚被构建的时候调用,系统不会去调用OnErrorListener.OnError()回调方法,同时,Idle状态不会被改变。 而如果是在reset()后调用,就会回调到OnErrorListener.OnError()方法,而且状态会改为Error.

  • MediaPlayer有一个重要的原则,当不需要使用MediaPlayer时候,应当马上调用release()方法释放资源。这个时候状态会变成End,而且再也不能到达其他的状态。

  • 通过create()方法得到MediaPlayer不是处于Idle状态,而是Prepared状态。

通常,很多播放的操作都会因为各种原因而失败。例如播放不支持的格式源、分辨率过高、获取源超时等等。通过注册错误监听器setOnErrorLinstener()可以很好的反馈告诉我们为什么播放失败,同时做异常处理。通常是在某个状态下调用了错误的方法导致的。当MediaPlayer处于Error状态的情况下,可以通过reset方法重置成Idle状态。


要播放一个资源,必须处于Prepared状态下,否者会抛异常。
* 有两个方法可以转变成这个状态。(同步)prepare()方法;(异步)prepareAsync()方法。成功后,都会调用OnPrepared()方法(OnPreparedListener接口)。
* Preparing状态是一个临时的状态,在MediaPlayer中并没有定义。


播放中的情况下,可以调用isPlaying()方法来判断MediaPlayer对象时候处于started状态。 onBufferingUpdate()方法可以跟踪播放的轨迹。 处于stared状态下调用start()方法是无意义的。


pause()方法可以stated状态转变成Paused状态,但是这个方法是异步执行的,所以状态不会马上变化。 stop()方法可以从Started, Paused, Prepared or PlaybackCompleted 转变成Stoped状态,一旦处于Stoped状态,就必须通过prepare()或者prepareAsync()方法把状态设置成Prepared状态才能播放。


想要调整播放的位置,可以通过seekTo(int)方法。这是一个异步方法。所以你调整完,需要一段时间才能完成。可以注册OnSeekComplete接口。等待回调。 Prepared, Paused and PlaybackCompleted这些状态下都可以调用。 ###嵌套的类 Alt text

####工程中的一个语音播放工具类 ``` /** * @ClassName AudioPlayUtil.java * @author KevinSu kevinsu917@126.com * @version 创建时间:2014-8-10 下午5:19:50 * @Description: 语音播放工具类,如果文件不存在会去下载 */

public class AudioPlayUtil implements OnCompletionListener, OnPreparedListener, DownloadFileCallback, OnErrorListener { //记得release private static MediaPlayer mediaPlayer; private static AudioPlayUtil instance;

//存放当前播放的Url
private String currentPlayUrl;
//文件下载后存放的位置,文件名为Url的md5
private final String downloadPath = PathUtil.getAudioDir( );

//音频播放的回调
private AudioPlayStateCallback audioCallback;
//文件下载的回调
private DownloadFileCallback downloadCallback;

private AudioPlayUtil( )
{
	// TODO Auto-generated constructor stub
}

public static AudioPlayUtil getInstance()
{
	if(instance == null)
	{
		instance = new AudioPlayUtil( );
	}
	return instance;
}


public void playAudio(Context mContext, String url)
{
	playAudio(mContext, url , null );
}

public void playAudio(Context mContext, String url, AudioPlayStateCallback playCallback)
{
	playAudio(mContext, url , playCallback, null );
}

/**
 * 播放音频的接口
 * @param mContext
 * @param url 文件的url
 * @param callback 播放的回调
 * @param downloadCallback 下载的回调
 */
public void playAudio(Context mContext, String url, AudioPlayStateCallback callback, DownloadFileCallback downloadCallback)
{
	if(mediaPlayer == null)
	{
		mediaPlayer = new MediaPlayer( );
		mediaPlayer.setOnCompletionListener( this );
		mediaPlayer.setOnPreparedListener( this );
	}
	

	if(mediaPlayer.isPlaying( ))
	{
		mediaPlayer.stop( );
		mediaPlayer.reset( );
		if(downloadCallback != null)
		{
			downloadCallback.onDownloadFileFinish( 1 , "" , "" );
		}
		if(this.audioCallback != null)
		{
			this.audioCallback.onPlayCompleted( );
		}
		
		
		if(url == currentPlayUrl)
		{

		}else
		{
			currentPlayUrl = url;
			String fileUrl = url;
			this.audioCallback = callback;
			this.downloadCallback = downloadCallback;
			
			if(url.contains( PathUtil.getHTTPPrefix( ) ))
			{
				fileUrl = downloadPath + CryptoUtil.md5( url ) + PathUtil.getMP3Postfix( );
			}
			
			File audioFile = new File( fileUrl );
			if(audioFile.exists( ))
			{
				initMediaPlayer(audioFile.getAbsolutePath( ));
			}else
			{
				//下载
				new DownLoadAuidoThread( mContext, url , this ).start( );
			}
		}
		
	}else
	{
		mediaPlayer.reset( );
		if(downloadCallback != null)
		{
			downloadCallback.onDownloadFileFinish( 1 , "" , "" );
		}
		
		String fileUrl = url;
		currentPlayUrl = url;
		this.audioCallback = callback;
		this.downloadCallback = downloadCallback;
		
		if(url.contains( PathUtil.getHTTPPrefix( ) ))
		{
			fileUrl = downloadPath + CryptoUtil.md5( url ) + PathUtil.getMP3Postfix( );
		}
		
		File audioFile = new File( fileUrl );
		if(audioFile.exists( ))
		{
			initMediaPlayer(fileUrl);
		}else
		{
			//下载
			new DownLoadAuidoThread( mContext, url , this ).start( );
		}
	}

}


private void initMediaPlayer(String url)
{
	try
	{
		mediaPlayer.setDataSource( url );
		mediaPlayer.prepareAsync( );
	}catch (Exception e) {
		// TODO: handle exception
		if(audioCallback != null)
		{
			audioCallback.OnPlayError( );
			resetConfig();
		}
	}
	
}

@Override
public void onPrepared( MediaPlayer mp )
{
	// TODO Auto-generated method stub
	mp.start( ); 
	if(audioCallback != null)
	{
		audioCallback.OnPlayStarted( );
	}
}

@Override
public void onCompletion( MediaPlayer mp )
{
	// TODO Auto-generated method stub
	if(audioCallback != null)
	{
		audioCallback.onPlayCompleted( );
	}
}

private void resetConfig()
{
	
	audioCallback = null;
	downloadCallback = null;
	if(mediaPlayer!=null)
	{
		mediaPlayer.reset( );
	}
}

public void releaseRes()
{
	if(mediaPlayer!=null)
	{
		mediaPlayer.release( );
		mediaPlayer = null;
	}
}

@Override
public void onDownloadFileProgress( long lenghtOfFile , long LengthOfDownloaded , int flag )
{
	// TODO Auto-generated method stub
	if(downloadCallback != null && flag == currentPlayUrl.hashCode( ))
	{
		downloadCallback.onDownloadFileProgress( lenghtOfFile , LengthOfDownloaded , flag );
	}
}

@Override
public void onDownloadFileFinish( int flag , String fileName , String savePath )
{
	// TODO Auto-generated method stub
	
	String completedFileName = "";
	if(fileName.contains( PathUtil.getTMPPostfix( ) ))
	{
		int endIndex = fileName.lastIndexOf( PathUtil.getTMPPostfix( ) );
		completedFileName = ( String ) fileName.subSequence( 0 , endIndex ) + PathUtil.getMP3Postfix( );
		File tmpFile = new File( savePath + fileName );
		final File completedFile = new File( savePath + completedFileName );
		try
		{
			completedFile.createNewFile( );
		}
		catch ( IOException e )
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(tmpFile.exists( ))
		{
			boolean isSuccess = tmpFile.renameTo( completedFile );
		}
		
		if(downloadCallback != null && flag == currentPlayUrl.hashCode( ))
		{
			if(completedFile.exists( ))
			{
				initMediaPlayer( completedFile.getAbsolutePath( ) );
			}
			
			downloadCallback.onDownloadFileFinish( flag , completedFileName , savePath );
		}
	}
}

@Override
public void onDownloadFileError( int flag , String fileName , String savePath )
{
	// TODO Auto-generated method stub
	if(downloadCallback != null)
	{
		downloadCallback.onDownloadFileError( flag , fileName , savePath );
	}
	
}

//音频播放的回调
public interface AudioPlayStateCallback
{
	/**开始播放 */
	public void OnPlayStarted();
	
	/**正在播放 */
	public void OnPlayingProgress();
	
	/**正在播放完毕*/
	public void onPlayCompleted();
	
	/**正在播放失败 */
	public void OnPlayError();
}

//Audio下载线程
class DownLoadAuidoThread extends Thread
{
	
	private Context context;
	private DownloadFileCallback callback;
	private String fileUrl;
	
	public DownLoadAuidoThread( Context context , String url , DownloadFileCallback callback )
	{
		// TODO Auto-generated constructor stub
		this.context = context;
		this.callback = callback;
		fileUrl = url;
	}
	
	@Override
	public void run( )
	{
		// TODO Auto-generated method stub
		String fileName = CryptoUtil.md5( fileUrl );
		//临时文件名
		String fileNameTemp = fileName + PathUtil.getTMPPostfix( );
		try
		{ 
			FileDownloadManager manager = new FileDownloadManager( context , callback ,
					fileUrl , fileNameTemp , downloadPath , fileUrl.hashCode( ) );
			manager.run( );
		}
		catch ( ConnectionException e )
		{
			// TODO Auto-generated catch block
			e.printStackTrace( );
		}
		super.run( );
	}
}

@Override
public boolean onError( MediaPlayer mp , int what , int extra )
{
	// TODO Auto-generated method stub
	return false;
} } ```