1. 程式人生 > >Android 多媒體MediaPlayer使用詳解

Android 多媒體MediaPlayer使用詳解

 現在的手機功能越來越豐富了,遙想10年前,MP3,MP4,MP5,還是很流行的,博主當時讀高中時很想擁有一臺,可以聽音樂和看電影。可是條件有限,學校也禁止此東西,所以只能偷偷的玩。而現在我們的手機也很早以前就支援了這些功能,而且介面和功能也遠遠超過了MP4。好吧,說多了,今天本文介紹的是Andriod系統自帶的Mediaplayer,和VideoView實現簡單的音樂和視訊的播放,至於想做出如酷狗音樂這樣的APP的話,只要想做,應該也不難,都是基於此實現了功能的擴充套件。

 Android的MediaPlayer包含了Audio和Video的播放功能,在Android的介面上,Music和Video兩個應用程式都是呼叫MediaPlaer來實現的。

  一、播放音訊檔案

         首先看看MediaPlaer的生命週期


下面是MediaPlayer提供的常用方法

方法 說明
MediaPlayer 構造方法
create 建立一個要播放的多媒體
getCurrentPosition 得到當前播放位置
getDuration 得到檔案的時間
getVideoHeight 得到視訊的高度
getVideoWidth 得到視訊的寬度
isLooping 是否迴圈播放
isPlaying 是否正在播放
pause 暫停
prepare 準備(同步)
prepareAsync 準備(非同步)
release 釋放MediaPlayer物件相關的資源
reset 重置MediaPlayer物件為剛剛建立的狀態
seekTo 指定播放的位置(以毫秒為單位的時間)
setAudioStreamType 設定流媒體的型別
setDataSource 設定多媒體資料來源(位置)
setDisplay 設定用SurfaceHolder來顯示多媒體
setLooping 設定是否迴圈播放
setOnButteringUpdateListener 網路流媒體的緩衝監聽
setOnErrorListener 設定錯誤資訊監聽
setOnVideoSizeChangedListener 視訊尺寸監聽
setScreenOnWhilePlaying 設定是否使用SurfaceHolder來保持螢幕顯示
setVolume 設定音量
start 開始播放
stop 停止播放

MediaPlayer的工作流程是這樣的:

1,首先建立MediaPlaer物件; *

2,然後呼叫setDataSource()方法來設定音訊檔案的路徑;**

3,再呼叫prepare()方法使MediaPlayer進入到準備狀態;

4,呼叫start方法就可以播放音訊。

    *  建立MediaPlaer物件有兩種方式  

     a 直接new出來

MediaPlayer mp = new MediaPlayer();
    b 使用create方式
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//這時就不用呼叫setDataSource了
當然上面首先得在res資料夾下新建raw資料夾,並放置一個test檔案

    ** 設定播放的檔案

      MediaPlayer要播放的檔案主要包括3個來源:
a. 使用者在應用中事先自帶的resource資源
例如:MediaPlayer.create(this, R.raw.test);
b. 儲存在SD卡或其他檔案路徑下的媒體檔案
例如:mp.setDataSource("/sdcard/test.mp3");
c. 網路上的媒體檔案
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
MediaPlayer的setDataSource一共四個方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
 
其中使用FileDescriptor時,需要將檔案放到與res資料夾平級的assets資料夾裡,然後使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
來設定datasource
先簡單看下效果圖吧


   上面的功能一看就知道了,就不用我說了吧

下面是實現程式碼

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="3dp"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="播放" />

        <Button
            android:id="@+id/pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="暫停" />

        <Button
            android:id="@+id/stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止" />
    </LinearLayout>

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="0"
        android:progress="0"
        android:secondaryProgress="0" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:text="當前時間" />

        <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="總時間" />
    </RelativeLayout>

</LinearLayout>
MainActivity.java
package com.example.musicplayer;

import java.io.File;
import java.io.IOException;

import android.R.integer;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener,
		OnSeekBarChangeListener {
	private Button play, pause, stop;
	private MediaPlayer player;
	private SeekBar mSeekBar;
	private TextView tv, tv2;
	private boolean hadDestroy = false;
	private Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {

			switch (msg.what) {
			case 0x01:

				break;

			default:
				break;
			}
		};
	};
	Runnable runnable = new Runnable() {

		@Override
		public void run() {

			if (!hadDestroy) {
				mHandler.postDelayed(this, 1000);
				int currentTime = Math
						.round(player.getCurrentPosition() / 1000);
				String currentStr = String.format("%s%02d:%02d", "當前時間 ",
						currentTime / 60, currentTime % 60);
				tv.setText(currentStr);
				mSeekBar.setProgress(player.getCurrentPosition());
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		play = (Button) findViewById(R.id.play);
		pause = (Button) findViewById(R.id.pause);
		stop = (Button) findViewById(R.id.stop);
		mSeekBar = (SeekBar) findViewById(R.id.seekbar);
		tv = (TextView) findViewById(R.id.tv);
		tv2 = (TextView) findViewById(R.id.tv2);
		mSeekBar.setOnSeekBarChangeListener(this);
		play.setOnClickListener(this);
		pause.setOnClickListener(this);
		stop.setOnClickListener(this);
		player = new MediaPlayer();
		initMediaplayer();

	}

	/**
	 * 初始化播放器
	 */
	private void initMediaplayer() {
		try {
			File file = new File(Environment.getExternalStorageDirectory()
					+ "/Download/", "aiqiu.mp3");
			player.setDataSource(file.getPath());
			Log.e("播放器", file.toString());
			player.prepare();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.play:
			if (!player.isPlaying()) {
				player.start();
				int totalTime = Math.round(player.getDuration() / 1000);
				String str = String.format("%02d:%02d", totalTime / 60,
						totalTime % 60);
				tv2.setText(str);
				mSeekBar.setMax(player.getDuration());
				mHandler.postDelayed(runnable, 1000);
			}

			break;
		case R.id.pause:
			if (player.isPlaying()) {
				player.pause();
			}
			break;
		case R.id.stop:
			if (player.isPlaying()) {
				player.reset();
				initMediaplayer();
			}
			break;

		default:
			break;
		}
	}

	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		if (player != null) {
			player.seekTo(seekBar.getProgress());
		}

	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		// TODO 自動生成的方法存根

	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		// TODO 自動生成的方法存根
	}

	@Override
	protected void onDestroy() {
		// TODO 自動生成的方法存根
		super.onDestroy();
		if (player != null) {
			player.stop();
			hadDestroy = true;
			player.release();
		}
	}

}
二、播放視訊檔案
     播放視訊檔案相比播放音訊檔案並不比它複雜,這裡使用VideoView類來實現。這個類將視訊的顯示和控制集於一身。

     VideoView和MediaPlaer也比較類似,主要有以下常用方法

方法名

功能描述

setVideoPath()

設定要播放的視訊檔案的位置。

start()

開始或繼續播放視訊。

pause()

暫停播放視訊。

resume()

將視訊重頭開始播放。

seekTo()

從指定的位置開始播放視訊。

isPlaying()

判斷當前是否正在播放視訊。

getDuration()

獲取載入的視訊檔案的時長。

因為VideoView是包裝過的MediaPlayer,所以使用起來很相似。

比如:

private void initVideoPlayer() {
		File file = new File(Environment.getExternalStorageDirectory()
				+ "/Download/", "Sample.3gp");
		videoPlayer.setVideoPath(file.getPath());// 指定視訊檔案的路徑

	}
事件處理
@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.play:
			if (!videoPlayer.isPlaying()) {
				videoPlayer.start();
			}
			break;
		case R.id.pause:
			if (videoPlayer.isPlaying()) {
				videoPlayer.pause();
			}
			break;
		case R.id.stop:
			if (videoPlayer.isPlaying()) {
				videoPlayer.resume();
			}
			break;

		default:
			break;
		}
	}

三、常見的MediaPlayer錯誤

        也就是它的錯誤狀態。比如這樣的錯誤 start called in state 0,0表示他的錯誤狀態,下面是MediaPlayer的狀態,原始碼中找到的:

 enum media_player_states {
        MEDIA_PLAYER_STATE_ERROR        = 0,           // 0狀態
        MEDIA_PLAYER_IDLE                = 1 << 0,     // 1狀態
        MEDIA_PLAYER_INITIALIZED        = 1 << 1,      // 2 狀態
        MEDIA_PLAYER_PREPARING            = 1 << 2,    // 4 狀態
        MEDIA_PLAYER_PREPARED            = 1 << 3,     // 8狀態
        MEDIA_PLAYER_STARTED            = 1 << 4,      // 16狀態
        MEDIA_PLAYER_PAUSED                = 1 << 5,   // 32狀態
        MEDIA_PLAYER_STOPPED            = 1 << 6,      // 64 狀態
        MEDIA_PLAYER_PLAYBACK_COMPLETE  = 1 << 7,      // 128 狀態
    }
    可以參照報錯的狀態和MediaPlayer的生命週期(上圖)進行錯誤分析。
好了,先寫到這,對以上內容有疑惑或紕漏的地方,歡迎留言指出!