首页 > 代码库 > Android FM模块学习之四源码解析(二)

Android FM模块学习之四源码解析(二)

上一章我们了解了FM主activity:FMRadio.java,若没查看的,请打开链接Android FM模块学习之四源码解析(一)

查看fmradio.java源码注释。接下来我们来看看FM重要的一个类:FMRadioService.java

由上一章我们已经知道,打开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中

在publicvoid onCreate() 方法里初始化数据

mPrefs = new FmSharedPreferences(this);初始化存取数据

 

电话监听

TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
      tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE |
                                       PhoneStateListener.LISTEN_DATA_ACTIVITY);

 保持CPU 运转,屏幕和键盘灯有可能是关闭的

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();<span style="font-family:KaiTi_GB2312;">  </span>释放唤醒锁

在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,都是通过这个ListeneronAudioFocusChange()方法来通知这个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,但是可以继续播放,不过要在降低音量。







Android FM模块学习之四源码解析(二)