1. 程式人生 > >android系統音量介面相關

android系統音量介面相關

涉及到的程式碼:

framework/base/media/
AudioManager.java
AudioService.java

framework/base/package/SystemUI/
VolumeDialogController.java ——- 介面邏輯處理
VolumeDialog.java ——— UI顯示
volume_dialog.xml ——— UI佈局檔案

什麼是AudioManger

AudioManger和AudioService的關係:

AudioManager 封裝了AudioService的服務,並通過getservice()方法獲取AudioService的 Service 例項,涉及到普遍使用的aidl.

呼叫的通用方式:
import android.media.AudioManager;
private AudioManager mAudioManager;
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
各種public方法隨你用,當然也包括系統音量的各種介面。

關於系統音量介面的呼叫將走到SystemUI中,涉及的關鍵檔案如下,看命名就能看出來:
framework/base/package/SystemUI/
VolumeDialogController.java ——- 介面邏輯處理
VolumeDialog.java ——— UI顯示
volume_dialog.xml ——— UI佈局檔案

關於系統音量的修改,一般情況下侷限於UI上的修改,那麼關注的檔案是VolumeDialog.java和volume_dialog.xml,至於如何修改,看能力和需求了。

CallbackHandler的使用 以及VolumeDialogController.java中C,VC,W這三個類的作用都是相當關鍵的,下面這篇部落格介紹比較多

題外話:

關於原始碼中引用資原始檔編譯報錯:cannot be resolved or is not a field

原始碼中新增額外的圖片,直接在程式碼中引用類似這樣的設定背景操作:
ImageView.setBackground(getContext().getDrawable(R.drawable.android_ic_music_0));

編譯時會報:cannot be resolved or is not a field錯誤,檢查程式碼沒有問題,各種解析度的資源路徑中也有圖片資源,R檔案中有對應的id,也是需要導R包的,編譯前touch *和git clean,仍然無法解決問題。

原理不明,解決方法如下:
在xml中先引用一下圖片(如果加入一批圖片,只需引用一張即可),這樣就能解決問題,之後在將xml中的引用去除,只在程式碼中引用,也能編譯通過,
貌似一上來直接在程式碼中引用圖片是不行的,只有在xml中有引用圖片,那麼新加入的一批圖片才會在更多的R檔案中註冊(全域性搜尋圖片名而得,個人臆想,不作數),此後才能在程式碼中引用。問題能解決,原因還是不明。

關於Dialog是否依附Activity的問題

Dialog一般情況下是依附於activity存在的,否則會報出Android.view.WindowManager$BadTokenException:Unable to add window的錯誤
特例情況(或者說解決上述報錯的辦法):
1.顯示一個系統界別的dialog,即全域性性質的dialog。這種dialog在任何介面下都可以彈出來。但是,這種dialog不相應home鍵和返回鍵,即強制使用者必須對dialog作出操作後。
使用方法是在dialog.show()語句之前設定dialog的window的type是system alert型。
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mDialog.show();
如需要,同時在AndroidManifast中申請許可權
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2.在dialog的背後加一個透明的activity。即先顯示一個透明的activity,在使用activity的context顯示dialog。需要注意的是,activity在destroy的時候一定要把dialog給dismiss掉,
否則activity消失但dialog還在,會crash。
以上是兩種特例情況,原始碼中的非activity下的dialog彈窗一般會使用特例1的方法(通話時長顯示等),但是並無法確認所有的彈窗都是Dialog。

關於外放和耳機音量統一問題

  • 前言:android源生的程式碼是將兩者的音量值獨立開來,這是很人性化的設計,畢竟人對耳機音量和外放音量的需求是不一樣的
  • 此處的統一音量值是特殊專案(無外放裝置的專案)的特殊需求,此處記錄一下完成思路,萬一忘了就尷尬了。。。
  • 其實還有一個方法就是驅動修改預設的音訊通道,但是有可能影響到其他的音量,比如藍芽耳機連線時的音量,所以還是在上層修改比較容易控制

    思路:不去強行修改原本正確的輸出裝置,以免導致其他bug,在做音量調節(多媒體音量)的時候,保持外放和耳機音量同步

  • 涉及程式碼:AudioManager.java,AudioService.java,AudioSystem.java

  • 關鍵方法:AudioService.java中的adjustStreamVolume(int streamType, int direction, int flags,String callingPackage, String caller, int uid)
  • 保持兩者音量同步的關鍵是在音量調節的時候能找到耳機和外放的裝置值,很幸運,關鍵方法中就找到了,通過下面的方法獲取到當前的輸出裝置(裝置的標誌位定義在Audioystem.java中)
    final int device = getDeviceForStream(streamTypeAlias);
    AudioSystem.DEVICE_OUT_SPEAKER:外放
    AudioSystem.DEVICE_OUT_WIRED_HEADSET:耳機
  • 那麼在關鍵方法的最後加上同步音量的程式碼就好了,注意,新增的同步音量的程式碼不可以彈出音量調節UI(VolumeDialog)
    新增如下:
if ((streamType == AudioSystem.STREAM_MUSIC)){
    if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
        setStreamVolumeInt(streamType,index,AudioSystem.DEVICE_OUT_WIRED_HEADSET,false,caller);
    } else if((device & AudioSystem.DEVICE_OUT_WIRED_HEADSET) != 0){
        setStreamVolumeInt(streamType,index,AudioSystem.DEVICE_OUT_SPEAKER,false,caller);
    }
}

思路明確,其實並不難