本文对Android音频播放做简单总结。
一.基本概念
(一)Android媒体格式
Android提供了对常用音频和视频格式的支持,它所支持的音频格式有MP3,3GP,OGG,和 WAVE(.ave)等,支持的视频格式有3GP和MPEG-4等,通过Android API提供的方法,可以实现音 频与视频的播放。
播放SD卡中的一个音频文件 ,注意权限的添加:
Android.permission.READ_EXTERNAL_STORAGE
(二)播放音频文件,一般使用的是:MediaPlayer类或SoundPool类。
二.MediaPlayer的使用总结
Media Player可以使用静态方法创建也可以使用无参构造的方法来创建。
(一)静态方法创建MediaPlayer对象
使用create方法来绑定资源
1.用于从资源ID对应的资源文件中装载音频,并返回新创建的MediaPlayer对象
MediaPlayer player=MediaPlayer.create(this,R.raw.d);
这里的资源文件是放在res/raw目录下的
2.用于根据指定的URI来装载音频,并返回新创建的MediaPlayer对象
MediaPlayer player=
MediaPlayer.create(this,Uri,pares(“http://www.music.baidu.com/sou nd/mylove.mp3”));
但是,注意,这里的访问网络上的资源时,要在AndroidManifest.xml文件中授予该程序访问网络 的权限,具代码如下
(二)根据无参数的构造方法,创建MediaPlayer对象
用setDataSource()方法实现资源的绑定。
具体代码:
MediaPlayer player=new MediaPlayer();
player.setDataSource(“/sdcard/s.wav”);
player.prepare();
使用构造方法创建的MediaPlayer必须预加载音频。才能去播放文件。
(三)MediaPlayer的常用方法
1.setDataSource() //设置 资源
2.start() throws IllegalStateException //开 始播放,如果没有预编译错过会报错
3.stop() //停 止播放
4.pause() //暂 停播放
5.prepare()//同步加载
6.prepareAsync()// 异步加载,完成后调用监听
7.isPlaying(); //本 地方法,判断播放状态
8.seekTo(int msec) //本地方法,跳转到时间点
9.int getDuration(); //本 地方法,获取音乐总长度
10.release() //释 放资源
11.reset() //重 置MediaPlayer
12.setLooping(boolean looping) //设 置循环
13.public native boolean isLooping(); //判 断循环状态
14.setVolume(float leftVolume, float rightVolume)
/ /设置音量,左声道和右声道
15.setVolume(float volume) //设 置音量
同步与异步的区别 同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需 要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统 会通知进程进行处理,这样可以提高执行的效率。
(四)MediaPlayer的几个监听方式
1. 异步加载完成时回调监听
public void setOnPreparedListener(OnPreparedListener listener)
异步监听,一般在异步预加载之前就要设置好。
2.音乐播放完毕后回调监听
public void setOnCompletionListener(OnCompletionListener listener)
一般用于设置播放完毕后,播放下一首还是循环播放
3.当跳转完成时的监听
public void setOnSeekCompleteListener(OnSeekCompleteListener listener)
一般用于监听进度突然改变的值的变化
三.SoundPool的使用总结
由于MediaPlayer占用资源较高,且不支持同时播放多个音频,所有Android为我们提供了另一个播放音频的类SounePool.SoundPool也就是音频池,它可以同时播放多个短促的音频,而且占用的资源少,例如游戏中的多个音频同时播放。
Sound Pool的音频文件也是放在res/raw/文件夹下面
使用SoundPool播放音频的步骤如下:
(一)创建SoundPool对象
new SoundPool(int maxStreams,int streamType,int srcQuality)
其中,maxStreams用来指定可以容纳多少个音频,streamType用来指定声音类型,可以通过 AudioManager类提供的常量进行指定,通常使用STREAM_MUSIC; srcQuality参数用于指定音频的品质,0为默认
(二)加载所要播放的音频,四种方式
1. Public int load(Context context,int resid,int priority)
用于通过指定的资源来加载音频
2.Public int load(String path,int priority)
用于通过音频文件的路径来加载音频
3.Public int load(AssetFileDescriptor afd,int priority)
用于从AssetFileDescriptor所对应的文件中加载音频
4. Public int load(FileDescriptor fd,long offset,long length,int priority )
用于加载FileDescriptor对象中,从offset开始,长度为length的音频
(三)控制播放音频
play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
6个参数的说明:
1)soundID 用于指定要播放的音频,该音频为通过load()方法返回的音频
2)leftVolume 用于指定左声道的音量,取值为0-1.0
3)rightVolume 用于指定左声道的音量,取值为0-1.0
4) Priority 用于指定播放音频的优先级,数值越大,优先级越高
5)Loop 用于设置循环次数0为不循环-1为循环
6)Rate 用于指定速率,1为正常,最小值为0.5 最高值为2
上面就是Android音频使用的类和常用的方法,下面结合实际,做两个简单的示例。
四.使用MediaPlayer来控制文件的播放和停止
程序功能实现:
1.能播放固定的音频文件资源
2.能控制音频文件的暂停和停止,开始
3.能够使用进度拖动条来控制音频文件的播放
对于其他的一些复杂功能,暂时没有去实现。
这里的使用的是使用的静态方法和无参的构造方法来控制音频文件,实际应用中只使用一种就可以了。
本程序上面两个按钮和下面两个按钮不能混用,一次程序运行中只能使用静态或者无参的对象来控制音频文件,这里只为了对比功能才放到一起的。
(一)xml文件的设计
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.lwz.mediaplayer.MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="音频播放"
android:textSize="20sp" />
<SeekBar
android:id="@+id/main_sb"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/main_tv_pro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="00:00/00:00"
android:textSize="15sp" />
<Button
android:id="@+id/main_btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="start"
android:text="播放" />
<Button
android:id="@+id/main_btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="stop"
android:text="停止" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#f00" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 动态使用音频对象" />
<Button
android:id="@+id/main_btn_start2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="start2"
android:text="播放" />
<Button
android:id="@+id/main_btn_stop2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="stop2"
android:text="停止" />
</LinearLayout>
(二)java代码的设计
package com.lwz.mediaplayer;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
/***
* 音频文件的使用
* 静态方法:播放的是程序本身的音频文件在/src/raw/文件夹下
* 动态方法:播放的是存储器的音频文件
*/
public class MainActivity extends Activity implements SeekBar.OnSeekBarChangeListener {
//定义2个音频播放的对象
MediaPlayer mediaPlayer;
MediaPlayer mediaPlayer2;
//定义布局内的按钮控件
Button start;
Button stop;
Button start2;
Button stop2;
SeekBar seekBar;
TextView tv_time;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化布局内的控件
start = (Button) findViewById(R.id.main_btn_start);
stop = (Button) findViewById(R.id.main_btn_stop);
start2 = (Button) findViewById(R.id.main_btn_start2);
stop2 = (Button) findViewById(R.id.main_btn_stop2);
seekBar = (SeekBar) findViewById(R.id.main_sb);
tv_time = (TextView) findViewById(R.id.main_tv_pro);
//设置停止按钮点无效
stop.setEnabled(false);
stop2.setEnabled(false);
//给SeekBar设置监听事件
seekBar.setOnSeekBarChangeListener(this);
}
public void start(View v) {
if (mediaPlayer == null) {
//如果音频文件对象是空的就实例化数据
mediaPlayer = MediaPlayer.create(this, R.raw.arcs);
//设置可以点击停止
stop.setEnabled(true);
}
//如果还没有开始播放,就播放音频,显示“暂停”
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
start.setText("暂停");
//开始计算
startTime();
//设置进度条的最大值
seekBar.setMax(mediaPlayer.getDuration() / 1000);
//设置一直循环
mediaPlayer.setLooping(true);
} else {
//如果正在显示“暂停”时,单击暂停,就暂停音频,显示“播放”
mediaPlayer.pause();
start.setText("播放");
//取消定时器
timer.cancel();
}
}
public void start2(View v) {
if (mediaPlayer2 == null) {
//如果音频文件对象是空的就实例化数据
//动态使用音频对象和静态使用使用区别的,这里直接new出来
mediaPlayer2 = new MediaPlayer();
try {
//给音频对象设置资源地址
mediaPlayer2.setDataSource(Environment.getExternalStorageDirectory().getAbsolutePath() + "/laolang.mp3");
//这里要预加载
mediaPlayer2.prepare();
} catch (IOException e) {
e.printStackTrace();
}
//设置可以点击停止
stop2.setEnabled(true);
}
//如果还没有开始播放,就播放音频,显示“暂停”
if (!mediaPlayer2.isPlaying()) {
mediaPlayer2.start();
start2.setText("暂停");
//开始计算
startTime();
//设置进度条的最大值
seekBar.setMax(mediaPlayer2.getDuration() / 1000);
//设置一直循环
mediaPlayer2.setLooping(true);
} else {
//如果正在显示“暂停”时,单击暂停,就暂停音频,显示“播放”
mediaPlayer2.pause();
start2.setText("播放");
//取消定时器
timer.cancel();
}
}
/**
* 时间开始跑
* 这里设置一个Timer对象来控制时间
*/
Timer timer;
//转换时间的一个对象
SimpleDateFormat format = new SimpleDateFormat("mm:ss");
private void startTime() {
timer = new Timer();
//一秒之后每秒执行一次的任务
timer.schedule(new TimerTask() {
@Override
public void run() {
//这里要在主线程执行
runOnUiThread(new Runnable() {
@Override
public void run() {
//进度条每次加一
seekBar.setProgress(seekBar.getProgress() + 1);
//获取当前播放的时间,单位毫秒秒
int time = seekBar.getProgress() * 1000;
//把显示进度的文字显示到屏幕上
if(mediaPlayer2!=null)
tv_time.setText(format.format(new Date(time)) + "/" + format.format(new Date(mediaPlayer2.getDuration())));
if(mediaPlayer!=null)
tv_time.setText(format.format(new Date(time)) + "/" + format.format(new Date(mediaPlayer.getDuration())));
//结束后重新开始,进度条
if (seekBar.getProgress() == seekBar.getMax()) {
seekBar.setProgress(0);
}
}
});
}
}, 1000, 1000);
}
public void stop(View v) {
//这里是停止不是暂停
recyle();
}
public void stop2(View v) {
//这里是停止不是暂停
recyle2();
}
/**
* 彻底释放资源
*/
public void recyle() {
//判断非空才需做处理
if (mediaPlayer != null) {
//保证音频停止
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
//回收资源
mediaPlayer.release();
//设置对象为空
mediaPlayer = null;
//设置显示文本为播放
start.setText("播放");
//设置禁止点击"停止"
stop.setEnabled(false);
}
//取消时间计数器
timer.cancel();
//设置进度条的位置为开始的地方
seekBar.setProgress(0);
}
/**
* 彻底释放资源
*/
public void recyle2() {
//判断非空才需做处理
if (mediaPlayer2 != null) {
//保证音频停止
if (mediaPlayer2.isPlaying()) {
mediaPlayer2.stop();
}
//回收资源
mediaPlayer2.release();
//设置对象为空
mediaPlayer2 = null;
//设置显示文本为播放
start2.setText("播放");
//设置禁止点击"停止"
stop2.setEnabled(false);
}
//取消时间计数器
timer.cancel();
//设置进度条的位置为开始的地方
seekBar.setProgress(0);
}
@Override
protected void onDestroy() {
//页面关闭前先彻底释放音频的资源
recyle();
recyle2();
super.onDestroy();
}
//定义一个布尔值判断是否是用户的操作的进度条
boolean fromUser;
//当进度条发送生改变时回调
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
this.fromUser = fromUser;
}
//进度条操作开始时回调
@Override
public void onStartTrackingTouch(SeekBar seekar) {
fromUser = false;
}
//进度条操作停止时回调
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//如果是用户拖动的进度条才做对应的时间跳转
if (fromUser) {
//把音频播放时间设置到拖动的位置
int time = seekBar.getProgress() * 1000;
if (mediaPlayer != null) {
mediaPlayer.seekTo(time);
}
}
}
}
程序运行前要保证资源文件的存在;
这里音频文件的停止和退出页面一定要释放资源。
程序运行后的效果:
点击播放后效果:
播放时点击暂停,就停止音频播放,如图所示:
上面的动态使用音频对象功能也是一样的。
这里就可以实现对音频文件的基本控制了。
五.SoundPool播放音频的简单示例
本示例功能比较简单,根据五种小音频文件,对应的设置五个按钮来触发播放,程序中可以同时播放多个音频,比如上一个音频文件没有播放完,下一个音频文件也可以播放。
(一)xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="play"
android:text="play" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="play1"
android:text="play1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="play2"
android:text="play2" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="play3"
android:text="play3" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="play4"
android:text="play4" />
</LinearLayout>
(二)java代码文件
package com.lwz.soundpool;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import java.util.HashMap;
/**
* 使用SoundPool播放音频,可以同时播放多个音频
* 但是主要是播放时间比较短的音频
* 它的文件资源也是一般放在本地中
*/
public class MainActivity extends AppCompatActivity {
SoundPool soundPool;
//创建一个集合存放音频数据
HashMap<Integer, Integer> map = new HashMap<>();
/*
创建SoundPool对象
1. SoundPool(int maxStreams,int streamType,int srcQuality)
2. //其中,maxStreams用来指定可以容纳多少个音频,streamType用来指定声音类型,可以通过
AudioManager类提供的常量进行指定,通常使用STREAM_MUSIC; srcQuality参数用于指定
音频的品质,0为默认
* */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建音频对象
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
//给集合设置数据
map.put(1, soundPool.load(this, R.raw.chimes, 100));
map.put(2, soundPool.load(this, R.raw.ding, 100));
map.put(3, soundPool.load(this, R.raw.enter, 100));
map.put(4, soundPool.load(this, R.raw.notify, 100));
map.put(5, soundPool.load(this, R.raw.ringout, 100));
}
/*
* play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
2. 参数说明:
3. 参数 描述
4. soundID 用于指定要播放的音频,该音频为通过load()方法返回的音频
5. leftVolume 用于指定左声道的音量,取值为0-1.0
6. rightVolume 用于指定左声道的音量,取值为0-1.0
7. priority 用于指定播放音频的优先级,数值越大,优先级越高
8. loop 用于设置循环次数0为不循环-1为循环
9. rate 用于指定速率,1为正常,最小值为0.5 最高值为2
* */
public void play(View v) {
soundPool.play(map.get(1), 1, 1, 100, 2, 1);
}
public void play1(View v) {
soundPool.play(map.get(2), 1, 1, 100, 2, 1);
}
public void play2(View v) {
soundPool.play(map.get(3), 1, 1, 100, 2, 1);
}
public void play3(View v) {
soundPool.play(map.get(4), 1, 1, 100, 2, 1);
}
public void play4(View v) {
soundPool.play(map.get(5), 1, 1, 100, 2, 1);
}
@Override
protected void onDestroy() {
super.onDestroy();
soundPool.release();
}
}
程序运行后的结果:
这里没有办法展示声音效果!
对比这两个音频控制文件:
MediaPlayer是可以播放长时间的音频文件,Sound Pool是用来播放短时间的音频文件的;
Media Player和SoundPool控制的音频文件都可以放在资源文件夹res/raw/下面,也可以使用存储器中的音频资源文件,但是SoundPool控制的文件一般比较下,放在/raw目录中即可。