橘子味的心
标题:6.3 Android音频

Android 系统支持三种不同来源的音频播放:
1)本地资源

存储在应用程序中的资源,例如存储在 RAW 文件夹下的媒体文件,只能被当前应用程序访问。

2)外部资源

存储在文件系统中的标准媒体文件,例如存储在 SD 卡中的文件,可以被所有应用程序访问。

3)网络资源

通过网络地址取得的数据流(URL),例如“http://www.musiconline.com/classic/007. mp3”,可以被所有应用程序访问。

Android N 支持的音频格式

Android N 支持的音频格式如表 1 所示。
格式/编码 支持的文件类型
AACLC/LTP 3GPP(.3gp)
MPEG-4(.mp4,.m4a)
ADTS raw AAC
MPEG-TS(.ts,not seekable,Android3.0+)
HE-AACv1(AAC+)
HE-AACv2(enhanced AAC+)
AMB-NB 3GPP(.3gp)
AMR-WB 3GPP(.3gp)
FLAC FLAC(.flac)only
MP3 MP3(.mp3)
MIDI Type 0 and 1(.mid,.xmf,.mxmf)
RTTTL/RTX(.rtttl,rtx)
OTA(.ota)
iMelody(.imy)
Vorbis Ogg(.ogg)
Matroska
PCM/WAVE WAVE(.wav)

音频播放器

实例 MediaPlayerAudioDemo 演示了分别播放三种类型的资源的方法。

该实例中 MediaPlayerAudioActivity 向 Intent 对象中传入要载入的资源类型,并通过该 Intent 启动用于播放音乐的 Activity:PlayAudio。PlayAudio 根据传入的参数分别获取对应的音乐资源并且播放。

实例 MediaPlayerAudioDemo 的运行效果如图 1 所示。
MediaPlayerAudioDemo的运行效果
图 1  MediaPlayerAudioDemo的运行效果

实例 MediaPlayerAudioDemo 中的 main.xml 代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical">
  6.  
  7.  
  8. <Button
  9. android:id="@+id/button01"
  10. android:layout_width="fill_parent"
  11. android:layout_height="wrap_content"
  12. android:text="播放存储在文件系统的音乐" />
  13.  
  14. <Button
  15. android:id="@+id/button02"
  16. android:layout_width="fill_parent"
  17. android:layout_height="wrap_content"
  18. android:text="播放网络中的音乐" />
  19.  
  20. <Button
  21. android:id="@+id/button03"
  22. android:layout_width="fill_parent"
  23. android:layout_height="wrap_content"
  24. android:text="播放本地资源的音乐" />
  25. </LinearLayout>
实例 MediaPlayerAudioDemo 中MainActivity.java 文件的代码如下:
  1. package introduction.android.batterydemo;
  2.  
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.support.v7.app.AppCompatActivity;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.CompoundButton;
  12. import android.widget.TextView;
  13. import android.widget.ToggleButton;
  14.  
  15. import org.w3c.dom.Text;
  16.  
  17. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  18. private Button button01, button02, button03;
  19. private String PLAY = "play";
  20. private int Local = 1;
  21. private int Stream = 2;
  22. private int Resources = 3;
  23.  
  24. @Override
  25. public void onCreate(Bundle saveInstanceState) {
  26. super.onCreate(saveInstanceState);
  27. setContentView(R.layout.activity_main);
  28. button01 = (Button) findViewById(R.id.button01);
  29. button02 = (Button) findViewById(R.id.button02);
  30. button03 = (Button) findViewById(R.id.button03);
  31. button01.setOnClickListener(this);
  32. button02.setOnClickListener(this);
  33. button03.setOnClickListener(this);
  34. }
  35.  
  36. @Override
  37. public void onClick(View v) {
  38. Intent intent = new Intent(MainActivity.this, PlayAudio.class);
  39. if (v == button01) {
  40. intent.putExtra(PLAY, Local);
  41. }
  42. if (v == button02) {
  43. intent.putExtra(PLAY, Stream);
  44. }
  45. if (v == button03) {
  46. intent.putExtra(PLAY, Resources);
  47. }
  48. MainActivity.this.startActivity(intent);
  49. }
  50. }
实例 MediaPlayerAudioDemo 中 PlayAudio 类实现播放音频的功能,根据 MediaPlayer-AudioActivity 类通过 Intent 传递过来的不同的值,而实现三种不同的播放音频的方式。

PlayAudio.java 文件的代码如下:
  1. package introduction.android.batterydemo;
  2.  
  3. import android.app.Activity;
  4. import android.media.MediaPlayer;
  5. import android.os.Bundle;
  6. import android.widget.TextView;
  7. import android.widget.Toast;
  8.  
  9. public class PlayAudio extends Activity {
  10. private TextView textview;
  11. private String PLAY = "paly";
  12. private MediaPlayer mediaplayer;
  13. private String path;
  14.  
  15. @Override
  16. public void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main);
  19. textview = (TextView) findViewById(R.id.textview);
  20. Bundle extras = getIntent().getExtras();
  21. playAudio(extras.getInt(PLAY));
  22. }
  23.  
  24. private void playAudio(int play) {
  25. // TODO Auto-generated method stub
  26. try {
  27. switch (play) {
  28. case 1:
  29. path = "sdcard/music/white.mp3";
  30. if (path == "") {
  31. Toast.makeText(PlayAudio.this, "在SD未找到音频文件",
  32. Toast.LENGTH_LONG);
  33. }
  34. mediaplayer = new MediaPlayer();
  35. mediaplayer.setDataSource(path);
  36. mediaplayer.prepare();
  37. mediaplayer.start();
  38. textview.setText("正在播放文件中的音乐");
  39. break;
  40. case 2:
  41. path = "http://www.musiconline.com/classic/007.mp3";
  42. if (path == "") {
  43. Toast.makeText(PlayAudio.this, "未找到您要播放的音乐",
  44. Toast.LENGTH_LONG).show();
  45. }
  46. mediaplayer = new MediaPlayer();
  47. mediaplayer.setDataSource(path);
  48. mediaplayer.prepare();
  49. mediaplayer.start();
  50. textview.setText("正在播放网络中的音乐");
  51. break;
  52. case 3:
  53. mediaplayer = MediaPlayer.create(this, null);
  54. mediaplayer.start();
  55. textview.setText("正在播放本地资源中的音乐");
  56. break;
  57. }
  58. } catch (Exception e) {
  59. System.out.println("出现异常");
  60. }
  61. }
  62.  
  63. @Override
  64. protected void onDestroy() {
  65. // TODO Auto-generated method stub
  66. super.onDestroy();
  67. if (mediaplayer != null) {
  68. mediaplayer.release();
  69. mediaplayer = null;
  70. }
  71. }
  72. }
其中,path 指向要播放的音频文件的位置。

本实例中,外部文件系统中的资源是放置在 SD 卡中的 music 目录下的 white.mp3;网络资源使用的是 http://www.musiconline.com/classic/007.mp3;本地资源使用的是 raw 目录下的 black.mp3 文件。

实例 MediaPlayerAudioDemo 中 AndroidManifest.xml 文件的代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="introduction.android.batterydemo"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6.  
  7. <uses-sdk android:minSdkVersion="10" />
  8. <application
  9. android:allowBackup="true"
  10. android:icon="@mipmap/ic_launcher"
  11. android:label="@string/app_name"
  12. android:roundIcon="@mipmap/ic_launcher_round"
  13. android:supportsRtl="true"
  14. android:theme="@style/AppTheme">
  15. <activity android:name=".MainActivity">
  16. <intent-filter>
  17. <action android:name="android.intent.action.MAIN" />
  18. <category android:name="android.intent.category.LAUNCHER" />
  19. </intent-filter>
  20. </activity>
  21. <activity android:name=".PlayAudio" />
  22. </application>
  23.  
  24. </manifest>
在该实例中,每次播放音频文件时都会从 MediaPlayerAudioActivity 跳转到一个新的 Activity,即 PlayAudio。

当返回 MediaPlayerAudioActivity 时,由于 PlayAudio 对象被释放掉,因此播放的音乐也随之停止,不再播放。若想在返回 MediaPlayerAudioActivity 时音乐不停止,则需要使用 Service 在后台播放音频文件。 

后台播放音频

实例 AudioServiceDemo 演示了如何在后台播放音频。该实例的运行效果如图 2 所示。当用户单击“启动 Service”按钮时,当前 Activity 结束,应用程序界面消失,返回 Android 应用程序列表,同时后台启动 Service,播放视频文件。

AudioServiceDemo的运行效果
图 2  AudioServiceDemo的运行效果

该实例界面简单,仅一个按钮。布局文件 main.xml 的代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical">
  6.  
  7. <Button
  8. android:id="@+id/button1"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:text="启动Service" />
  12. </LinearLayout>
实例 AudioServiceDemo 中 Activity 文件 AudioServiceDemoActivity.java 的代码如下:
  1. package introduction.android.audioservicedemo;
  2.  
  3. import android.content.Intent;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.os.Bundle;
  6. import android.view.View;
  7. import android.widget.Button;
  8.  
  9. public class MainActivity extends AppCompatActivity {
  10. private Button btn;
  11.  
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. btn = (Button) findViewById(R.id.button1);
  17. btn.setOnClickListener(new View.OnClickListener() {
  18. @Override
  19. public void onClick(View view) {
  20. startService(new Intent("introduction.android.AudioServiceDemo.MY_AUDIO_SERVICE"));
  21. finish();
  22. }
  23. });
  24. }
  25. }
AudioServiceDemoActivity 在按钮被单击后使用 startService() 方法启动了自定义的服务 MY_AUDIO_SERVICE,然后调用 finish() 方法关闭当前 Activity。该服务需要在 AndroidManifest. xml 文件中进行声明。

AndroidManifest.xml 的代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2.  
  3. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  4. package="introduction.android.AudioServiceDemo"
  5. android:versionCode="1"
  6. android:versionName="1.0">
  7.  
  8. <uses-sdk android:minSdkVersion="14" />
  9. <application
  10. android:icon="@drawable/ic_launcher"
  11. android:label="@string/app_name">
  12. <activity
  13. android:name=".AudioServiceDemoActivity"
  14. android:label="@string/app_name">
  15. <intent-filter>
  16. <action android:name="android.intent.action.MAIN" />
  17. <category android:name="android.intent.category.LAUNCHER" />
  18. </intent-filter>
  19. </activity>
  20. <service android:name="MyAudioService">
  21. <intent-filter>
  22. <action android:name="introduction.android.AudioServiceDemo.MY_AUDIO_SERVICE" />
  23. <category android:name="android.intent.category.DEFAULT" />
  24. </intent-filter>
  25. </service>
  26. </application>
  27. </manifest>
其中:
  1. <service android:name="MyAudioService">
  2. <intent-filter>
  3. <action android:name="introduction.android.AudioServiceDemo.MY_AUDIO_SERVICE" />
  4. <category android:name="android.intent.category.DEFAULT" />
  5. </intent-filter>
  6. </service>
定义了名为 MyAudioService 的 Service,该 Service 对名为“introduction.android.AudioServiceDemo. MY_AUDIO_SERVICE”的动作进行处理。

实例 AudioServiceDemo 中 MyAudioService.java 的代码如下:
  1. package introduction.android.audioservicedemo;
  2.  
  3. import android.app.Service;
  4. import android.content.Intent;
  5. import android.media.MediaPlayer;
  6. import android.os.IBinder;
  7.  
  8. import java.io.IOException;
  9.  
  10. public class MyAudioService extends Service {
  11. private MediaPlayer mediaplayer;
  12.  
  13. @Override
  14. public IBinder onBind(Intent argO) {
  15. // TODO Auto-generated method stub
  16. return null;
  17. }
  18.  
  19. @Override
  20. public void onDestroy() {
  21. // TODO Auto-generated method stub
  22. super.onDestroy();
  23. if (mediaplayer != null) {
  24. mediaplayer.release();
  25. mediaplayer = null;
  26. }
  27. }
  28.  
  29. @Override
  30. public void onStartCommand(Intent intent, int flags, int startId) {
  31. // TODO Auto-generated method stub
  32. super.onStartCommand(intent, flags, startId);
  33. String path = "sdcard/music/white.mp3";
  34. mediaplayer = new MediaPlayer();
  35. try {
  36. mediaplayer.setDataSource(path);
  37. mediaplayer.prepare();
  38. mediaplayer.start();
  39. } catch (IOException e) {
  40. // TODO Auto-generated catch block
  41. e.printStackTrace();
  42. }
  43. }
  44. }
该服务启动 Mediaplayer,并播放存放于 SD 卡中的“sdcard/music/white.mp3”文件。