Android音頻管理之AudioManager(原創)

本文已獨家授權 郭霖 ( guolin_blog?) 公眾號發布!

本篇文章主要介紹的是Android應用(WebView加載H5的音頻管理只是其中的一種)如何有效管理或定制音頻的基本內容和一些思路。

事情的起因是這樣的,最近接到個需求WebView加載H5游戲,嗯,霹靂巴拉一頓猛敲基本上就搞定了,針對8.1系統也做了一些適配。好了,驗貨的時間到了,產品用他那傲嬌的手指一頓操作后不耐煩的說,這個應用為什么按下HOME鍵后(Back鍵禁用了 - - !)游戲還有聲音???當時我在想,目標Activity生命周期 onResume()和onPause()不是寫了對應的 myWebView.onResume();以及 myWebView.onPause()的啊啊!難不成那廝又在戲劇性耍我?

帶著黑人小哥頭上3個問號表情的我說到,按下HOME鍵沒有聲音的嘛,我先試試。產品瞬間反轉諂媚一笑說到,下午可以搞定嘛?我說,這個聲音可能是H5游戲引擎技術的問題,我先看下什么問題。然后,就是老套路編譯代碼打開應用插上耳機,進入游戲BGM時間,按下HOME鍵,MMP,聲音真沒消失!!!

好了,遇到問題后我跟大家的常規套路一樣,打開瀏覽器,輸入問題,接著search 結果真是驚呆了我&我的小伙伴,這都是些啥。

search - 1

怎么還出現了 中間home鍵按下有咯噔的聲音怎么回事?可能是問題沒有描述清楚,更加詳細一點的。嗯,看上去貌似挺靠譜

逐個點進去看看,最后統計匯總下這些博客里面提供的辦法,有使用反射(這個感覺怪怪的就沒有試驗)、有說什么要自定義webview,原生webview控制不住的......(我這本來就是用的第三方輪子)、有說針對生命周期下手的(這種特殊情況就是上面寫的webview.onResume()等等,之前也說了生命周期沒用)、有要我這邊研究H5音頻控件的......(喂喂喂、研究會花時間的好嘛?)、有說使用android與H5交互(也就是Android調H5界面關閉聲音的方法,這個是我之前想到的一種辦法)這個方法貌似可行,但是在后面的一些博客說到這種方案有些問題。。。更有甚者說,在Activity生命周期里面使用 myWebView.reload();這個函數,我也是醉了,拜托這是重新加載好嘛。

看來網上的一些辦法不行(或者搜索姿勢不對),我就帶著問題(描述的很清楚寫了生命周期沒有作用)去各大Android技術群里面詢問有經驗的開發人員,結果這些朋友還是說要在生命周期里面做手腳( - - !),無奈,只得另尋他法。

接著找到了研究游戲的H5技術,我問他們這是什么情況,他們說不清楚。。。然后我說可不可以讓我調取你們的JS腳本,他們說這樣會有一些問題。。。你還是自己想想辦法吧。

萬般無奈,最后突然想到了,不管你是什么BGM,你都是聲音,既然是聲音那么Android肯定提供了一些API供我們調用,果不其然,在Android多媒體找到了AudioManager這個類。

AudioManager(audio翻譯過來就是聲音、音頻):

AudioManager,音頻管理類,它主要提供了豐富的API讓開發者對應用的音量和鈴聲模式進行控制以及訪問。主要內容涉及到音頻流、聲音、藍牙、擴音器、耳機等等。

A:獲取實例

由于音頻管理涉及到多媒體,因此這個AudioManager獲取實例的姿勢是這樣的:

AudioManager audio = (AudioManager)Context.getSystemService(Context.AUDIO_SERVICE);

B:豐富的API

音頻管理類提供了大量的API,這些API是我們經常看到或者用到的,比如,調節音量,我相信對于很多人來說,調節音量這個姿勢是很常見的,比如你打開某視頻APP、某音樂APP其中肯定有調節音量大小的手勢,那么調節音量內部的邏輯可以使用 adjustStreamVolume(int streamType, int direction, int flags)

參數預覽:

streamType :要調整的音頻流類型。類型有以下幾種:

STREAM_VOICE_CALL(電話的音頻流),

STREAM_SYSTEM(系統聲音的音頻流),

STREAM_RING(電話鈴聲的音頻流),

STREAM_MUSIC(用于音樂播放的音頻流)

STREAM_ALARM(警報的音頻流)

區分流類型的目的是讓用戶能夠單獨地控制不同的種類的音頻。但上述音頻種類中,大多數都是被系統限制。除非應用需要做替換鬧鐘的鈴聲的操作,不然的話你只能通過STREAM_MUSIC來播放你的音頻。也就是說我們最常見操作的就是STREAM_MUSIC這個類型。

direction :調整音量的方向。其中: ADJUST_LOWER(減少鈴聲音量),ADJUST_RAISE(增加鈴聲音量)或 ADJUST_SAME(保持之前的鈴聲音量)

flags :一個或多個標志。可能這里的標志不是很好理解,是這樣,AudioManager提供了一些常量,我們可以將這些系統已經準備好的常量設置為這里的flags,比如:

FLAG_ALLOW_RINGER_MODES(更改音量時是否包括振鈴模式作為可能的選項),

FLAG_PLAY_SOUND(是否在改變音量時播放聲音),

FLAG_REMOVE_SOUND_AND_VIBRATE(刪除可能在隊列中或正在播放的任何聲音/振動(與更改音量有關)),

FLAG_SHOW_UI(顯示包含當前音量的吐司),

FLAG_VIBRATE(是否進入振動振鈴模式時是否振動)

比如我現在想要增加音量,就可以這樣寫:

AudioManager .?adjustStreamVolume (AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);

這句代碼的意思是指:指定調節類型為 音樂的音頻,增大音量,顯示音量圖形示意。舉一反三下面就是降低音量的代碼:

AudioManager .?adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_PLAY_SOUND);

比如,我想要獲取手機的音量,可以調取getStreamVolume(int streamType); 這里的streamType指獲得手機的當前流類型的音量,最大值為7(筆者親測 Android5.1系統 中興)最小值為0。

setStreamVolume(int streamType, int index, int flags)這個API顧名思義就是根據音頻流類型去設置音量大小的。主要這里的index不能超過最大索引,也就是7。

比如藍牙相關的API:

isBluetoothA2dpOn() :這個方法是檢查是否打開或關閉了到藍牙耳機的A2DP音頻路由。

isBluetoothScoOn(): 這個方法主要是檢查通信是否使用藍牙SCO。

startBluetoothSco(): 啟動藍牙SCO音頻連接

setBluetoothScoOn(boolean on): 請求使用藍牙SCO耳機進行通信。(設置true 代表用于通信的藍牙SCO; 設置false 即不使用藍牙SCO進行通信)

void stopBluetoothSco(): 停止藍牙SCO音頻連接。

麥克風相關:

setMicrophoneMute(boolean on):設置麥克風靜音開啟或關閉。( 設置true 關閉麥克風也就是麥克風靜音; 設置false,即關閉靜音打開麥克風)

setSpeakerphoneOn(boolean on):這個方法主要是判斷是否打開擴音器(設置true,即打開免提電話; false將其關閉)

isMicrophoneMute():判斷麥克風是否靜音或是否打開(如果麥克風靜音則為true,否則為false)

isMusicActive():判斷是否有音樂處于活躍狀態(如果任何音樂曲目有效,則為true)

等等,具體的更豐富更全面的API可以參考?AudioManager官方文檔

綜上,筆者的這個問題就可以通過AudioManager去進行操作:

首先,寫一個監聽HOME鍵的代碼(我這里使用的是廣播),在廣播里面進行操作,如果按下HOME鍵以后,廣播通知AudioManage關閉聲音,然后打開頁面回到目標Activity,在對應的生命周期里面進行音量的設置

代碼如下:

首先是監聽HOME鍵的廣播:

public class InnerRecevier extends BroadcastReceiver {

private AudioManager mAudioManager;

? ? final String SYSTEM_DIALOG_REASON_KEY ="reason";

? ? final String SYSTEM_DIALOG_REASON_RECENT_APPS ="recentapps";

? ? final String SYSTEM_DIALOG_REASON_HOME_KEY ="homekey";

? ? @Override

? ? public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

? ? ? ? mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

? ? ? ? if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {

String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);

? ? ? ? ? ? if (reason !=null) {

if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {

//按下HOEM鍵后,設置音頻流類型為STREAM_MUSIC,Volume為0(也就是沒有聲音)

mAudioManager.setStreamVolume(STREAM_MUSIC,0,0);

//? ? ? ? ? ? ? ? ? ? Toast.makeText(context, "點擊了Home鍵", Toast.LENGTH_SHORT).show();

? ? ? ? ? ? ? ? }else if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {

//? ? ? ? ? ? ? ? ? ? Toast.makeText(context, "多任務鍵被監聽", Toast.LENGTH_SHORT).show();

? ? ? ? ? ? ? ? }

}

}

}

}

接著,在 目標Activity 注冊廣播,(別忘了清單文件注冊):

void initHomeBroadCast() {

? ?//創建廣播

? ? InnerRecevier innerReceiver =new InnerRecevier();

? ? //動態注冊廣播

? ? IntentFilter intentFilter =new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

? ? //啟動廣播

? ? registerReceiver(innerReceiver, intentFilter);

}

最后,對生命周期onRestart()方法里面,通過AudioManage設置音量即可

@Override

protected void onRestart() {

????super.onRestart();

? ? //設置音量 ?音頻流類型為STREAM_MUSIC,Volume為6 第二個索引不能超過最大索引

? ? mAudioManager.setStreamVolume(STREAM_MUSIC, 6, 0);

}

測試:成功解決了WebView加載H5按下HOME鍵的聲音問題。

哦~這個產品貌似忘了電源鍵也是可以關閉界面的吧 ~ ~ ~

進一步思考:

我們知道,一款手機可能會有多個應用去播放音頻,(手機安裝多款音視頻播放器這個是很常見的現象、同時打開多個音視頻播放器也是很正常的)

試想如果不有效的處理應用的音頻,會出現什么情況?我們在聽歌的同時可能還會聽到啪啪啪的聲音。(注:這里的啪啪啪指觀看羽毛球視頻)為了防止多個音樂播放應用同時播放音頻,

谷歌技術團隊使用音頻焦點(Audio Focus)來控制音頻的播放。也就是,當且僅當apk獲取到音頻焦點成功以后,才可以播放音頻!

首先,如何獲取音頻焦點?獲取音頻焦點通過( AudioManager類 )這個方法:

? public int? requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)

來獲取音頻焦點,這個方法有三個參數,下面仔細分析下這三個參數

參數一:OnAudioFocusChangeListener(音頻焦點發生改變時候的監聽),這個OnAudioFocusChangeListener是AudioManager的一個內部接口,本質是監聽 音頻焦點的狀態。

比如,是否獲取了焦點、焦點是否失去、焦點暫時失去等狀態 ,通過源碼可以得知,它有四種狀態,分別是:

狀態一:AudioManager.AUDIOFOCUS_GAIN

狀態二:AudioManager.AUDIOFOCUS_LOSS

狀態三:AudioManager.AUDIOFOCUS_LOSS_TRANSIENT

狀態四:AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK

以上四種狀態分別代表的意思如下:

狀態一:即應用獲取了焦點,獲取了焦點即正常播放音頻

狀態二:即應用徹底失去了焦點,不能播放音頻

狀態三:即應用暫時失去焦點(比如我們按下HOME鍵、電源鍵等)

狀態四:應用暫時失去焦點,但是這個DUCK(DUCK翻譯:鴨子、閃避、躲避)狀態比較特殊,這種狀態意味著其它應用可以繼續播放,僅僅是在這一刻降低自己應用的音量,直到重新獲取到音頻焦點后恢復正常音量。應用場景有,比如在播放音樂的時候突然出現一個短暫的短信提示聲音,僅僅是把歌曲的音量暫時調低,使得用戶能夠聽到短信提示聲,在此之后便立馬恢復正常播放;再比如語言導航等等

注意:一旦結束了播放,需要確保調用abandonAudioFocus()方法。也就是告知系統我們不再需要獲取焦點且注銷AudioManager.OnAudioFocusChangeListener監聽器。

因此我們可以有如下代碼去監聽狀態:

? ? //焦點發生改變的監聽

? ? private AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener(){

? ? ? ? @Override

? ? ? ? public void onAudioFocusChange(int focusChange) {

? ? ? ? ? ? Log.i(TAG, "onAudioFocusChange: == "+focusChange);

? ? ? ? ? ? if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT){

? ? ? ? ? ? //暫停播放? 停止播放

? ? ? ? ? ? ? ? Log.i(TAG, "onAudioFocusChange: == AUDIOFOCUS_LOSS_TRANSIENT 暫停播放 停止播放");

? ? ? ? ? ? }else if (focusChange == AUDIOFOCUS_GAIN){

? ? ? ? ? ? //獲取焦點? 繼續播放

? ? ? ? ? ? ? ? Log.i(TAG, "onAudioFocusChange: == AUDIOFOCUS_GAIN 獲取焦點? 繼續播放");

? ? ? ? ? ? }else if (focusChange == AUDIOFOCUS_LOSS){

? ? ? ? ? ? //徹底丟失焦點

? ? ? ? ? ? ? ? mAudioManager.abandonAudioFocus(afChangeListener);

? ? ? ? ? ? ? ? Log.i(TAG, "onAudioFocusChange: == AUDIOFOCUS_LOSS");

? ? ? ? ? ? }

? ? ? ? }

? ? };

參數二:streamType(流類型)這個上面也說了,一般設置為STREAM_MUSIC

參數三:durationHint (持續時間) 根據源碼得知有以下幾種固定寫法:

A:AUDIOFOCUS_GAIN_TRANSIENT? 焦點請求是臨時

B: AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK? 如果它的音頻輸出被暫停,那么之前成功獲取焦點的就可以繼續播放

C: AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 臨時的(語音備忘記錄或語音識別)

D:AUDIOFOCUS_GAIN 也就是焦點請求(這個用的較多)

另外源碼可以得知,這個獲取音頻焦點的返回值只有兩種:

一種是:AUDIOFOCUS_REQUEST_FAILED = 0

一種是:AUDIOFOCUS_REQUEST_GRANTED = 1,其中,AUDIOFOCUS_REQUEST_GRANTED代表的就是獲取音頻焦點成功

所以,獲取音頻焦點的寫法如下:

? ? ? ? int result = mAudioManager.requestAudioFocus(afChangeListener,

? ? ? ? ? ? ? ? // Use the music stream.

? ? ? ? ? ? ? ? AudioManager.STREAM_MUSIC,

? ? ? ? ? ? ? ? // Request permanent focus.

? ? ? ? ? ? ? ? AudioManager.AUDIOFOCUS_GAIN);

//? ? ? 如果請求結果AudioManager.AUDIOFOCUS_REQUEST_GRANTED 則表明 請求成功

? ? ? ? if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {

? ? ? ? }

思考:AudioManager的強大之處在于可以捕獲音頻流類型、藍牙相關、麥克風相關等等等,怎么使用主觀能動性全在開發者手中;另外通過音頻焦點、生命周期以及主動設置音量,可以幫助我們有效主動管理音頻。

如果這篇文章對您有開發or學習上的些許幫助,希望各位看官留下寶貴的star,謝謝。

Ps:著作權歸作者所有,轉載請注明作者, 商業轉載請聯系作者獲得授權,非商業轉載請注明出處(開頭或結尾請添加轉載出處,添加原文url地址),文章請勿濫用、開源項目僅供學習交流、也希望大家尊重筆者的勞動成果,謝謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,923評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,253評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,801評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,023評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,530評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,494評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,639評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,890評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,242評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,626評論 2 370

推薦閱讀更多精彩內容

  • 音頻播放 音頻播放聲音分為MediaPlayer和AudioTrack兩種方案的。MediaPlayer可以播放多...
    安仔夏天勤奮閱讀 43,402評論 6 43
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,775評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,574評論 25 707
  • 我不說什么心靈雞湯。我需要重塑我的新生。 臉蛋依舊重要,不支持不反對整容,但是擱我身上實現不了,因為我討厭假的。我...
    沐未閱讀 309評論 0 1
  • 這個世界上只分為兩種人:為生命英勇奮戰的人,和投降死去的人。無疑,《萬物的簽名》中的主角——阿爾瑪是前一種人。 這...
    饅頭橘子閱讀 525評論 4 5