1. 程式人生 > >Android FM模組學習之四原始碼解析(一)

Android FM模組學習之四原始碼解析(一)

  前一章我們瞭解了FM手動調頻,接下來我們要分析FM模組用到的原始碼。此原始碼是基於高通平臺的,別的平臺都大同小異,只不過是平臺自己作了些小改動而已。

   首先要看的當然是主activity,

FMRadio.java

  fmradio類啟動FMRadioService.java類呼叫FmSharedPreferences類進行儲存資料,PresetStation調整頻率

setVolumeControlStream(AudioManager.STREAM_MUSIC);音樂回放即媒體音量

LoadedDataAndState載入資料狀態

HorizontalNumberPicker

水平刻度盤類繼承LinearLayout 

mPicker.setTextSize(mDisplayWidth/ TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER);設定字型的大小,螢幕寬度除以20

mPicker.setDensity(outMetrics.densityDpi);設定控制元件密度

mPicker.setOnValueChangedListener設定監聽事件

valueToFrequency(newVal);刻度滑動選中的值,上升下降調整限制值FmConfig配置檔案裡setLowerLimit(int lowLimit)在設定裡選中地區時候的頻率取值範圍

mPrefs.getFrequencyStepSize(),獲得設定的步長大小

mHandler.post(mRadioChangeFrequency);更新在刻度盤上顯示頻率資訊、

tuneRadio(int frequency)調整fm頻率

FMRadioService.java類isFmOn()方法:

registerCallbacks註冊回撥IFMRadioServiceCallbacks的物件、

ServiceStub繼承IFMRadioService.Stub使用到WeakReferenc弱引用,WeakReference是弱於 SoftReference 的引用型別。弱引用的特性和基本與軟引用相似,區別就在於弱引用所指向的物件只要進行系統垃圾回收,不管記憶體使用情況如何,永遠對其進行回收(get() 方法返回 null)。

IBindermBinder = new ServiceStub(this); bindService IBinder等於serviceStub物件。

fmOn()部分程式碼:

/**

*當來電話不是閒置的時候就返回false

*/

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

return bStatus;

}

mReceiver= new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);

FmReceiver接受廣播類繼承FmTransceiver

/ * *
*
建構函式接收方物件,路徑
*
電臺和事件回撥。
* < p >

* @param devicePath調頻裝置路徑字串。
* @param
回撥事件回撥處理從調頻接收機*事件。
*

* /

public FmReceiver(String devicePath,FmRxEvCallbacksAdaptorcallback) throws InstantiationException {

mControl = new FmRxControls();

mRxEvents = new FmRxEventListner();

//registerClient(callback);

mCallback = callback;

}

獲得FM現在的狀態

public int getFMState()

{

/* Current State of FM device */

int currFMState =FmTransceiver.getFMPowerState();

return currFMState;

}

滑動刻度盤動畫效果:

mAnimation= AnimationUtils.loadAnimation(this,R.anim.preset_select);

靜音控制元件監聽,點選控制元件有聲無聲切換。

mMuteButton.setOnClickListener(mMuteModeClickListener);

喇叭與耳機監聽切換

mSpeakerButton.setOnClickListener(mSpeakerClickListener);

開啟關閉fm控制元件切換

mOnOffButton.setOnClickListener(mTurnOnOffClickListener);

向右調頻控制元件監聽

mForwardButton.setOnClickListener(mForwardClickListener);

mForwardButton.setOnLongClickListener(mForwardLongClickListener);

向左調頻控制元件監聽

mBackButton.setOnClickListener(mBackClickListener);

mBackButton.setOnLongClickListener(mBackLongClickListener);

單擊收藏按鈕把收藏頻率定位調頻到刻度盤上,長期按鈕將刻度盤頻率收藏到按鈕上儲存到data分割槽下的fmradio_prefs.xml檔案 key是tation_name0x1

頻率顯示控制元件

mTuneStationFrequencyTV= (TextView)findViewById(R.id.prog_frequency_tv);

長按頻率顯示控制元件監聽方法:

mTuneStationFrequencyTV.setOnLongClickListener(mFrequencyViewClickListener);

錄音檢視顯示與監聽事件

mRecordingMsgTV= (TextView)findViewById(R.id.record_msg_tv);

if (mRecordingMsgTV != null) {

mRecordingMsgTV.setOnClickListener(mRecordButtonListener);

}

自動關掉FM時間顯示

mSleepMsgTV= (TextView)findViewById(R.id.sleep_msg_tv);

fm訊號強度

mRSSI =(ImageView)findViewById(R.id.signal_level);

if (mRSSI != null) {

mRSSI.setVisibility(View.INVISIBLE);

}

顯示控制元件顯示資訊等資料顯示

protectedvoid setDisplayvalue()

onRestart()

獲取 IFMRadioService請求焦點

onStop中方法呼叫private boolean isSleepTimerActive()是否是睡眠。

if(isSleepTimerActive()){

mSleepUpdateHandlerThread.interrupt();

}

如果是睡眠活動就睡眠更新執行緒中斷執行緒

當錄音開始,走onStop()方法時候就停止錄音更新執行緒

private boolean isRecording()

if (null!= mRecordUpdateHandlerThread) {

mRecordUpdateHandlerThread.interrupt();

}

public void onStart()

使用者選擇高清晰多媒體顯示建立命令失敗.

1、如果未儲存則呼叫onCreateDialog(int),然後再呼叫onPrepareDialog(int, Dialog)

(2)如果儲存了對話方塊物件,則直接呼叫onPrepareDialog(int,Dialog),不會再去create,所以有時候當你再輸入框裡無論如何輸入什麼內容,對話方塊的內容都是第一次產生的。

removeDialog(int)是用來清除Activity儲存下來的Dialog物件,如果不加removeDialog將會導致無論在對話方塊裡輸入什麼內容,

彈出來的對話方塊始終都是第一次保留下來的,這裡所以要加上它

建立搜尋dialog

createSearchDlg(id,dlgBuilder)

FMConfig.java

public int getRdsStd () {

return mRdsStd;

}

registerFMSettingListner();註冊收音機設定配置檔案

mPrefs.Load();呼叫FmSharedPreferences的Load()方法獲取fmradio_prefs.xml資料

遇見bug:將設定地區自動選擇印度(外單專案)

/* LoadConfiguration */

if(Locale.getDefault().equals(Locale.CHINA)) {

setCountry(sp.getInt(FMCONFIG_COUNTRY,REGIONAL_BAND_CHINA));

} else {

setCountry(sp.getInt(FMCONFIG_COUNTRY, REGIONAL_BAND_NORTH_AMERICA));

}

/* Last list the user was navigating */

Local.getDafault()在第一次刷機後設置本地語言後

protected void onPause()的時候,將資訊儲存mPrefs.Save();

ScrollerText控制元件繼承Handler

/ * *
*
移動一個字元留下的文字和文章

*SCROLLER_UPDATE_DELAY_MS後延遲下更新訊息。
*
如果滾動整個字串,然後它會顯示整個字串
*
並等待SCROLLER_RESTART_DELAY_MS滾動重啟
* /

voidupdateText()更新錄音時間

停止錄音時間跟新

void stopScroll() {

mStatus = SCROLLER_STOPPED;

removeMessages(SCROLLER_MSG_TICK);

removeMessages(SCROLLER_MSG_RESTART);

removeMessages(SCROLLER_MSG_START);

resetScroll();

}

重新設定錄音時間顯示

private void resetScroll()

啟動錄音時間

void startScroll() {

初始化搜尋

private void initiateSearch(int pty)

resetSearch()重新收索

private void cancelSearch()關閉收索

初始化搜尋列表

private void initiateSearchList()

初始化睡眠定時器

 private void initiateSleepTimer(long seconds) {

mSleepAtPhoneTime =(SystemClock.elapsedRealtime()) + (seconds * 1000);

Log.d(LOGTAG, "Sleep in seconds:" + seconds);

initiateSleepThread();

}

初始化睡眠執行緒

private void initiateSleepThread()

Intent launchPreferencesIntent = new Intent().setClass(this,

Settings.class);

launchPreferencesIntent.putExtra(Settings.RX_MODE,true);

startActivityForResult(launchPreferencesIntent,

ACTIVITY_RESULT_SETTINGS);

private void enableSpeaker()  揚聲器可用

private void updateExpiredRecordTime()更新錄音時間

private Runnable doRecordProcessing = new Runnable()錄音進度

public void onResume()

mService.registerCallbacks(mServiceCallbacks);註冊回撥service

mService.cancelDelayedStop(FMRadioService.STOP_SERVICE);延遲關閉FMRadioService

public  boolean bindToService(Context context,ServiceConnection callback)啟動在FMRadio的onStart()方法判斷條件啟動FMRadioService

if((mService == null ) && (false == bindToService(this, osc)))

onDestroy()方法中解綁nRegisterReceiver(mFmSettingReceiver);

boolean isWiredHeadsetAvailable()判斷耳機是否可用,在FMRadioService類裡新增public void registerHeadsetListener()註冊耳機監聽事件,mHeadsetPlugged = (intent.getIntExtra("state", 0) == 1);狀態監聽

Intent裡的public static final String ACTION_HEADSET_PLUG =

"android.intent.action.HEADSET_PLUG";耳機靜態常量定義。

再啟動FMRadioService監聽 registerHeadsetListener();耳機

/ * *確定是否一個內部天線。
* FMOn
返回快取的值初始化。

*
* @return
真正的如果內部天線可用或連線
*
耳機插入,如果內部天線是錯誤的
*沒有和有線耳機不是插入。
* /

public boolean isAntennaAvailable()FMRadioService

public void readInternalAntennaAvailable()確定是否有內部天線,呼叫類FMReceivce的父類FMTransceiver類傳送接收訊號類

public boolean getInternalAntenna()方法,通過FmReceiverJNI類呼叫getControlNative方法。

恢復設定預設地區設定private voidRestoreDefaults()

當天線可以用的時候就呼叫UI介面可用顯示

private void enableRadioOnOffUI() {

boolean bEnable = isFmOn();

/* Disable if no antenna/headset isavailable */

if (!isAntennaAvailable()) {

bEnable = false;

}

enableRadioOnOffUI(bEnable);

}

呼叫此方法private void enableRadioOnOffUI(boolean bEnable)顯示FMRadio.java UI介面

boolean isCallActive()電話呼叫活動,FMRadioService類isCallActive當狀態不為零表示在通話中

public boolean isCallActive()

{

//Non-zero: Call state is RINGING orOFFHOOK on the available subscriptions

//zero: Call state is IDLE on all theavailable subscriptions

if(0 != getCallState()) return true;

return false;

}

private void enableRadio()可使用收音機

private void disableRadio()不可用收音機

private void resetRadio()重新設定收音機

public void clearStationList()清除電臺列表資訊

public boolean fmConfigure()收音機配置

/ *設定調頻模組自動切換到另一個頻率
*
站如果一個頻率的訊號強度比
*目前調諧頻率。
*
*布林bEnable:真:自動切換到更強的交替頻率。
*假:不要切換到備用頻率。
*

* @return真實如果設定自動對焦模式api呼叫成功,錯誤如果api失敗了。
*注:回撥FmRxEvRadioTuneStatus時將呼叫
*完成不同的頻率。
* /

呼叫FMRadioService類 publicboolean enableAutoAF(boolean bEnable)方法。

public void fmAudioOutputMode()輸出立體聲音

private void startRecording()錄音開始

private void setRecordingStopImage()錄音停止圖片設定

private void setRecordingStartImage()錄音開始圖片設定、

private void startRecordingTimer()錄音啟動的時間

private void stopRecording()停止錄音

private boolean isRecording()判斷是否在錄音

private boolean isSpeakerEnabled()判斷揚聲器是可用

private boolean stationExists(PresetStationstation )長按收藏按鈕式電臺頻率是都存在

private void addToPresets()新增電臺頻率顯示到按鈕上

FmSharedPreferences.addStation(selectedStation.getName(),selectedStation

.getFrequency(),currentList);

setupPresetLayout();

呼叫FmSharedPreferences類的addStation方法新增到mListOfPlists列表裡

private void resetSearchProgress()重置搜尋進度

updateSearchProgress()更新搜尋進度

setupPresetLayout()安裝頻率佈局,收藏頻率至按鈕上初始化

updateStationInfoToUI()更新電臺資訊介面資訊

private boolean isFmOn()收音機是否開啟

/*如果啟用了模擬路徑返回true */

public boolean isAnalogModeEnabled() {

return misAnalogPathEnabled;

}

/ *返回調頻(Soc)音訊硬體是否有限。
*
* @return真如果調頻音訊是柔和的,假如果不低調。
*
* /

public boolean isMuted() {

return mMuted;

}

private boolean isScanActive()掃描活動布林值

private boolean isSeekActive()查詢活動布林值

private boolean isSearchActive()搜尋活動布林值

public PresetStation getCurrentTunedStation()獲得現在調整電臺

private void SeekNextStation()在搜尋查詢下一個頻率

private void initiateSearch(int pty)初始化搜尋

/** SEEK Station with the matching PI */

private void initiatePISearch(int pi)需找匹配的頻率

private void resetSearch()從新搜尋

private void cancelSearch()關閉搜尋

private void initiateSearchList()初始化搜尋列表
private void initiateSleepTimer(long seconds)
初始化睡眠計時器

private void initiateSleepThread()初始化睡眠執行緒

private void endSleepTimer()結束睡眠計時器時間

private boolean hasSleepTimerExpired()睡眠計時器停止

private boolean isSleepTimerActive()睡眠計時器活動是否活動

private void updateExpiredSleepTime()更新停止睡眠時間

private String makeTimeString(long secs)時間格式字串

private void tuneRadio(int frequency)調整收音機頻率

private void resetFMStationInfoUI()從新設定收音機電臺顯示介面資訊

IFMRadioServiceCallbacks類回撥時候

Runnable mRadioEnabled = new Runnable()收音機可用

Runnable mRadioDisabled = new Runnable()收音機不可用

Runnable mRadioReset = new Runnable()收音機重新設定

Runnable mUpdateStationInfo = new Runnable()跟新收音機電臺資訊

Runnable mOnMute = new Runnable()收音機靜音設定

Runnable mOnStereo = new Runnable()立體聲音

Runnable mUpdateRadioText = new Runnable()更新收音機文字資訊

Runnable mRadioChangeFrequency = newRunnable()調整頻率

Runnable mUpdateExtenRadioText = newRunnable()更新延伸收音機文字資訊

Runnable mUpdateProgramService = newRunnable()跟新service進度

private void DebugToasts(String str, intduration)彈出提示資訊

private void registerFMSettingListner() 註冊設定改變監聽

private void unRegisterReceiver(BroadcastReceiver myReceiver)解除安裝註冊避免重複註冊報異常