android中音量調節的辦法

  Android調整音量方法有兩種,一種是漸進式,即像手動按音量鍵一樣,一步一步增加或減少,另一種是直接設置音量值.

  漸進式的:

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

   public void adjustStreamVolume (int streamType, int direction, int flags);

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

    adjustStreamVolume 最終調用的是這個方法,這個方法裏有三個參數,分析如下:

     第一個,streamType是需要調整音量的類型:  
    STREAM_ALARM 警報  
    STREAM_MUSIC 音樂回放即媒體音量  
    STREAM_NOTIFICATION 窗口頂部狀態欄Notification,  
    STREAM_RING 鈴聲  
    STREAM_SYSTEM 系統  
    STREAM_VOICE_CALL 通話  
    STREAM_DTMF 電話撥號音頻  

   

    第二個direction,是調整的方向,增加或減少:  

    ADJUST_LOWER 降低音量  
    ADJUST_RAISE 升高音量  
    ADJUST_SAME 保持不變,貌似只是顯示當前音量大小。

   

     第三個flags是一些附加參數,只介紹兩個常用的  
     FLAG_PLAY_SOUND 調整音量時播放聲音  
     FLAG_SHOW_UI 調整時顯示音量條,就是按音量鍵出現的那個  
     0 表示什麼也沒有  


   下面看下adjustStreamVolume 的具體調用:

   public void adjustStreamVolume(int streamType, int direction, int flags) { 

                       IAudioService service = getService(); 

                      try {  
                            service.adjustStreamVolume(streamType, direction, flags);  
                          } catch (RemoteException e) {  
                      Log.e(TAG, "Dead object in adjustStreamVolume", e);  
                }  
       }  

   從代碼裏面可以看到,這裏是調用的AudioService裏面的adjustStreamVolume()方法,而AudioService的實現文件是:AudioService.java,其方法實現如下:




    public void adjustStreamVolume(int streamType, int direction, int flags) {  
           ensureValidDirection(direction);      //數據正確性檢查  
           ensureValidStreamType(streamType); //數據正確性檢查  
                      。  
                      。  
                      。  
           // If stream is muted, adjust last audible index only  
           int index;    //局部變量,保存調整後的音量狀態  
            //進行實際的音量調整,在mAudioHandler裏面進行。  
           if (streamState.muteCount() != 0) {  
               if (adjustVolume) {  
                   streamState.adjustLastAudibleIndex(direction);  
                   // Post a persist volume msg  
                   sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,  
                           SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);  
               }  
               index = streamState.mLastAudibleIndex;  
           } else {  
               if (adjustVolume && streamState.adjustIndex(direction)) {  
                   // Post message to set system volume (it in turn will post a message  
                   // to persist). Do not change volume if stream is muted.  
                   sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,  
                           streamState, 0);  
               }  
               index = streamState.mIndex;  
           }  
             
           // UI    //畫UI,即調整音量時出現的那個ProgressBar  
           mVolumePanel.postVolumeChanged(streamType, flags);  
           // Broadcast Intent    //發送廣播,廣播音量有改變的系統事件  
           sendVolumeUpdate(streamType, oldIndex, index);  
       }  




        下面先來看看畫UI的過程:
        跟進VolumePanel,發現這個類是一個handle,在postVolumeChanged()方法裏面有如下代碼:
[java] view plaincopy


    public void postVolumeChanged(int streamType, int flags) {  
            if (hasMessages(MSG_VOLUME_CHANGED)) return;  
            removeMessages(MSG_FREE_RESOURCES);  
            obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();  
        }  




        這裏利用了android裏面的消息機制來傳遞消息。對android的消息機制有所瞭解的應該知道,這個sendToTarget()方法實際上最後的Target就是它本身,也就是VolumePanel這個類本身,因此我們去這個Handle的handleMessage()方法裏面查找對於MSG_VOLUME_CHANGED這個類型消息的處理:
[java] view plaincopy


    case MSG_VOLUME_CHANGED: {  
                    onVolumeChanged(msg.arg1, msg.arg2);  
                    break;  
                }  




        可以看到,後續是在onVolumeChanged()這個方法裏面處理的,其兩個參數分別是streamType和flags,其中streamType是要調整的音量類型,而flags是傳過來的UI類型。onVolumeChanged()方法代碼如下:


[java] view plaincopy


    protected void onVolumeChanged(int streamType, int flags) {  
      
      
            if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");  
               
              //根據flags的不同,來做不同的處理  
            if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {    
                onShowVolumeChanged(streamType, flags);//UI顯示  
            }  
      
      
            if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {  
                removeMessages(MSG_PLAY_SOUND);  
                sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);//播放聲音  
            }  
      
      
            if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {  
                removeMessages(MSG_PLAY_SOUND);  
                removeMessages(MSG_VIBRATE);  
                onStopSounds();//停止播放聲音和震動  
            }  
      
      
            removeMessages(MSG_FREE_RESOURCES);  
            sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);  
        }  




        通過代碼可以知道,根據傳進去的flags不同,有不同的處理,下面就看看onShowVolumeChanged()方法的處理,也就是ProgressBar的顯示:
 
[java] view plaincopy


    protected void onShowVolumeChanged(int streamType, int flags) {  
            int index = mAudioService.getStreamVolume(streamType);  
            int message = UNKNOWN_VOLUME_TEXT;  
            int additionalMessage = 0;  
            mRingIsSilent = false;  
      
      
            if (LOGD) {  
                Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType  
                        + ", flags: " + flags + "), index: " + index);  
            }  
      
      
            // get max volume for progress bar  
            int max = mAudioService.getStreamMaxVolume(streamType);  
      
      
            switch (streamType) {  
      
      
                case AudioManager.STREAM_RING: {   //鈴聲的處理  
                    setRingerIcon();  
                    message = RINGTONE_VOLUME_TEXT;  
                    Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(  
                            mContext, RingtoneManager.TYPE_RINGTONE);  
                    Uri ringTwoUri = RingtoneManager.getActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE,   
                            PhoneFactory.RAW_PHONE_ID);  
                    if ((ringuri == null) && (ringTwoUri == null)) {  
                        additionalMessage =  
                            //com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;  
                            com.android.internal.R.string.volume_music_hint_sim1_and_sim2_silent_ringtone_selected;  
                        mRingIsSilent = true;  
                    } else if ((ringuri == null) && (ringTwoUri != null)) {  
                        additionalMessage =   
                            com.android.internal.R.string.volume_music_hint_silent_sim1_ringtone_selected;  
                    } else if ((ringuri != null) && (ringTwoUri == null)) {  
                        additionalMessage =   
                                com.android.internal.R.string.volume_music_hint_sim2_silent_ringtone_selected;  
                    }  
                    break;  
                }  
      
      
                case AudioManager.STREAM_MUSIC: {   //音樂聲音的處理  
                    message = MUSIC_VOLUME_TEXT;  
                    if (mAudioManager.isBluetoothA2dpOn()) {  
                        additionalMessage =  
                            com.android.internal.R.string.volume_music_hint_playing_through_bluetooth;  
                        setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_ad2p);  
                    } else {  
                        setSmallIcon(index);  
                    }  
                    break;  
                }  
      
      
                case AudioManager.STREAM_FM: {  //FM聲音的處理  
                    message = FM_VOLUME_TEXT;  
                    setSmallIcon(index);  
                    break;  
                }  
      
      
                case AudioManager.STREAM_VOICE_CALL: { //通話聲音的處理  
                    /* 
                     * For in-call voice call volume, there is no inaudible volume. 
                     * Rescale the UI control so the progress bar doesn't go all 
                     * the way to zero and don't show the mute icon. 
                     */  
                    index++;  
                    max++;  
                    message = INCALL_VOLUME_TEXT;  
                    setSmallIcon(index);  
                    break;  
                }  
      
      
                case AudioManager.STREAM_ALARM: {   //鬧鐘聲音的處理  
                    message = ALARM_VOLUME_TEXT;  
                    setSmallIcon(index);  
                    break;  
                }  
      
      
                case AudioManager.STREAM_NOTIFICATION: {   //Notification聲音的處理  
                    message = NOTIFICATION_VOLUME_TEXT;  
                    setSmallIcon(index);  
                    Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(  
                            mContext, RingtoneManager.TYPE_NOTIFICATION);  
                    if (ringuri == null) {  
                        additionalMessage =  
                            com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;  
                        mRingIsSilent = true;  
                    }  
                    break;  
                }  
      
      
                case AudioManager.STREAM_BLUETOOTH_SCO: {  //藍牙_sco?不知道是什麼東西。。  
                    /* 
                     * For in-call voice call volume, there is no inaudible volume. 
                     * Rescale the UI control so the progress bar doesn't go all 
                     * the way to zero and don't show the mute icon. 
                     */  
                    index++;  
                    max++;  
                    message = BLUETOOTH_INCALL_VOLUME_TEXT;  
                    setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);  
                    break;  
                }  
            }  
      
      
            String messageString = Resources.getSystem().getString(message);   //根據調整的聲音不同,顯示不同的信息  
            if (!mMessage.getText().equals(messageString)) {  
                mMessage.setText(messageString);  
            }  
      
      
            if (additionalMessage == 0) {  
                mAdditionalMessage.setVisibility(View.GONE);  
            } else {  
                mAdditionalMessage.setVisibility(View.VISIBLE);  
                mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage));  
            }  
      
      
            if (max != mLevel.getMax()) {  
                mLevel.setMax(max);  
            }  
            mLevel.setProgress(index);  //設置ProgressBar的值  
      
      
            mToast.setView(mView);  
            mToast.setDuration(Toast.LENGTH_SHORT);  
            mToast.setGravity(Gravity.TOP, 0, 0);  
            mToast.show();  
      
      
            // Do a little vibrate if applicable (only when going into vibrate mode)  
            if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&  
                    mAudioService.isStreamAffectedByRingerMode(streamType) &&  
                    mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE &&  
                    mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {  
                sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);  
            }  
        }  




        在通話聲音的處理中,有個setSmallIcon()函數,可以看到,這個是根據不同情況選擇ProgressBar上面顯示的圖片的。
[java] view plaincopy


    private void setSmallIcon(int index) {  
           mLargeStreamIcon.setVisibility(View.GONE);  
           mSmallStreamIcon.setVisibility(View.VISIBLE);  
      
      
           mSmallStreamIcon.setImageResource(index == 0  
                   ? com.android.internal.R.drawable.ic_volume_off_small  
                   : com.android.internal.R.drawable.ic_volume_small);  
       }  




        View view = mView = inflater.inflate(com.android.internal.R.layout.volume_adjust, null);
        mLevel就是顯示的那個ProgressBar,mLevel = (ProgressBar) view.findViewById(com.android.internal.R.id.level);
        從這裏我們可以看到,聲音調整顯示的佈局文件是volume_adjust.xml,如果想自己對聲音顯示的佈局進行調整的話,就可以自己手動修改這個佈局文件,達到自己想要的效果了。
        到這裏就把聲音調整的UI顯示過程分析完了,下面接着來分析聲音調整廣播發送sendVolumeUpdate():
 
[java] view plaincopy


    private void sendVolumeUpdate(int streamType, int oldIndex, int index) {  
            oldIndex = (oldIndex + 5) / 10;  
            index = (index + 5) / 10;  
      
      
            Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);  
            intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);  
            intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);  
            intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);  
      
      
            mContext.sendBroadcast(intent);  
        }  

        可以看到,這裏發送了一個廣播,而廣播的內容是:VOLUME_CHANGED_ACTION,也即"android.media.VOLUME_CHANGED_ACTION";當對音量改變事件有興趣時,就可以接收這個廣播,並做出相應的處理。至此,聲音調整的相關流程就分析的差不多了。


    直接設置音量值:

    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

   audioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, arg1, 0);

   audioManager.setStreamVolume(3, arg1, 0);//  3 代表  AudioManager.STREAM_MUSIC

   

這個方法主要用在有seekbar,調節音量的形式上。在設置->聲音->音量 就是這種形式。

    但是這個方法有個缺點,就是在播放音樂的時候使用這種方式連續調整音量,會倒是音樂聲音出現斷斷續續的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章