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

时间: 2024-10-12 07:17:31

Android FM模块学习之四源码解析(二)的相关文章

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

由于最近一直忙项目,没有时间来更新文档,今天抽空来写一点,希望大家可以学习使用! 这一章当然还是来分析FM模块的源码.FmReceiver.java publicFmReceiver(String devicePath,FmRxEvCallbacksAdaptor callback) throwsInstantiationException { mControl = new FmRxControls(); mRxEvents = new FmRxEventListner(); //registe

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

前一章我们了解了FM手动调频,接下来我们要分析FM模块用到的源码.此源码是基于高通平台的,别的平台都大同小异,只不过是平台自己作了些小改动而已. 首先要看的当然是主activity, FMRadio.java fmradio类启动FMRadioService.java类调用FmSharedPreferences类进行存储数据,PresetStation调整频率 setVolumeControlStream(AudioManager.STREAM_MUSIC);音乐回放即媒体音量 LoadedDa

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

我今天想分享的是FM模块的保存方法,即FmSharedPreferences.java FmSharedPreferences(Context context)在构造方法中加载Load()方法, public void  Load(){       Log.d(LOGTAG, "Load preferences ");       if(mContext == null)       {          return;       } SharedPreferences sp = m

Android FM模块学习之四源码分析(3)

接着看FM模块的其他几个次要的类的源码.这样来看FM上层的东西不是太多. 请看android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\Settings.java protected void onCreate(BundlesavedInstanceState) 从FMRadio.java用使用Intent跳转携带参数过来,在onCreate获取携带数据. protected void onCreate(Bundle savedIn

Android FM模块学习之四源码学习(2)

前几章我们分析了FM模块的几个主要的类文件,今天要分析的是:FMTransceiver.java   // 某些工程中名称为FMRadioService.java public class FmTransceiver { /* Primary FM States : * FM will be in one of the 4 states at any point of time * '0' - FMState_Turned_Off * '1' - FMState_Rx_Turned_On * '

Android FM模块学习之四源码分析(五)

前几章我们分析了FM模块的几个主要的类文件,今天要分析的是:FMTransceiver.java public class FmTransceiver { /* Primary FM States : * FM will be in one of the 4 states at any point of time * '0' - FMState_Turned_Off * '1' - FMState_Rx_Turned_On * '2' - FMState_Tx_Turned_On * '3' -

Android FM模块学习之四源码分析(七)

接上一篇,现在分析android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\StationListActivity.java protectedvoid onCreate(Bundle savedInstanceState)方法里 绑定FMRadioService服务 bindService((newIntent()).setClass(this, FMRadioService.class), osc, 0); 实例化ListVie

Android FM模块学习之源码分析(六)

现在是2015年1月啦,得改口说去年了,去年抽时间整理了一些FM模块的主要源码类的东西,今年再整理一下几个次要的类的源码.这样来看FM上层的东西不是太多. 请看android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\Settings.java protected void onCreate(BundlesavedInstanceState) 从FMRadio.java用使用Intent跳转携带参数过来,在onCreate获取携带数

AndroidFm模块学习之四源码解析(十一)

接上一篇,接下来看看android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\FmTags.java 当点击FMRadio.java菜单的全部频道选项,跳转到FmTags.java类 定义了一个ListView控件和一个简单适配器 private ListView la; private ArrayAdapter<String> adapter; 使用Handler刷新UI界面 private final Handler mHan