編輯:關於Android編程
現在開始做音樂播放器的模塊,遇到了幾個問題
當播放音樂的過程中,去調節音量或者情景模式中的鈴聲設置,結果會有兩種聲音同時響起。引起此問題的原因是音樂焦點問題沒弄清
public static final int AUDIOFOCUS_NONE = 0;
指示申請得到的Audio Focus不知道會持續多久,一般是長期占有;獲得了Audio Focus;
public static final int AUDIOFOCUS_GAIN = 1;
指示要申請的AudioFocus是暫時性的,會很快用完釋放的;
public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
不但說要申請的AudioFocus是暫時性的,還指示當前正在使用AudioFocus的可以繼續播放,只是要“duck”一下(降低音量)。
public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4;
AudioManager.OnAudioFocusChangeListener是申請成功之後監聽AudioFocus使用情況的Listener,後續如果有別的程序要競爭AudioFocus,都是通過這個Listener的onAudioFocusChange()方法來通知這個Audio Focus的使用者的。
失去了Audio Focus,並將會持續很長的時間
public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
暫時失去Audio Focus,並會很快再次獲得。必須停止Audio的播放,但是因為可能會很快再次獲得AudioFocus,這裡可以不釋放Media資源;
public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
暫時失去AudioFocus,但是可以繼續播放,不過要在降低音量。
public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
-1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
看看剛才修改的一個問題:
問題描述:播放音樂時鬧鐘到來,把鬧鐘放在後台時進入文件管理器播放音頻,鬧鐘仍然在響應,鬧鐘和音樂同時響起;
問題分析:在鬧鐘鈴聲響起時,沒有去做音頻焦點的處理
解決方案:在packages/apps/deskclock/src/com/android/deskclock/alarms/AlarmKlaxon.java文件中加上焦點處理
修改後的源碼:
package com.android.deskclock.alarms;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Vibrator;
import com.android.deskclock.Log;
import com.android.deskclock.R;
import com.android.deskclock.provider.AlarmInstance;
import java.io.IOException;
/*add by leo.tan 20140717 for bugzilla 20064 start */
import android.os.Handler;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Message;
/*add by leo.tan 20140717 for bugzilla 20064 end */
/**
* Manages playing ringtone and vibrating the device.
*/
public class AlarmKlaxon {
private static final long[] sVibratePattern = new long[] { 500, 500 };
// Volume suggested by media team for in-call alarms.
private static final float IN_CALL_VOLUME = 0.125f;
private static boolean sStarted = false;
private static MediaPlayer sMediaPlayer = null;
/*add by leo.tan 20140717 for bugzilla 20064 start */
private static final int FOCUSCHANGE = 3000;
private static final int FADEDOWN = 5;
private static final int FADEUP = 6;
private static final int RETRY_REQUEST_FOCUS = 7;
private static final int OVER_SHORT_VIBRATOR = 8;
private static OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
android.util.Log.v(AlarmKlaxon, mAudioFocusListener::focusChange--> + focusChange);
mHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
}
};
/*add by leo.tan 20140717 for bugzilla 20064 end */
public static void stop(Context context) {
Log.v(AlarmKlaxon.stop());
if (sStarted) {
sStarted = false;
// Stop audio playing
if (sMediaPlayer != null) {
sMediaPlayer.stop();
AudioManager audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
audioManager.abandonAudioFocus(null);
sMediaPlayer.release();
sMediaPlayer = null;
}
/*add by leo.tan 20140717 for bugzilla 20064 start */
final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.abandonAudioFocus(mAudioFocusListener);
/*add by leo.tan 20140717 for bugzilla 20064 end */
((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE)).cancel();
}
}
public static void start(final Context context, AlarmInstance instance,
boolean inTelephoneCall) {
Log.v(AlarmKlaxon.start());
// Make sure we are stop before starting
stop(context);
if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) {
Uri alarmNoise = instance.mRingtone;
// Fall back on the default alarm if the database does not have an
// alarm stored.
if (alarmNoise == null) {
alarmNoise = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
if (Log.LOGV) {
Log.v(Using default alarm: + alarmNoise.toString());
}
}
TODO: Reuse mMediaPlayer instead of creating a new one and/or use RingtoneManager.
sMediaPlayer = new MediaPlayer();
sMediaPlayer.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(Error occurred while playing audio. Stopping AlarmKlaxon.);
AlarmKlaxon.stop(context);
return true;
}
});
try {
// Check if we are in a call. If we are, use the in-call alarm
// resource at a low volume to not disrupt the call.
if (inTelephoneCall) {
Log.v(Using the in-call alarm);
sMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
setDataSourceFromResource(context, sMediaPlayer, R.raw.in_call_alarm);
} else {
sMediaPlayer.setDataSource(context, alarmNoise);
}
startAlarm(context, sMediaPlayer);
}catch (Exception ex) {
Log.v(Using the fallback ringtone);
// The alarmNoise may be on the sd card which could be busy right
// now. Use the fallback ringtone.
try {
// Must reset the media player to clear the error state.
sMediaPlayer.reset();
setDataSourceFromResource(context, sMediaPlayer, R.raw.fallbackring);
startAlarm(context, sMediaPlayer);
} catch (Exception ex2) {
// At this point we just don't play anything.
Log.e(Failed to play fallback ringtone, ex2);
}
}
}
if (instance.mVibrate && !inTelephoneCall) {
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(sVibratePattern, 0);
}
sStarted = true;
}
// Do the common stuff when starting the alarm.
private static void startAlarm(Context context, MediaPlayer player) throws IOException {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// do not play alarms if stream volume is 0 (typically because ringer mode is silent).
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
player.setAudioStreamType(AudioManager.STREAM_ALARM);
player.setLooping(true);
player.prepare();
audioManager.requestAudioFocus(null,
AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
player.start();
}
/*add by leo.tan 20140717 for bugzilla 20064 start */
//在這個地方進行焦點的請求
final int requestResult = audioManager.requestAudioFocus(mAudioFocusListener,
AudioManager.STREAM_ALARM,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
/*add by leo.tan 20140717 for bugzilla 20064 end */
}
private static void setDataSourceFromResource(Context context, MediaPlayer player, int res)
throws IOException {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(res);
if (afd != null) {
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
}
}
/*add by leo.tan 20140717 for bugzilla 20064 start */
private static void setVolume(float vol) {
if(sMediaPlayer != null){
sMediaPlayer.setVolume(vol, vol);
}
}
//用handler來對焦點進行處理
private static Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case FOCUSCHANGE: {
switch (msg.arg1) {
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mHandler.removeMessages(FADEUP);
mHandler.sendEmptyMessage(FADEDOWN);
break;
case AudioManager.AUDIOFOCUS_GAIN:
mHandler.removeMessages(FADEDOWN);
mHandler.sendEmptyMessage(FADEUP);
break;
}
break;
}
case FADEDOWN:
// Turn off the sound
setVolume(0.0f);
break;
case FADEUP:
// Turn on the sound
setVolume(1.0f);
break;
}
}
};
/*add by leo.tan 20140717 for bugzilla 20064 end */
}
Android開發之通知欄Notification詳解
Notification的用法 --- 狀態欄通知發送一個狀態欄通知必須的兩個類:1. NotificationManager --- 狀態欄通知的管理類,負責發通知,清
Android開發中CheckBox的簡單用法示例
本文實例講述了Android開發中CheckBox的簡單用法。分享給大家供大家參考,具體如下:CheckBox是一種在界面開發中比較常見的控件,Android中UI開發也
Android 編程下字庫的使用及注意事項
在安卓操作系統下對於 TextView 字體的支持非常有限,默認情況下 TextView 的 typeface 屬性支持 Sans,serif,monospace 這三種
[Android開發系列]IT博客應用
1.關於坑 好吧,在此之前先來說一下,之前開的坑,恩,確實是坑,前面開的兩個android開發教程的坑,對不起,實在是沒什麼動力了,不過源碼都有的,大家可以參照githu