編輯:關於Android編程
Android提供了常見的音頻、視頻的編碼、解碼機制。借助於多媒體類MediaPlayer的支持,開發人員可以很方便在在應用中播放音頻、視頻。本篇博客主要講解在Android平台下如何播放一個音頻文件。
本篇博客主要內容如下:
MediaPlayer
上面提到過,Android下對於音頻、視頻的支持均需要使用到MediaPlayer,它主要用來控制Android下播放文件或流的類。MediaPlayer處於Android多媒體包下"android.media.MediaPlayer",僅有一個無參的構造函數,雖然僅為我們提供了一個無參的構造函數,為了方便我們初始化,還為我們提供了幾個靜態的create()方法用於完成MediaPlayer初始化的工作。
MediaPlayer除了通過上面兩個create()方法在初始化的時候指定媒體資源,還可以通過MediaPlayer.setDataSource()方法為初始化後的MediaPlayer設置媒體資源,setDataSource()具有多個重載函數,適用於不同的媒體資源來源,以下講解幾個常用的,其他的可以查閱官方文檔。
MediaPlayer的音頻源
通過上面介紹的初始化MediaPlayer的播放時媒體數據源的方法可以看出,MediaPlayer支持的數據源有:本地文件、內部的Uri(內容提供者)、外部Uri。
如,設置一個本地SD卡的資源:
1 mediaPlayer = new MediaPlayer();
2 mediaPlayer.setDataSource("/sdcarc/a.mp3");
注意讀內存卡,還需要設定訪問內存卡的權限:
如,設置一個外部uri的網絡流媒體資源:
1 mediaPlayer = new MediaPlayer(); 2 mediaPlayer.setDataSource(http://192.168.1.102:1231/music/a.mp3);
如果訪問網絡流媒體資源,還需要設置訪問網絡的權限:
使用MediaPlayer播放音樂
MediaPlayer其實是一個封裝的很好的音頻、視頻流媒體操作類,如果查看其源碼,會發現其內部是調用的native方法,所以它其實是有C++實現的。
既然是一個流媒體操作類,那麼必然涉及到,播放、暫停、停止等操作,實際上MediaPlayer也為我們提供了相應的方法來直接操作流媒體。
通過上面三個方法,只要設定好流媒體數據源,即可在應用中播放流媒體資源,為了更好的操作流媒體,MediaPlayer還為我們提供了一些其他的方法,這裡列出一些常用的,詳細內容參閱官方文檔。
大部分方法的看方法名就可以理解,但是有幾個方法需要單獨說明一下。
在使用MediaPlayer播放一段流媒體的時候,需要使用prepare()或prepareAsync()方法把流媒體裝載進MediaPlayer,才可以調用start()方法播放流媒體。
setAudioStreamType()方法用於指定播放流媒體的類型,它傳遞的是一個int類型的數據,均以常量定義在AudioManager類中, 一般我們播放音頻文件,設置為AudioManager.STREAM_MUSIC即可。
除了上面介紹的一些方法外,MediaPlayer還提供了一些事件的回調函數,這裡介紹幾個常用的:
MediaPlayer使用技巧
在使用MediaPlayer的使用過程中,有個小技巧需要說明一下:
1、在使用start()播放流媒體之前,需要裝載流媒體資源。這裡最好使用prepareAsync()用異步的方式裝載流媒體資源。因為流媒體資源的裝載是會消耗系統資源的,在一些硬件不理想的設備上,如果使用prepare()同步的方式裝載資源,可能會造成UI界面的卡頓,這是非常影響用於體驗的。因為推薦使用異步裝載的方式,為了避免還沒有裝載完成就調用start()而報錯的問題,需要綁定MediaPlayer.setOnPreparedListener()事件,它將在異步裝載完成之後回調。異步裝載還有一個好處就是避免裝載超時引發ANR((Application Not Responding)錯誤。
1 mediaPlayer = new MediaPlayer();
2 mediaPlayer.setDataSource(path);
3 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
4
5 // 通過異步的方式裝載媒體資源
6 mediaPlayer.prepareAsync();
7 mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
8 @Override
9 public void onPrepared(MediaPlayer mp) {
10 // 裝載完畢回調
11 mediaPlayer.start();
12 }
13 });
2、使用完MediaPlayer需要回收資源。MediaPlayer是很消耗系統資源的,所以在使用完MediaPlayer,不要等待系統自動回收,最好是主動回收資源。
1 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
2 mediaPlayer.stop();
3 mediaPlayer.release();
4 mediaPlayer = null;
5 }
3、使用MediaPlayer最好使用一個Service來使用,並且在Service的onDestory()方法中回收MediaPlayer資源,實際上,就算是直接使用Activity承載MediaPlayer,也最好在銷毀的時候判斷一下MediaPlayer是否被回收,如果未被回收,回收其資源,因為底層調用的native方法,如果不銷毀還是會在底層繼續播放,而承載的組件已經被銷毀了,這個時候就無法獲取到這個MediaPlayer進而控制它。
1 @Override
2 protected void onDestroy() {
3 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
4 mediaPlayer.stop();
5 mediaPlayer.release();
6 mediaPlayer = null;
7 }
8 super.onDestroy();
9 }
4、對於單曲循環之類的操作,除了可以使用setLooping()方法進行設置之外,還可以為MediaPlayer注冊回調函數,MediaPlayer.setOnCompletionListener(),它會在MediaPlayer播放完畢被回調。
1 // 設置循環播放
2 // mediaPlayer.setLooping(true);
3 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
4
5 @Override
6 public void onCompletion(MediaPlayer mp) {
7 // 在播放完畢被回調
8 play();
9 }
10 });
5、因為MediaPlayer一直操作的是一個流媒體,所以無可避免的可能一段流媒體資源,前半段可以正常播放,而中間一段因為解析或者源文件錯誤等問題,造成中間一段無法播放問題,需要我們處理這個錯誤,否則會影響Ux(用戶體驗)。可以為MediaPlayer注冊回調函數setOnErrorListener()來設置出錯之後的解決辦法,一般重新播放或者播放下一個流媒體即可。
1 mediaPlayer.setOnErrorListener(new OnErrorListener() {
2
3 @Override
4 public boolean onError(MediaPlayer mp, int what, int extra) {
5 play();
6 return false;
7 }
8 });
Demo--一個簡單的MP3播放器
上面已經介紹了MediaPlayer播放一段音頻文件的所有需要用到的內容。下面通過一個簡單的Demo來演示如何使用MediaPlayer播放一個SD卡上的MP3文件。操作MediaPlayer應該放在Service中完成,這裡為了簡單,使用Activity直接操作MediaPlayer。代碼注釋裡寫的很清楚裡,這裡不再累述。
執行這個示例需要在/sdcard/目錄下存在xm.mp3的文件。
1 package cn.bgxt.mediaplayerdemo;
2
3 import java.io.File;
4 import android.media.AudioManager;
5 import android.media.MediaPlayer;
6 import android.media.MediaPlayer.OnCompletionListener;
7 import android.media.MediaPlayer.OnErrorListener;
8 import android.media.MediaPlayer.OnPreparedListener;
9 import android.os.Bundle;
10 import android.app.Activity;
11 import android.view.View;
12 import android.widget.Button;
13 import android.widget.EditText;
14 import android.widget.Toast;
15
16 public class MainActivity extends Activity {
17 private EditText et_path;
18 private Button btn_play, btn_pause, btn_replay, btn_stop;
19 private MediaPlayer mediaPlayer;
20
21 @Override
22 protected void onCreate(Bundle savedInstanceState) {
23 super.onCreate(savedInstanceState);
24 setContentView(R.layout.activity_main);
25
26 et_path = (EditText) findViewById(R.id.et_path);
27 btn_play = (Button) findViewById(R.id.btn_play);
28 btn_pause = (Button) findViewById(R.id.btn_pause);
29 btn_replay = (Button) findViewById(R.id.btn_replay);
30 btn_stop = (Button) findViewById(R.id.btn_stop);
31
32 btn_play.setOnClickListener(click);
33 btn_pause.setOnClickListener(click);
34 btn_replay.setOnClickListener(click);
35 btn_stop.setOnClickListener(click);
36 }
37
38 private View.OnClickListener click = new View.OnClickListener() {
39
40 @Override
41 public void onClick(View v) {
42
43 switch (v.getId()) {
44 case R.id.btn_play:
45 play();
46 break;
47 case R.id.btn_pause:
48 pause();
49 break;
50 case R.id.btn_replay:
51 replay();
52 break;
53 case R.id.btn_stop:
54 stop();
55 break;
56 default:
57 break;
58 }
59 }
60 };
61 /**
62 * 播放音樂
63 */
64 protected void play() {
65 String path = et_path.getText().toString().trim();
66 File file = new File(path);
67 if (file.exists() && file.length() > 0) {
68 try {
69 mediaPlayer = new MediaPlayer();
70 // 設置指定的流媒體地址
71 mediaPlayer.setDataSource(path);
72 // 設置音頻流的類型
73 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
74
75 // 通過異步的方式裝載媒體資源
76 mediaPlayer.prepareAsync();
77 mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
78 @Override
79 public void onPrepared(MediaPlayer mp) {
80 // 裝載完畢 開始播放流媒體
81 mediaPlayer.start();
82 Toast.makeText(MainActivity.this, "開始播放", 0).show();
83 // 避免重復播放,把播放按鈕設置為不可用
84 btn_play.setEnabled(false);
85 }
86 });
87 // 設置循環播放
88 // mediaPlayer.setLooping(true);
89 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
90
91 @Override
92 public void onCompletion(MediaPlayer mp) {
93 // 在播放完畢被回調
94 btn_play.setEnabled(true);
95 }
96 });
97
98 mediaPlayer.setOnErrorListener(new OnErrorListener() {
99
100 @Override
101 public boolean onError(MediaPlayer mp, int what, int extra) {
102 // 如果發生錯誤,重新播放
103 replay();
104 return false;
105 }
106 });
107 } catch (Exception e) {
108 e.printStackTrace();
109 Toast.makeText(this, "播放失敗", 0).show();
110 }
111 } else {
112 Toast.makeText(this, "文件不存在", 0).show();
113 }
114
115 }
116 /**
117 * 暫停
118 */
119 protected void pause() {
120 if (btn_pause.getText().toString().trim().equals("繼續")) {
121 btn_pause.setText("暫停");
122 mediaPlayer.start();
123 Toast.makeText(this, "繼續播放", 0).show();
124 return;
125 }
126 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
127 mediaPlayer.pause();
128 btn_pause.setText("繼續");
129 Toast.makeText(this, "暫停播放", 0).show();
130 }
131
132 }
133
134 /**
135 * 重新播放
136 */
137 protected void replay() {
138 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
139 mediaPlayer.seekTo(0);
140 Toast.makeText(this, "重新播放", 0).show();
141 btn_pause.setText("暫停");
142 return;
143 }
144 play();
145 }
146
147 /**
148 * 停止播放
149 */
150 protected void stop() {
151 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
152 mediaPlayer.stop();
153 mediaPlayer.release();
154 mediaPlayer = null;
155 btn_play.setEnabled(true);
156 Toast.makeText(this, "停止播放", 0).show();
157 }
158
159 }
160
161 @Override
162 protected void onDestroy() {
163 // 在activity結束的時候回收資源
164 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
165 mediaPlayer.stop();
166 mediaPlayer.release();
167 mediaPlayer = null;
168 }
169 super.onDestroy();
170 }
171 }
效果展示:



教你安裝配置Android Studio
Google的在Google I/O大會上推出了一款新的開發工具android studio。這是一款基於intellij IDE的開發工具,使用Gradle構建,相信做
Android6.0 運行時權限簡單理解
6.0 運行時權限處理在6.0以前 權限都是在安裝時授權的,如果用戶不授權就無法安裝;Android從6.0(API 23)開始 使用運行時權限,而不是像以前那樣安裝時授
Android4.4 SystemUI分析之PowerUI
以下分析是基於MTK Android4.4原生的SystemUI與Google 的SystemUI有微小的區別,但兩者的整體框架是差不多的。這一篇是分析SystemUI的
Android - Android調用JNI方法 及 代碼
Android調用JNI方法 及 代碼 JNI: Java Native Interface, 實現Java和C/C++的互通. 在Andro