上一章咱們瞭解了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,可是能夠繼續播放,不過要在下降音量。