MediaPlayer
这个类主要是播放视频类.
AudioManager
这个类管理在一个设备上的音频资源和音频输出流.
Manifest声明
1.网络声明
<uses-permission android:name="android.permission.INTERNET" />
2.如果播放器应用需要将屏幕变暗或者停止处理器,或者需要调用 MediaPlayer.setScreenOnWhilePlaying() 或者 MediaPlayer.setWakeMode() 方法,需要声明:
<uses-permission android:name="android.permission.WAKE_LOCK" />
MediaPlayer类的使用
MediaPlayer类支持几种不同媒体来源例如:
1. 本地资源
2. 网络URI
3. 外部URL(流)
媒体来源1: 本地资源(存储在应该的res/raw/ 目录下)
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
这种情况下,一个"raw"资源是不需要系统做任何解析的一个文件.然而,这个资源不应该是原始音频文件.它应该是一个被支持的正确编码和格式化的媒体文件.
媒体来源2: 网络URI
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
媒体来源3: 通过HTTP取得的远程URL
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();
注意:在调用setDataSource(), 方法的时候一定要catch 获得 抛出 IllegalArgumentException 和 IOException 两个异常,因为你定义的文件可能不存在.
Asynchronous Preparation 异步准备
原则上可以直接使用MediaPlayer.但是,有一些事情必须要一体化. 例如,调用prepare() 方法可能花费很长时间来执行,因为它可能涉及到抓取或者编码媒体数据.因此,这中情况下任何方法都有可能花费很长时间去执行,你不应该在UI线程中调用这个方法.
框架支持的一个很方便完成这个操作的方法是:调用 prepareAsync() 方法.这个方法在后台开始prepare媒体并且马上就返回.当media准备完成.通过设置 setOnPreparedListener() 的 MediaPlayer.OnPreparedListener 接口中 onPrepared() 方法就会被调用.
Managing State(状态管理)
MediaPlayer 有一个内部状态当你写你的代码时你必须时刻注意,因为某些操作只有在特定的状态下才有效.
具体状态请查看SDK文档中 MediaPlayer 类.
Releasing the MediaPlayer(释放MediaPlayer)
一个MediaPlayer 可能会消耗大量的系统资源.因此,你应该总是采取额外的方式去确保你不会持有一个MediaPlayer 实例,超过实际需要.
当你完成操作时时,你应该总是调用 release() 方法去确保任何分配的系统资源给正确的释放掉.例如,当你使用一个MediaPlayer 而你的activity调用onStop()方法时,你一定要释放MediaPlayer .因为当你的activity没有和用户交互的时候,去持有一个MediaPlayer意义不大.(除非需要在后台播放媒体文件).
当你的activity是resumed 或者 restarted 状态,你需要去创建一个新的MediaPlayer 并且准备再次回复之前的播放.
这里是释放和让MediaPlayer 为null的代码:
mediaPlayer.release();
mediaPlayer = null;
Using a Service with MediaPlayer
Running asynchronously(异步运行)
首先,像一个activity一样,在一个Service中的所有工作默认的是在一个单独的线程中,如果你在同一个应用中运行一个activity和一个Service,默认的他们使用的是同一个线程(主线程).因此,service需要快速处理引入的intent并且在处理这些intent的时候不要处理冗长的运算.
如果需要这么做,你一定要异步处理这些事情.
举例来说,当在你的主线程中使用一个 MediaPlayer 时.你应该调用 prepareAsync() 而不是 prepare() ,例如:
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mMediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mMediaPlayer = ... // initialize it here
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
Handling asynchronous errors(异常异步处理)
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mMediaPlayer;
public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mMediaPlayer.setOnErrorListener(this);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
这个是很重要的,当一个错误出现, MediaPlayer 移动到到Error状态并且在你再次使用它之前你一定要重置 MediaPlayer.
Using wake locks
当设计应用在后台播放媒体文件时,设备在你的service运行的时候可能会待机. 因为android系统在设备待机的时候尝试节省电池.所有系统会尝试切断任何非必要的手机特征,包括CPU和Wifi硬件.然而,如果你的service正在播放或者流处理音乐,你会想防止系统干扰你的播放.
为了确保你的service继续运行在这些条件下,你需要使用"wake locks".一个wake lock 是一种提醒体统你的应用正在使用一些特性,系统应该保持可用,就算电话处于空闲状态.
注意:你应该总是节俭的使用wake locks,并且持有他们只有在真正必要的时候,因为他们会大大降低设备电池寿命.
要确保CPU在你的 MediaPlayer 播放的时候继续处于运行状态,当初始化你的 MediaPlayer 时调用 setWakeMode() . 一旦你这么做了, MediaPlayer 会持有指定的lock在播放的时候. 并且在paused或者stoped状态时,会释放掉这个lock.
mMediaPlayer = new MediaPlayer();
// ... other initialization here ...
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
在这个例子中获得wake lock 的前提是只有当CPU处于清醒状态.如果你是通过网络流媒体并且使用的是wifi的话,你可以也想要去持有一个 WifiLock ,这种方式你必须收到获取和释放.因此,当你开始通过远程URL准备 MediaPlayer 时,你应该创建和获得 Wi-Fi lock .例如:
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
当你暂停或者停止媒体,或者你很长时间没有用到网络,你应该释放这个lock:
wifiLock.release();
Handling audio focus(处理音频的焦点)
尽管只有一个activity可以运行在任何时候,但是android 是一个多任务环境.这给使用音频的应用产生了一个特别的挑战,因为哪里只有一个音频输入但是可能有几个媒体service竞争使用.在android2.2之前,没有一个内置机制来解决这个问题.这可能在某些情况下导致糟糕的用户体验.
当你的应用需要输入音频时,你应该总是请求音频聚焦(audio focus).一旦它有了,它可以使用自由的输出声音了,但是它应该总是保持对焦点的监听.如果它被通知它已经失去了音频焦点,它应该马上杀掉这个音频或者降低它到一个安静的水平并且只有当它再次接受到focus时再重新播放.
要请求音频聚焦,你一定要调用 AudioManager 的 requestAudioFocus() 方法,
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// could not get audio focus.
}
requestAudioFocus() 方法的第一个参数是一个 AudioManager.OnAudioFocusChangeListener ,当一个音频焦点发生改变的时候他的 onAudioFocusChange() 方法会被调用.
因此你可以在activity或者service中实现这个接口:
class MyService extends Service
implements AudioManager.OnAudioFocusChangeListener {
// ....
public void onAudioFocusChange(int focusChange) {
// Do something based on focus change...
}
}
focusChange 参数告诉你音频焦点发生了什么样的改变:
· AUDIOFOCUS_GAIN: 你得到这个音频的焦点
· AUDIOFOCUS_LOSS: 你可能长时间失去了这个音频的焦点. 你一定要停止所有的音频播放.因为你应该希望不要在后台聚焦太长事件,这个是一个好地方去尽可能的释放掉你的资源.例如:你应该释放掉MediaPlayer.
· AUDIOFOCUS_LOSS_TRANSIENT: 你暂时的失去了音频的焦点,但是应该要马上回到焦点上.你一定要停止掉所有的音频的播放,但是你能持有你的资源因为你可能很快的再次获得聚焦.
· AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 你暂时的失去了音频的焦点,但是你允许继续用小音量播放音乐而不是完全杀掉音频.
使用代码:
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
记住这些api只有在API 等级为8(android 2.2)或者之上才能用.
Handling the AUDIO_BECOMING_NOISY Intent
有可能会出现噪音的情况,例如,用户先用耳机听音乐,然后把耳机给拔出来的这种情况.
你可以通过处理 ACTION_AUDIO_BECOMING_NOISY 意图来确保在这种情况下,你的应用停止播放音乐.
<receiver android:name=".MusicIntentReceiver">
<intent-filter>
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
</intent-filter>
</receiver>
public class MusicIntentReceiver implements android.content.BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().equals(
android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
// signal your service to stop playback
// (via an Intent, for instance)
}
}
}