一、前期基礎知識儲備
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交互的,這樣可以方便地提供視頻播放界面。