android声音调整源代码分析(转)

花门楼前见秋草,岂能贫贱相看老。这篇文章主要讲述android声音调整源代码分析(转)相关的知识,希望能为你提供帮助。
From:  http://blog.csdn.NET/bmj/article/details/8796421
加注: Settings.System.SAFE_HEADSET_VOLUME
【android声音调整源代码分析(转)】Android调整音量方法有两种,一种是渐进式,即像手动按音量键一样,一步一步增加或减少,另一种是直接设置音量值.
        下面先分析第一种渐进式的:
[java]  view plaincopy  

  1. AudioManager  am  =  (AudioManager)  getSystemService(Context.AUDIO_SERVICE);    
  2. public  void  adjustStreamVolume  (int  streamType,  int  direction,  int  flags)       
  3. am.adjustStreamVolume  (AudioManager.STREAM_MUSIC,  AudioManager.ADJUST_RAISE,  AudioManager.FLAG_SHOW_UI);        

        解释一下三个参数

第一个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 表示什么也没有  

        首先跟进AudioManager的adjustStreamVolume()方法可以看到如下代码:
 
[java]  view plaincopy  
  1. public  void  adjustStreamVolume(int  streamType,  int  direction,  int  flags)  {   
  2.                 IAudioservice  service  =  getService();    
  3.                 try  {   
  4.                         service.adjustStreamVolume(streamType,  direction,  flags);    
  5.                 }  catch  (RemoteException  e)  {   
  6.                         Log.e(TAG,  "Dead  object  in  adjustStreamVolume",  e);    
  7.                 }   
  8.         }   

        从代码里面可以看到,这里是调用的AudioService里面的adjustStreamVolume()方法,而AudioService的实现文件是:AudioService.java,其方法实现如下:
 
[java]  view plaincopy  
  1. public  void  adjustStreamVolume(int  streamType,  int  direction,  int  flags)  {   
  2.               ensureValidDirection(direction);             //数据正确性检查   
  3.               ensureValidStreamType(streamType);   //数据正确性检查   
  4.                                     。   
  5.                                     。   
  6.                                     。   
  7.               //  If  stream  is  muted,  adjust  last  audible  index  only   
  8.               int  index;         //局部变量,保存调整后的音量状态   
  9.                 //进行实际的音量调整,在mAudioHandler里面进行。   
  10.               if  (streamState.muteCount()  !=  0)  {   
  11.                       if  (adjustVolume)  {   
  12.                               streamState.adjustLastAudibleIndex(direction);    
  13.                               //  Post  a  persist  volume  msg   
  14.                               sendMsg(mAudioHandler,  MSG_PERSIST_VOLUME,  streamType,   
  15.                                               SENDMSG_REPLACE,  0,  1,  streamState,  PERSIST_DELAY);    
  16.                       }   
  17.                       index  =  streamState.mLastAudibleIndex;    
  18.               }  else  {   
  19.                       if  (adjustVolume  & &   streamState.adjustIndex(direction))  {   
  20.                               //  Post  message  to  set  system  volume  (it  in  turn  will  post  a  message   
  21.                               //  to  persist).  Do  not  change  volume  if  stream  is  muted.   
  22.                               sendMsg(mAudioHandler,  MSG_SET_SYSTEM_VOLUME,  STREAM_VOLUME_ALIAS[streamType],  SENDMSG_NOOP,  0,  0,   
  23.                                               streamState,  0);    
  24.                       }   
  25.                       index  =  streamState.mIndex;    
  26.               }   
  27.                  
  28.               //  UI        //画UI,即调整音量时出现的那个ProgressBar   
  29.               mVolumePanel.postVolumeChanged(streamType,  flags);    
  30.               //  Broadcast  Intent        //发送广播,广播音量有改变的系统事件   
  31.               sendVolumeUpdate(streamType,  oldIndex,  index);    
  32.       }   

        下面先来看看画UI的过程:
        跟进VolumePanel,发现这个类是一个handle,在postVolumeChanged()方法里面有如下代码:
[java]  view plaincopy  
  1. public  void  postVolumeChanged(int  streamType,  int  flags)  {   
  2.                 if  (hasMessages(MSG_VOLUME_CHANGED))  return;    
  3.                 removeMessages(MSG_FREE_RESOURCES);    
  4.                 obtainMessage(MSG_VOLUME_CHANGED,  streamType,  flags).sendToTarget();    
  5.         }   

        这里利用了android里面的消息机制来传递消息。对android的消息机制有所了解的应该知道,这个sendToTarget()方法实际上最后的Target就是它本身,也就是VolumePanel这个类本身,因此我们去这个Handle的handleMessage()方法里面查找对于MSG_VOLUME_CHANGED这个类型消息的处理:
[java]  view plaincopy  
  1. case  MSG_VOLUME_CHANGED:  {   
  2.                                 onVolumeChanged(msg.arg1,  msg.arg2);    
  3.                                 break;    
  4.                         }   

        可以看到,后续是在onVolumeChanged()这个方法里面处理的,其两个参数分别是streamType和flags,其中streamType是要调整的音量类型,而flags是传过来的UI类型。onVolumeChanged()方法代码如下:


[java]  view plaincopy  
  1. protected  void  onVolumeChanged(int  streamType,  int  flags)  {   
  2.    
  3.    
  4.                 if  (LOGD)  Log.d(TAG,  "onVolumeChanged(streamType:  "  +  streamType  +  ",  flags:  "  +  flags  +  ")");    
  5.                      
  6.                     //根据flags的不同,来做不同的处理   
  7.                 if  ((flags  &   AudioManager.FLAG_SHOW_UI)  !=  0)  {       
  8.                         onShowVolumeChanged(streamType,  flags); //UI显示   
  9.                 }   
  10.    
  11.    
  12.                 if  ((flags  &   AudioManager.FLAG_PLAY_SOUND)  !=  0  & &   !  mRingIsSilent)  {   
  13.                         removeMessages(MSG_PLAY_SOUND);    
  14.                         sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND,  streamType,  flags),  PLAY_SOUND_DELAY); //播放声音   
  15.                 }   
  16.    
  17.    
  18.                 if  ((flags  &   AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE)  !=  0)  {   
  19.                         removeMessages(MSG_PLAY_SOUND);    
  20.                         removeMessages(MSG_VIBRATE);    
  21.                         onStopSounds(); //停止播放声音和震动   
  22.                 }   
  23.    
  24.    
  25.                 removeMessages(MSG_FREE_RESOURCES);    
  26.                 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES),  FREE_DELAY);    
  27.         }   

        通过代码可以知道,根据传进去的flags不同,有不同的处理,下面就看看onShowVolumeChanged()方法的处理,也就是ProgressBar的显示:
 
[java]  view plaincopy  
  1. protected  void  onShowVolumeChanged(int  streamType,  int  flags)  {   
  2.                 int  index  =  mAudioService.getStreamVolume(streamType);    
  3.                 int  message  =  UNKNOWN_VOLUME_TEXT;    
  4.                 int  additionalMessage  =  0;    
  5.                 mRingIsSilent  =  false;    
  6.    
  7.    
  8.                 if  (LOGD)  {   
  9.                         Log.d(TAG,  "onShowVolumeChanged(streamType:  "  +  streamType   
  10.                                         +  ",  flags:  "  +  flags  +  "),  index:  "  +  index);    
  11.                 }   
  12.    
  13.    
  14.                 //  get  max  volume  for  progress  bar   
  15.                 int  max  =  mAudioService.getStreamMaxVolume(streamType);    
  16.    
  17.    
  18.                 switch  (streamType)  {   
  19.    
  20.    
  21.                         case  AudioManager.STREAM_RING:  {      //铃声的处理   
  22.                                 setRingerIcon();    
  23.                                 message  =  RINGTONE_VOLUME_TEXT;    
  24.                                 Uri  ringuri  =  RingtoneManager.getActualDefaultRingtoneUri(   
  25.                                                 mContext,  RingtoneManager.TYPE_RINGTONE);    
  26.                                 Uri  ringTwoUri  =  RingtoneManager.getActualDefaultRingtoneUri(mContext,  RingtoneManager.TYPE_RINGTONE,     
  27.                                                 PhoneFactory.RAW_PHONE_ID);    
  28.                                 if  ((ringuri  ==  null)  & &   (ringTwoUri  ==  null))  {   
  29.                                         additionalMessage  =   
  30.                                                 //com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;    
  31.                                                 com.android.internal.R.string.volume_music_hint_sim1_and_sim2_silent_ringtone_selected;    
  32.                                         mRingIsSilent  =  true;    
  33.                                 }  else  if  ((ringuri  ==  null)  & &   (ringTwoUri  !=  null))  {   
  34.                                         additionalMessage  =     
  35.                                                 com.android.internal.R.string.volume_music_hint_silent_sim1_ringtone_selected;    
  36.                                 }  else  if  ((ringuri  !=  null)  & &   (ringTwoUri  ==  null))  {   
  37.                                         additionalMessage  =     
  38.                                                         com.android.internal.R.string.volume_music_hint_sim2_silent_ringtone_selected;    
  39.                                 }   
  40.                                 break;    
  41.                         }   
  42.    
  43.    
  44.                         case  AudioManager.STREAM_MUSIC:  {      //音乐声音的处理   
  45.                                 message  =  MUSIC_VOLUME_TEXT;    
  46.                                 if  (mAudioManager.isBluetoothA2dpOn())  {   
  47.                                         additionalMessage  =   
  48.                                                 com.android.internal.R.string.volume_music_hint_playing_through_bluetooth;    
  49.                                         setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_ad2p);    
  50.                                 }  else  {   
  51.                                         setSmallIcon(index);    
  52.                                 }   
  53.                                 break;    
  54.                         }   
  55.    
  56.    
  57.                         case  AudioManager.STREAM_FM:  {    //FM声音的处理   
  58.                                 message  =  FM_VOLUME_TEXT;    
  59.                                 setSmallIcon(index);    
  60.                                 break;    
  61.                         }   
  62.    
  63.    
  64.                         case  AudioManager.STREAM_VOICE_CALL:  {  //通话声音的处理   
  65.                                 /* 
  66.                                   *  For  in-call  voice  call  volume,  there  is  no  inaudible  volume. 
  67.                                   *  Rescale  the  UI  control  so  the  progress  bar  doesn‘t  go  all 
  68.                                   *  the  way  to  zero  and  don‘t  show  the  mute  icon. 
  69.                                   */   
  70.                                 index++;    
  71.                                 max++;    
  72.                                 message  =  INCALL_VOLUME_TEXT;    
  73.                                 setSmallIcon(index);    
  74.                                 break;    
  75.                         }   
  76.    
  77.    
  78.                         case  AudioManager.STREAM_ALARM:  {      //闹钟声音的处理   
  79.                                 message  =  ALARM_VOLUME_TEXT;    
  80.                                 setSmallIcon(index);    
  81.                                 break;    
  82.                         }   
  83.    
  84.    
  85.                         case  AudioManager.STREAM_NOTIFICATION:  {      //Notification声音的处理   
  86.                                 message  =  NOTIFICATION_VOLUME_TEXT;    
  87.                                 setSmallIcon(index);    
  88.                                 Uri  ringuri  =  RingtoneManager.getActualDefaultRingtoneUri(   
  89.                                                 mContext,  RingtoneManager.TYPE_NOTIFICATION);    
  90.                                 if  (ringuri  ==  null)  {   
  91.                                         additionalMessage  =   
  92.                                                 com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;    
  93.                                         mRingIsSilent  =  true;    
  94.                                 }   
  95.                                 break;    
  96.                         }   
  97.    
  98.    
  99.                         case  AudioManager.STREAM_BLUETOOTH_SCO:  {    //蓝牙_sco?不知道是什么东西。。   
  100.                                 /* 
  101.                                   *  For  in-call  voice  call  volume,  there  is  no  inaudible  volume. 
  102.                                   *  Rescale  the  UI  control  so  the  progress  bar  doesn‘t  go  all 
  103.                                   *  the  way  to  zero  and  don‘t  show  the  mute  icon. 
  104.                                   */   
  105.                                 index++;    
  106.                                 max++;    
  107.                                 message  =  BLUETOOTH_INCALL_VOLUME_TEXT;    
  108.                                 setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);    
  109.                                 break;    
  110.                         }   
  111.                 }   
  112.    
  113.    
  114.                 String  messageString  =  Resources.getSystem().getString(message);       //根据调整的声音不同,显示不同的信息   
  115.                 if  (!mMessage.getText().equals(messageString))  {   
  116.                         mMessage.setText(messageString);    
  117.                 }   
  118.    
  119.    
  120.                 if  (additionalMessage  ==  0)  {   
  121.                         mAdditionalMessage.setVisibility(View.GONE);    
  122.                 }  else  {   
  123.                         mAdditionalMessage.setVisibility(View.VISIBLE);    
  124.                         mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage));    
  125.                 }   
  126.    
  127.    
  128.                 if  (max  !=  mLevel.getMax())  {   
  129.                         mLevel.setMax(max);    
  130.                 }   
  131.                 mLevel.setProgress(index);     //设置ProgressBar的值   
  132.    
  133.    
  134.                 mToast.setView(mView);    
  135.                 mToast.setDuration(Toast.LENGTH_SHORT);    
  136.                 mToast.setGravity(Gravity.TOP,  0,  0);    
  137.                 mToast.show();    
  138.    
  139.    
  140.                 //  Do  a  little  vibrate  if  applicable  (only  when  going  into  vibrate  mode)   
  141.                 if  ((flags  &   AudioManager.FLAG_VIBRATE)  !=  0  & &    
  142.                                 mAudioService.isStreamAffectedByRingerMode(streamType)  & &    
  143.                                 mAudioService.getRingerMode()  ==  AudioManager.RINGER_MODE_VIBRATE  & &    
  144.                                 mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER))  {   
  145.                         sendMessageDelayed(obtainMessage(MSG_VIBRATE),  VIBRATE_DELAY);    
  146.                 }   
  147.         }   

        在通话声音的处理中,有个setSmallIcon()函数,可以看到,这个是根据不同情况选择ProgressBar上面显示的图片的。
[java]  view plaincopy  
  1. private  void  setSmallIcon(int  index)  {   
  2.               mLargeStreamIcon.setVisibility(View.GONE);    
  3.               mSmallStreamIcon.setVisibility(View.VISIBLE);    
  4.    
  5.    
  6.               mSmallStreamIcon.setImageResource(index  ==  0   
  7.                               ?  com.android.internal.R.drawable.ic_volume_off_small   
  8.                               :  com.android.internal.R.drawable.ic_volume_small);    
  9.       }   

        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  
  1. private  void  sendVolumeUpdate(int  streamType,  int  oldIndex,  int  index)  {   
  2.                 oldIndex  =  (oldIndex  +  5)  /  10;    
  3.                 index  =  (index  +  5)  /  10;    
  4.    
  5.    
  6.                 Intent  intent  =  new  Intent(AudioManager.VOLUME_CHANGED_ACTION);    
  7.                 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,  streamType);    
  8.                 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE,  index);    
  9.                 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE,  oldIndex);    
  10.    
  11.    
  12.                 mContext.sendBroadcast(intent);    
  13.         }   

        可以看到,这里发送了一个广播,而广播的内容是:VOLUME_CHANGED_ACTION,也即"android.media.VOLUME_CHANGED_ACTION"; 当对音量改变事件有兴趣时,就可以接收这个广播,并做出相应的处理。至此,声音调整的相关流程就分析的差不多了。










































    推荐阅读