【Android应用开发技术:媒体开发】音频

作者:郭孝星

微博:郭孝星的新浪微博

邮箱:[email protected]

博客:http://blog.csdn.net/allenwells

Github:https://github.com/AllenWells

Android为播放音乐、闹铃、通知铃、来电声音、系统声音、打电话声音和DTMF频道都分别维护了一个隔离的音频流,这是我们能够控制不同音频的前提,这其中大多数的音频流都是被系统限制的,不能胡乱使用。

一 音频控制

默认情况下,按下音量控制键会调节当前被激活的音频流,如果我们的App没有播放任何声音,则会调节闹铃的声音。如果是一个游戏或音乐程序,则需要不管是否正在播放歌曲或者游戏目前是否发出声音,按硬件的音量键都会有相应的音量调节。

Android提供了setVolumeControlStream()的方法来直接控制指定的音频流,我们需要在鉴别出App使用了哪个音频流之后,在Activity或Fragment创建的时候就设置音量控制,这样保证App是否可见,音频功能都能正常工作,如下所示:

setVolumeControlStream(AudioManager.STREAM_MUSIC);

常见的媒体播放控制如下所示:

  • play
  • pause
  • stop
  • skip
  • previous

当我们在软件或者硬件(耳麦线控等)进行上述操作时,系统都会广播一个ACTION_MEDIA_BUTTON的Intent,为了响应那些操作,需要在AndroidManifest.xml文件中注册一个BroadcastReceiver,如下所示:

<receiver android:name=".RemoteControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

Receiver需要判断这个广播是来自哪个按钮的操作,Intent在 EXTRA_KEY_EVENT 中包含了KEY的信息,同样KeyEvent类包含了一列 KEYCODE_MEDIA_ 的静态变量来表示不同的媒体按钮,如下所示:

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                // Handle key press.
            }
        }
    }
}

因为可能有多个程序都同样监听了哪些控制按钮,那么必须在代码中特意控制当前哪个Receiver会进行响应,就就是需要进行适时的注册监听和取消监听,如下所示:

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);

二 音频焦点

当有多个音频播放音频时,它们的交互对于用户体验就显得很重要,为了防止多个App同时播放音频,Android使用焦点Audio Focus来控制音频播放,只有获得Audio Focus的App才能播放音频,App 播放音频的过程如下所示:

  • 发出请求
  • 接受请求
  • 音频焦点锁定

2.1 请求获取音频焦点

在App播放音频之前。我们需要获取它将要使用的音频流的音频焦点。通过使用requsetAudioFocus()方法来获取想要的音频焦点,如果请求成功,该方法会返回AUDIOFOCUS_REQUEST_GRANTED。

请求焦点的时候,我们需要确定请求的是哪种焦点类型,如下所示:

  • 短暂的焦点锁定:当期待播放一个短暂的音频的时候,例如,播放导航提示。
  • 开启Ducking的短暂焦点锁定:当请求短暂音频焦点的时候,我们可以选择是否开启Ducking。

Ducking是一个特殊的机制使得允许音频间歇性的短暂播放。 通常情况下,一个好的App在失去音频焦点的时候它会立即保持安静。如果我们选择在请求短暂音频焦点的时候开启了Ducking,那意味着其它App可以继续播放,仅仅是在这一刻降低自己的音量,在短暂重新获取到音频焦点后恢复正常音量。

也就是说,不用理会这个请求短暂焦点的请求,这并不会导致目前在播放的音频受到牵制,比如在播放音乐的时候突然出现一个短暂的短信提示声音,这个时候仅仅是把播放歌曲的音量暂时调低,好让短信声能够让用户听到,之后立马恢复正常播放。

  • 永久的焦点锁定:当计划播放可预期到的较长的音频的时候,例如播放音乐。

举例1

在播放音乐的时候请求永久的音频焦点,如下所示:

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    am.registerMediaButtonEventReceiver(RemoteControlReceiver);
    // Start playback.
}

一旦结束了播放,我们需要确保调用abandonAudioFocus()方法。这样会通知系统说你不再需要获取焦点并且取消注册AudioManager.OnAudioFocusChangeListener的监听。在释放短暂音频焦点的情况下,这会允许任何被打断的App继续播放。

// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);

举例2

开启Ducking的短暂焦点锁定,如下所示:

// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback.
}

2.2 处理失去音频焦点

在音频焦点的监听器里面,当接受到描述焦点改变的事件时会触发onAudioFocusChange()回调方法,对于失去焦点有三种类型,如下所示:

  • 短暂的失去焦点:通常在失去焦点的情况下,我们会暂停当前音频的播放或者降低音量,同时需要准备在重新获取焦点之后恢复播放。
  • 开启Ducking的短暂的失去焦点:Ducking是一个特殊的机制使得允许音频间歇性的短暂播放。在Ducking的情况下,正常播放的歌曲会降低音量来凸显**这个短暂的音频声音,这样既让这个短暂的声音比较突出,又不至于打断正常的声音。
  • 永久的失去焦点:假设另外一个程序开始播放音乐,那么我们的程序就应该有效的结束自己。常见的做法是:移除Button监听,允许新的音频播放器独占监听那些按钮时间,并且放弃自己的音频焦点,这种情况下,重新播放你的音频之前。我们需要确保用户重新点击了App的播放按钮。

举例1

短暂的失去焦点,如下所示:

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
            // Pause playback
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Resume playback
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
            am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
            am.abandonAudioFocus(afChangeListener);
            // Stop playback
        }
    }
};

举例2

播放器在暂时失去焦点时降低音量,并在重新获得音频焦点之后恢复原来的音量,如下所示:

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
            // Lower the volume
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Raise it back to normal
        }
    }
};

三 音频设备

用户在播放音乐时有多个选择,可以使用内置的扬声器,有线耳机或者A2DP蓝牙耳机。

A2DP(Advanced Audio Distribution Profile)即蓝牙音频传输模型协定,A2DP是能够采用耳机内的芯片来堆栈数据,达到声音的高清晰度。有A2DP的耳机就是蓝牙立体声耳机。声音能达到44.1kHz,一般的耳机只能达到8kHz。如果手机支持蓝牙,只要装载A2DP协议,就能使用A2DP耳机了。

3.1 检测音频输出设备

选择音频输出设备会影响App的行为,可以使用AudioManager来查询某个音频是输出到扬声器,有线耳机还是蓝牙,如下所示:

if (isBluetoothA2dpOn()) {
    // Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
    // Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
    // Adjust output for headsets
} else {
    // If audio plays and noone can hear it, is it still playing?
}

2.2 处理音频设备的改变

当有线耳机被拔出或者蓝牙设备断开连接的时候,音频流会自动输出到内置的扬声器上。假设之前播放声音很大,这个时候突然转到扬声器播放会显得非常嘈杂。Android系统会在那种事件发生时会广播带有ACTION_AUDIO_BECOMING_NOISY的intent。无论何时播放音频去注册一个BroadcastReceiver来监听这个intent会是比较好的做法。

在音乐播放器下,用户通常希望发生那样事情的时候能够暂停当前歌曲的播放。在游戏里,通常会选择减低音量。

private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
            // Pause the playback
        }
    }
}
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private void startPlayback() {
    registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}
private void stopPlayback() {
    unregisterReceiver(myNoisyAudioStreamReceiver);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-27 13:05:30

【Android应用开发技术:媒体开发】音频的相关文章

本人讲课时录制的Android应用开发技术教学视频

网盘地址:http://yun.baidu.com/pcloud/album/info?query_uk=1963923831&album_id=3523786484935252365 本人讲课时录制的视频,采用webex录制,视频文件内容相对较小30-50兆左右,1个视频文件平均大概有1个小时左右的时间,每个例子基本上从建立项目开始边做边讲. 由于讲课范围是Android应用开发技术,视频没涉及搭建环境,基础控件的使用等基础内容. 主要内容包括: 后台服务. 服务的绑定.服务和线程.远程服务和

【Android应用开发技术:用户界面】章节列表

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:用户界面]章节列表 [Android应用开发技术:用户界面]用户界面基本原理 [Android应用开发技术:用户界面]设备适配 [Android应用开发技术:用户界面]用户界面布局技巧 [Android应用开发技术:用户界面]View基本原理 [

【Android应用开发技术:图像处理】Bitmap显示性能优化分析

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:图像处理]章节列表 Bitmap经常会消耗大量内存而导致程序崩溃,常见的异常如下所示:java.lang.OutofMemoryError:bitmap size extends VM budget,因此为了保证程序的稳定性,我们应该小心处理程序

【Android应用开发技术:用户界面】界面设计中易混淆的概念汇总

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:用户界面]章节列表 一 px.dp.sp px:即像素,每个px对应屏幕上的一个点. dp:即设备独立像素,一种基于屏幕密度的抽象单位,在每英寸160点的显示器上:1 dp = 1 px. sp:即比例像素,主要用来处理字体大小,可以根据用户字体

【Android应用开发技术:用户界面】9Patch图片设计

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:用户界面]章节列表 9Patch图片是一种特殊的PNG图片,该图片以.9.png为后缀名,它在原始图片四周各添加一个宽度为1像素的线条,这4条线决定了该图片的缩放规则和内容显示格则. 一 9Patch图片的显示规则 9Patch图片left边和t

【Android应用开发技术:网络通信】网络服务可发现基本原理

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:网络通信]章节列表 网络服务发现(Network Service Discovery)是一种在局域网内可以辨识并使用其他设备上提供的服务的技术,这种技术在端对端应用(例如:文件共享.联机游戏)中提供很好的帮助. NSD是基于Apple的Bonjo

【Android应用开发技术:用户界面】布局管理器

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:用户界面]章节列表 布局管理继承于ViewGroup.它用来管理Android应用用户界面里各组件,它的使用使得Android应用的图形用户界面具有良好的平台无关性. 常见的布局方式例如以下所看到的: 线性布局 表格布局 帧布局 相对布局 网络布

【Android应用开发技术:媒体开发】打印

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells 在Android 4.4以及更高版本的系统中,提供了直接从Android应用程序打印图片和文字的服务,包括打印图片.HTML页面以及创建自定义打印文档等. 一 打印图片 Android Support Library中的PrintHelper提供了一种打印图片的简单方法,该类

【Android应用开发技术:媒体开发】录像

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells 一 启用相机 1.1 请求相机权限 <manifest ... > <uses-feature android:name="android.hardware.camera" android:required="true" /&g