android AudioManager AUDIOFOCUS

如今開始做音乐播放器的模块。遇到了几个问题

当播放音乐的过程中,去调节音量或者情景模式中的铃声设置,结果会有两种声音同一时候响起。

引起此问题的解决办法是音乐焦点问题没弄清

现分析一下音乐焦点的几个属性:源代码在frameworks/base/media/java/andorid/media/AudioManager.java中

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 <span id="summary_alias_container"><span id="short_desc_nonedit_display"></span></span> 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 */
}
<span id="summary_alias_container"></span>
时间: 2024-12-13 19:35:20

android AudioManager AUDIOFOCUS的相关文章

Android AudioManager获取媒体焦点

Android是多任务系统,Audio系统是竞争资源.Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争的管理与协调.本文主要讲解AudioFocus的使用. 按照AudioFocus的机制,在使用AudioStream之前,需要申请AudioFocus,在获得AudioFocus之后才可以使用相应的AudioStream:如果有别的程序竞争你正在使用的AudioStream,你的程序需要在收到通知之

从零开始学android&lt;AudioManager 声音编辑器.五十.&gt;

我们在使用android手机的时候往往会用到声音的增大和缩小操作,在设置情景模式的时候往往会用到静音和震动的操作,这往往就是由AudioManager来控制的. 今天我们就来看一下AudioManager 的使用. 首先要想操作声音必须取得这个服务,在前面我们学过取得系统服务的方法 AudioManager audio = (AudioManager) super.getSystemService(Context.AUDIO_SERVICE); 然后用这个类中的方法来操作声音组件,接下来用例子进

Android audioManager解决MediaPlayer AudioTrack 调节音量问

在听筒模式下 am.setSpeakerphoneOn(false); setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); am.setMode(AudioManager.MODE_IN_CALL); 我用Mediaplayer AudioTrack调节音量总是失败 at.setStereoVolume(vol, vol); player.setVolume(vol,vol); 后来 决定用AudioManager来调节音量 Audio

Android Audio Focus的应用(requestAudioFocus)

网址:http://blog.csdn.net/dadoneo/article/details/8252933 FROM: http://www.linuxidc.com/Linux/2012-04/57902.htm ================================================ Android是多任务系统,Audio系统是竞争资源.Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对A

Android多媒体开发介绍(转)

Android多媒体开发介绍 转自:http://blog.csdn.net/reiliu/article/details/9060557 一.       多媒体架构 基于第三方PacketVideo公司的OpenCORE来实现,支持所有通用的音频/视频/静态图像格式,包括:MPEG4.H.264.MP3.AAC.AMR.JPG.PNG.GIF等.从功能上分为两部分,一是音/视频的回放(PlayBack),二是音视频的纪录(Recorder). CODEC(编解码器)使用OpenMAX 1L

【Android】实验7 BindService模拟通信 截止提交日期2016.5.3

实验7 BindService模拟通信 [目的] 实现启动端和BindService之间的双向通信 [要求] 1)   实现从启动端传递一个数据至BindService端: 2)   实现使用BindService服务播放项目源文件中的音乐: 3)   实现在启动端通过“增加”和“降低”两个按钮控制音频音量大小. 4)   实现在启动端通过“暂停”按钮控制音频暂停播放. [原理] 参考教案,理解BindService和启动端的通信原理 AudioManager的理解 AudioManager类位

android音量知识总结

当开发多媒体应用或者游戏应用的时候,需要使用音量控制键来设置程序的音量大小.在Android系统中有多中音频流,通过Activity中的函数 setVolumeControlStream(int streamType)可以设置该Activity中音量控制键控制的音频流,一般在onCreate函数中设置. Android中有如下几种音频流: •AudioManager.STREAM_MUSIC /** The audio stream for music playback */•AudioMana

android调节音量——AudioManager的应用

Android中可以通过程序获取系统手机的铃声和音量.同样,也可以设置铃声和音量.android中给出了AudioManager类来实现音量获取.音量控制. 本篇基于 Android API 中的 AudioManager 作讲述,最后给出实例. 下面是本篇大纲: 1.认识 AudioManager 2.AudioManager 主要方法介绍 3.参数解释 1.认识 AudioManager AudioManager 类位于 android.Media 包中,该类提供访问控制音量和钤声模式的操作

Android开发之AudioManager(音频管理器)详解

AudioManager简介: AudioManager类提供了访问音量和振铃器mode控制.使用Context.getSystemService(Context.AUDIO_SERVICE)来得到这个类的一个实例. 公有方法: Public Methods int abandonAudioFocus(AudioManager.OnAudioFocusChangeListenerl) 放弃音频的焦点. void adjustStreamVolume(int streamType, int dir