Android FM模塊學習之四源碼解析(二)

上一章咱們瞭解了FM主activity:FMRadio.java,若沒查看的,請打開連接Android FM模塊學習之四源碼解析(一)java

查看fmradio.java源碼註釋。接下來咱們來看看FM重要的一個類:FMRadioService.javaapi

由上一章咱們已經知道,打開FM時,在OnStart函數中會bindToService來開啓服務,函數

    public  boolean bindToService(Context context, ServiceConnection callback) {
          Log.e(LOGTAG, "bindToService: Context with serviceconnection callback");
          context.startService(new Intent(context, FMRadioService.class));
          ServiceBinder sb = new ServiceBinder(callback);
          sConnectionMap.put(context, sb);
          return context.bindService((new Intent()).setClass(context,
                                                             FMRadioService.class), sb, 0);
       }學習

開啓了服務,就會去作一些FM的操做和響應。咱們進入到類FMRadioSerivce.java中ui

在publicvoid onCreate() 方法裏初始化數據this

mPrefs = new FmSharedPreferences(this);初始化存取數據設計

 

電話監聽
TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
      tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE |
                                       PhoneStateListener.LISTEN_DATA_ACTIVITY);對象

保持CPU 運轉,屏幕和鍵盤燈有多是關閉的blog

    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
          mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
          mWakeLock.setReferenceCounted(false);
接口

註冊屏幕是否開啓

     registerScreenOnOffListener();

      註冊耳機

     registerHeadsetListener();

     registerSleepExpired();

     registerRecordTimeout();

     registerDelayedServiceStop();

     registerFMRecordingStatus();

 

Activity銷燬界面方法

public void onDestroy()

在onDestroy方法裏mDelayedStopHandler.removeCallbacksAndMessages(null); 移除回調handler信息

關閉警鈴cancelAlarms()

將聲音設爲音樂焦點
 //release the audio focus listener
      AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
      if (isMuted()) {
          mMuted = false;
          audioManager.setStreamMute(AudioManager.STREAM_MUSIC,false);
      }
      audioManager.abandonAudioFocus(mAudioFocusListener);

遺棄音頻管理監聽

audioManager.abandonAudioFocus(mAudioFocusListener);

 

在onDestroy方法裏卸載註冊

    /* Remove the Screen On/off listener */
          if (mScreenOnOffReceiver != null) {
              unregisterReceiver(mScreenOnOffReceiver);
              mScreenOnOffReceiver = null;
          }
          /* Unregister the headset Broadcase receiver */
          
           if (mAirplaneModeReceiver != null) {
              unregisterReceiver(mAirplaneModeReceiver);
              mAirplaneModeReceiver = null;
          }
          
          if (mHeadsetReceiver != null) {unregisterReceiver(mHeadsetReceiver);
              mHeadsetReceiver = null;
          }
          if( mMusicCommandListener != null ) {
              unregisterReceiver(mMusicCommandListener);
              mMusicCommandListener = null;
          }
          if( mFmMediaButtonListener != null ) {
              unregisterReceiver(mFmMediaButtonListener);
              mFmMediaButtonListener = null;
          } if (mAudioBecomeNoisyListener != null) {
              unregisterReceiver(mAudioBecomeNoisyListener);
              mAudioBecomeNoisyListener = null;
          }
          if (mSleepExpiredListener != null ) {
              unregisterReceiver(mSleepExpiredListener);
              mSleepExpiredListener = null;
          }
          if (mRecordTimeoutListener != null) {
              unregisterReceiver(mRecordTimeoutListener);
              mRecordTimeoutListener = null;
          } if (mDelayedServiceStopListener != null) {
              unregisterReceiver(mDelayedServiceStopListener);
              mDelayedServiceStopListener = null;
          }
          if (mFmRecordingStatus != null ) {
              unregisterReceiver(mFmRecordingStatus);
              mFmRecordingStatus = null;
          }


關閉收音機fmOff();關掉調頻:禁用調頻主機和硬件。

    TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
          tmgr.listen(mPhoneStateListener, 0);
     
          Log.d(LOGTAG, "onDestroy: unbindFromService completed");
     
          //unregisterReceiver(mIntentReceiver);
          mWakeLock.release();


 

mWakeLock.release();//釋放喚醒鎖

在onDestroy方法裏設置電話監聽參數0失去監聽來電狀態

      tmgr.listen(mPhoneStateListener, 0);


public void registerFMRecordingStatus()控制

private void stop() 卸載FMMediaButtonIntentReceiver類的註冊

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

         ComponentName fmRadio = new ComponentName(this.getPackageName(),

                                 FMMediaButtonIntentReceiver.class.getName());

         mAudioManager.unregisterMediaButtonEventReceiver(fmRadio);

stop()方法調用gotoIdleState()方法刪除mDelayedStopHandler空消息,關閉警鈴

    private void gotoIdleState()
     
    mDelayedStopHandler.removeCallbacksAndMessages(null);
     
         cancelAlarms();
     
         setAlarmDelayedServiceStop();
     
         stopForeground(true);
     
      

public IBinder onBind(Intent intent)綁定service

mDelayedStopHandler.removeCallbacksAndMessages(null);

     cancelAlarms();關閉,警鈴

     mServiceInUse = true; 服務可使用

     /* Application/UI is attached, so get out of lower power mode */

     setLowPowerMode(false);

 

public void onRebind(Intent intent)從新捆綁service

 

public void onStart(Intent intent, intstartId) StartService啓動方法

 

public boolean onUnbind(Intent intent) 解綁service

 

得到進程名字方法private String getProcessName()

private void sendRecordIntent(int action) 發送錄音無序廣播在註冊錄音廣播接收方法中

 

private void sendRecordServiceIntent(intaction) 發送錄音狀態無序廣播

 

private void startFM()啓動FM方法:

    if(true == mAppShutdown) { // not to sendintent to AudioManager in Shutdown
     
    if (isCallActive()) { // when Call is activenever let audio playback
     
    if ( true == mPlaybackInProgress ) // no needto resend event


 

請求音頻聲音焦點

    AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
     
          int granted = audioManager.requestAudioFocus(mAudioFocusListener,AudioManager.STREAM_MUSIC,
     
                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

 

判斷是否請求聲音焦點成功

    if(granted !=AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
     
             Log.d(LOGTAG, "audio focuss couldnot be granted");
     
             return;
     
          }

 

    /**

     *請求改變焦點失敗

     */

    public static final int AUDIOFOCUS_REQUEST_FAILED = 0;

    /**

     *請求改變焦點成功     */

    public static final intAUDIOFOCUS_REQUEST_GRANTED = 1;

 

實現audioManager類的焦點監聽接口

     private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
           public void onAudioFocusChange(int focusChange) {
               mDelayedStopHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
           }
       };

 

 

請求聲音焦點當music響起fm短期暫停,music關閉後fm繼續播放private void requestFocus()

 

mAudioManager.registerMediaButtonEventReceiver(fmRadio);註冊多媒體按鈕事件監聽

mStoppedOnFocusLoss = false;中止聲音焦值爲false

中止fm使用

    private void stopFM(){
     
    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,AudioSystem.DEVICE_STATE_UNAVAILABLE,"");
     
    }

從新設置fm

    private void resetFM() {
     
           AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
     
       AudioSystem.DEVICE_STATE_UNAVAILABLE, "")
     
    }

mA2dpDeviceState.isDeviceAvailable()藍牙耳機設備可用是否可用

mOverA2DP全局變量是用來表示藍牙是否可用

 

public boolean mute()靜音模式 mMute 全局變量做爲是否開啓靜音模式作判斷

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

設置靜音audioManager.setStreamMute(AudioManager.STREAM_MUSIC,false);

public booleanisWiredHeadsetAvailable()耳機可用

public boolean isCallActive()通話中,調用getCallState()方法返回0爲空閒沒有通話,返回1是通話狀態。

public void stopRecording()中止錄音

private void fmActionOnCallState(int state ) 通話活動狀態在進入fm方法privateboolean fmOn()中調用此方法,會用到通話監聽獲取state值。

public boolean startA2dpPlayback()啓動藍牙播放方法,

 

final Runnable mHeadsetPluginHandler= new Runnable()方法當耳機被拔出就關閉fm

     final Runnable    mHeadsetPluginHandler = new Runnable() {
            public void run() {
                /* Update the UI based on the state change of the headset/antenna*/
                if(!isAntennaAvailable())
                {
                   // if (!isFmOn())
                   //     return;       
                    /* Disable FM and let the UI know */
                    fmOff();
                    try
                    {
                        /* Notify the UI/Activity, only if the service is "bound"
                      by an activity and if Callbacks are registered
                         */
                        if((mServiceInUse) && (mCallbacks != null) )
                        {
                            mCallbacks.onDisabled();
                        }
                    } catch (RemoteException e)
                    {
                        e.printStackTrace();
                    }
                }else
                {
                    /* headset is plugged back in,
                   So turn on FM if:
                   - FM is not already ON.
                   - If the FM UI/Activity is in the foreground
                     (the service is "bound" by an activity
                      and if Callbacks are registered)
                     */
                    if ((!isFmOn()) && (mServiceInUse)
                            && (mCallbacks != null))
                    {
                        if( true != fmOn() ) {
                            return;
                        }
                        try
                        {
                            mCallbacks.onEnabled();
                        } catch (RemoteException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };

public void clearStationInfo()清除fm狀態信息

FmRxEvCallbacksAdaptor fmCallbacks= new FmRxEvCallbacksAdaptor() 廣播接收類詳解

public void FmRxEvEnableReceiver()可用接收

public voidFmRxEvDisableReceiver() 失去接收功能

public void FmRxEvRadioReset()收音機重新設置

調整fm頻率並保持

FmSharedPreferences.setTunedFrequency(frequency);

mPrefs.Save();

enableStereo(FmSharedPreferences.getAudioOutputMode());獲取立體聲音是否可用

更新狀態欄的通知信息

startNotification();設計fm在後臺播放,service不被殺死,掛一個前臺通知,在中止FMRadioService中止前臺通知:

startForeground(FMRADIOSERVICE_STATUS,status);

在gotoIdleState()轉到fm空閒狀態的時候就中止前警鈴,後警鈴後臺服務,前臺通知欄等關閉。

mDelayedStopHandler.removeCallbacksAndMessages(null);

      cancelAlarms();

      setAlarmDelayedServiceStop();

      stopForeground(true);

 

readInternalAntennaAvailable() 讀的調頻的內部天線可用狀態

private booleansetAudioPath(boolean analogMode) 安裝判斷鈴聲路徑

/*
*打開調頻:調頻硬件啓動,並初始化調頻模塊
* @return返回值爲真:若是調頻使api調用成功, 假:若是api失敗了。
* /

private boolean fmOn()

mReceiver = newFmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);

從調頻堆棧接收回調函數

FmRxEvCallbacksAdaptor fmCallbacks = newFmRxEvCallbacksAdaptor()

 

mReceiver.setRawRdsGrpMask();

FmSharedPreferences.clearTags();清除Tag

Fm從新設置

public void FmRxEvRadioReset()

fmRadioReset();

調整屏幕狀態

public void FmRxEvRadioTuneStatus(intfrequency)

FmSharedPreferences.setTunedFrequency(frequency);

           mPrefs.Save();

清除狀態信息

if(mReceiver != null) {

    clearStationInfo();

}

更新狀態欄通知

startNotification();

判斷RDSS支持

public void FmRxEvRdsLockStatus(booleanbRDSSupported)

mCallbacks.onStationRDSSupported(bRDSSupported);

 

更新警鈴立體聲狀態

public void FmRxEvStereoStatus(booleanstereo)

mCallbacks.onAudioUpdate(stereo);

 

public void FmRxEvServiceAvailable(booleansignal)

fm搜索完成

public void FmRxEvSearchComplete(intfrequency)

FmSharedPreferences.setTunedFrequency(frequency);

clearStationInfo();

startNotification();

mWakeLock.acquire(10*1000);喚醒鎖屏超時10*1000就釋放喚醒對象

電話不是在空閒狀態換回false

if ( TelephonyManager.CALL_STATE_IDLE !=getCallState() ) {

        return bStatus;

}

正常進入fm

    if (isFmOn())
     
    {
     
         /* FM Is already on,*/
     
         bStatus = true;
     
         Log.d(LOGTAG, "mReceiver.already enabled");
     
    }

 獲取fm配置

FmConfig config =FmSharedPreferences.getFMConfiguration();

 

廣播接收穫取FM配置狀態

bStatus =mReceiver.enable(FmSharedPreferences.getFMConfiguration());

 

揚聲器可用設置boolean值

    if (isSpeakerEnabled()) {
     
                    setAudioPath(false);
     
               } else {
     
                    setAudioPath(true);
     
               }

 真正啓動fm播放聲音是AudioSystem.setDeviceConnectionState(); 設置fm設備啓動,設置fm耳機播放仍是揚聲器播放(核心代碼)

    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
                                   AudioSystem.DEVICE_STATE_AVAILABLE, "");
               if (isSpeakerEnabled()) {
                   mSpeakerPhoneOn = true;
                   Log.d(LOGTAG, "Audio source set it as speaker");
                   AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER);
               } else {
                   Log.d(LOGTAG, "Audio source set it as headset");
                   AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
               }


audio焦點監聽事件

AudioManager.OnAudioFocusChangeListener是申請成功以後監聽AudioFocus使用狀況的Listener,後續若是有別的程序要競爭AudioFocus,都是經過這個Listener的onAudioFocusChange()方法來通知這個AudioFocus的使用者的。

請求audio焦點屬性

AUDIOFOCUS_GAIN
指示申請獲得的AudioFocus不知道會持續多久,通常是長期佔有;
AUDIOFOCUS_GAIN_TRANSIENT
指示要申請的AudioFocus是暫時性的,會很快用完釋放的;
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK不但說要申請的AudioFocus是暫時性的,還指示當前正在使用AudioFocus的能夠繼續播放,只是要「duck」一下(下降音量)。

AUDIOFOCUS_REQUEST_GRANTED:申請成功;
AUDIOFOCUS_REQUEST_FAILED:申請失敗。

搶奪焦點
AUDIOFOCUS_GAIN:得到了Audio Focus;
AUDIOFOCUS_LOSS:失去了Audio Focus,並將會持續很長的時間。這裏由於可能會停掉很長時間,因此不單單要中止Audio的播放,最好直接釋放掉Media資源。而由於中止播放Audio的時間會很長,若是程序由於這個緣由而失去AudioFocus,最好不要讓它再次自動得到AudioFocus而繼續播放,否則忽然冒出來的聲音會讓用戶感受莫名其妙,感覺很很差。這裏直接放棄AudioFocus,固然也不用再偵聽遠程播放控制【以下面代碼的處理】。要再次播放,除非用戶再在界面上點擊開始播放,才從新初始化Media,進行播放。
AUDIOFOCUS_LOSS_TRANSIENT:暫時失去Audio Focus,並會很快再次得到。必須中止Audio的播放,可是由於可能會很快再次得到AudioFocus,這裏能夠不釋放Media資源;

AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暫時失去AudioFocus,可是能夠繼續播放,不過要在下降音量。    

相關文章
相關標籤/搜索