在Android应用开发中,背景音乐是增强用户体验的重要元素。本文将介绍如何使用Android SDK提供的MediaPlayer类来为Android应用添加背景音乐。假设读者已经熟悉Android的活动(Activity)和服务(Service)的概念。
MediaPlayer类是AndroidSDK中用于控制音频/视频文件和流播放的类。它的生命周期以状态机图的形式实现。官方的Android开发页面对其生命周期有详细的解释。在播放音乐之前,MediaPlayer需要处于准备(prepared)状态。媒体源可以是本地资源、内部URI或者外部URL(流媒体)。本文主要关注使用本地资源。因此,需要将应用的音乐文件保存在/res/raw目录下。支持的音乐文件格式包括MP3、MIDI、WAV、MP4、MP4A、3GP、FLAC、ADTS Raw AAC(仅解码.aac)。
为了让媒体在用户与应用交互时在后台播放,必须从应用的主活动启动一个服务,并且该服务包含所有与播放相关的函数。为了允许活动与服务交互,还需要一个服务连接。简而言之,需要实现一个绑定服务。
播放控制操作可能会因为各种原因失败,例如不支持的音视频格式、音视频交错不良、分辨率过高、流媒体超时、编程错误等。因此,需要包含足够的错误处理和恢复机制。在所有这些错误条件下,如果事先通过setOnErrorListener(android.media.MediaPlayer.OnErrorListener)注册了OnErrorListener,内部播放器引擎将调用用户供应的OnErrorListener.onError()方法。即使没有注册错误监听器,MediaPlayer也会进入"错误"状态。因此,对于MediaPlayer的错误处理机制,必须对MediaPlayer的状态图有良好的理解。
以下是实现MediaPlayer的基本服务的代码。代码是用Java编写的,使用AndroidSDK在Eclipse IDE中编写。将此代码复制为一个名为MusicService.class的单独Java文件,放在Android应用的src目录下。示例音乐文件"jingle.mp3"也可以下载,并应存储在"res/raw"目录下。
这是一个允许其他应用组件绑定并与之交互的绑定服务,因此必须实现onBind()回调方法。关于绑定服务的更多细节超出了本文的范围,因为本文主要处理MediaPlayer的实现,并且假设读者对服务有良好的理解。
以下是这里实现的重要术语和函数,其余的都是自解释的:
以下是Java代码示例:
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;
public class MusicService extends Service implements MediaPlayer.OnErrorListener {
private final IBinder mBinder = new ServiceBinder();
MediaPlayer mPlayer;
private int length = 0;
public MusicService() { }
public class ServiceBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mPlayer = MediaPlayer.create(this, R.raw.jingle);
mPlayer.setOnErrorListener(this);
if (mPlayer != null) {
mPlayer.setLooping(true);
mPlayer.setVolume(100, 100);
}
mPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
onError(mPlayer, what, extra);
return true;
}
});
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mPlayer.start();
return START_STICKY;
}
public void pauseMusic() {
if (mPlayer.isPlaying()) {
mPlayer.pause();
length = mPlayer.getCurrentPosition();
}
}
public void resumeMusic() {
if (!mPlayer.isPlaying()) {
mPlayer.seekTo(length);
mPlayer.start();
}
}
public void stopMusic() {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mPlayer != null) {
try {
mPlayer.stop();
mPlayer.release();
} finally {
mPlayer = null;
}
}
}
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(this, "music player failed", Toast.LENGTH_SHORT).show();
if (mPlayer != null) {
try {
mPlayer.stop();
mPlayer.release();
} finally {
mPlayer = null;
}
}
return false;
}
}
在应用的活动类(Java文件)中,使用以下代码进行服务连接:
private boolean mIsBound = false;
private MusicService mServ;
private ServiceConnection Scon = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
mServ = ((MusicService.ServiceBinder) binder).getService();
}
public void onServiceDisconnected(ComponentName name) {
mServ = null;
}
};
void doBindService() {
bindService(new Intent(this, MusicService.class), Scon, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
unbindService(Scon);
mIsBound = false;
}
}
在上面的代码中,创建了一个服务连接Scon,它在onServiceConnected()中获取服务并将其设置为mServ变量,并在服务断开连接时将其设置为null。doBindService()和doUnbindService()分别用于绑定和解绑服务。有一个布尔标志mIsBound,当服务绑定到活动时,将其设置。
Intent music = new Intent();
music.setClass(this, MusicService.class);
startService(music);
mServ.pauseMusic();
mServ.resumeMusic();
mServ.stopMusic();
<service android:name="MusicService" android:enabled="true">