一、前期基础知识储备
MediaPlayer是一个支持音频及视频文件播放的Android类,可播放不同来源(本地 网络)、多种格式(如WAV/MP3/MPEG-4/3GPP/Ogg Vorbis)的多媒体文件。
多媒体文件可以是存储在应用程序的 res/raw 文件夹下,也可以是存储在手机的文件系统中,甚至可以是来自与网络的流媒体。raw文件夹负责存放那些不需要Android编译系统特别处理的各类文件,res下默认是没有raw文件夹的,创建方法如下:
实例化一个MediaPlayer对象一共有三种方法:
1)create(Context context, Uri uri, SurfaceHolder holder)
Convenience method to create a MediaPlayer for a given Uri.
2)create(Context context, int resid)
Convenience method to create a MediaPlayer for a given resource id.
3)create(Context context, Uri uri)
Convenience method to create a MediaPlayer for a given Uri.
注意: 你只能通过标准输出设备来播放音频文件。目前来讲,就是通过手机的扬声器和蓝牙耳机。 不能在通话时播放音频文件。
使用MediaPlayer时申请的权限:
1)<uses-permission android:name="android.permission.INTERNET" />
Internet Permission - 如果你打算使用 MediaPlayer 来播放网络流媒体内容,那么你的应用需要有这个权限;
2)<uses-permission android:name="android.permission.WAKE_LOCK" />
Wake Lock Permission - 如果你想要应用不灭屏或者进程不进入休眠状态,或者你像要在你的应用程序中使用 MediaPlayer.setScreenOnWhilePlaying() 方法或 MediaPlayer.setWakeMode() 方法, 你需要添加这个权限
二、上代码,具体实现
以下是代码的整体设计图:
从图中可以看到代码中包含一个Fragment即一个用于托管Fragemnt的Activity。MyAudioMediaPlayer是我们编写的类,用于封装MediaPlayer类。也可以选择不封装MediaPlayer类,而让Fragment直接与MediaPLayer进行交互。不过,为了保持代码的整洁与独立,这里使用了封装MediaPlayer类的设计。
(1)Fragment代码及Fragemnt的布局文件;
public class HelloMoonFragment extends Fragment {
private MyAudioPlayer mPlayer;
private Button mPlayButton;
private Button mStopButton;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_hello_moon, container, false);
mPlayButton = (Button) view.findViewById(R.id.hellomoon_playBtn);
mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayer.play(getActivity());
}
});
mStopButton = (Button) view.findViewById(R.id.hellomoon_stopBtn);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayer.stop();
}
});
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
mPlayer.stop();
}
}
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:src="@drawable/bv"
android:contentDescription="图片也可以有文字属性?"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:layout_weight="1"/>
<TableRow
android:gravity="center|bottom"
android:layout_weight="0">
<Button
android:id="@+id/hellomoon_playBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="play"/>
<Button
android:id="@+id/hellomoon_stopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop"/>
</TableRow>
</TableLayout>
使用MediaPlayer之后,Fragment中要覆写onDestroy()方法,这是因为MediaPlayer运行在一个不同的线程中,这里在Fragment的销毁方法中调用MediaPlayer的销毁方法,防止MediaPlayer一直占用着音频解码硬件及其他系统资源,而这些系统资源是由所有应用所共享的。
(2)Activity代码及Activity的布局文件;
public class PaintPositionInTest extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PaintPositionIn paintPositionIn = new PaintPositionIn(this);
setContentView(R.layout.activity_hello_moon);
}
}
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/helloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.administrator.animation_practice.fragment.HelloMoonFragment">
</fragment>
这里用Fragemnt的静态加载方法,将其直接添加进Activity的布局文件,使用静态加载Fragment的方式失去了灵活性和掌控能力,但是对于Activity的静态部分而言,静态加载也是一个不错的选择。
(3)封装MediaPlayer类的MyAudioPlayer类;
public class MyAudioPlayer {
private MediaPlayer mPlayer;
public void stop(){
if (mPlayer != null){
mPlayer.release();
mPlayer = null;
}
}
public void play(Context c){
stop();
mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
stop();
}
});
try {
mPlayer.prepare(); //预加载音频 使用时放置在try catch中
} catch (IOException e) {
e.printStackTrace();
}
mPlayer.start(); //正式加载音频
}
}
在play()方法中,调用对应的create(Context context, int resid)创建mPlayer实例;并且在方法的开头调用stop()方法,可避免用户多次点击Play按钮创建多个MediaPlayer实例的情况的发生。同时设立监听器,音频文件播放完之后,立即调用stop()方法,尽可能快递释放MediaPlayer实例及其占用的资源。
在stop()方法中,释放MediaPlayer实例并将mPlayer变量设置为null,调用Media.release()方法,销毁该实例。
封装MediaPlayer之后,之后管理播放器的状态会更加的方便,实际开发中,相机、音频播放、视频播放等多媒体行为,生命周期、状态非常多,需要多重控制,封装代码之后可以更加方便的管理。
三、使用MediaPlayer播放视频
在Android系统中,快速刷新显示的可视图像(比如视频、相机预览)是在SurfaceView中显示的,准确地说,是在SurfaceView内嵌的Surface中显示的。通过SurfaceView的SurfaceHolder,可实现Surface上显示视频。使用MediaPlayer播放视频的关键代码在于将MediaPlayer类与SurfaceHolder关联起来,方法为:
MediaPlayer.setDisplay(SurfaceHolder);
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
...
private SurfaceView mSurfaceView;
private String mMediaPath;
private MediaPlayer mMediaPlayer;
private SurfaceHolder mSurfaceHolder;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//6.0及以上系统请求运行时权限 利用权限申请工具类
requestCameraAndStoragePermission();
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //必须-设置Surface不需要维护自己的缓冲区
initBtnClick();
SurfaceHolder holder = mSurfaceView.getHolder();
holder.addCallback(this);
}
private void requestCameraAndStoragePermission() {
...
...
};
......
......
private void initBtnClick() {
mPlayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.reset();
Uri uri = Uri.parse(mMediaPath);
mMediaPlayer = MediaPlayer.create(MainActivity.this,uri);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDisplay(mSurfaceHolder);
try{
mMediaPlayer.prepare();
}catch (Exception e){
e.printStackTrace();
}
mMediaPlayer.start();
}
}
});
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
mSurfaceHolder = surfaceHolder;
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mSurfaceView = null;
mSurfaceHolder = null;
releaseMediaRecorder();
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
if (mMediaPlayer != null){
mMediaPlayer.release();
mMediaPlayer = null;
}
}
}
通常来说,使用VideoView实例播放视频更加容易些,不同于SurfaceView同MediaPlayer的交互,VideoView是与MediaController交互的,这样可以方便地提供视频播放界面。