Android FM 模块学习之四 源码解析(1)
?
前一章我們了解了FM手動調(diào)頻,接下來我們要分析FM模塊用到的源碼。此源碼是基于高通平臺的,別的平臺都大同小異,只不過是平臺自己作了些小改動而已。
首先要看的當然是主activity,
?
FMRadio.java
?fmradio 類啟動FMRadioService.java 類調(diào)用FmSharedPreferences類進行存儲數(shù)據(jù),PresetStation調(diào)整頻率
?setVolumeControlStream(AudioManager.STREAM_MUSIC); 音樂回放即媒體音量
?LoadedDataAndState 加載數(shù)據(jù)狀態(tài)
?HorizontalNumberPicker 水平刻度盤類繼承LinearLayout?
?mPicker.setTextSize(mDisplayWidth/ TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER); 設置字體的大小,屏幕寬度除以20
?mPicker.setDensity(outMetrics.densityDpi); 設置控件密度
?mPicker.setOnValueChangedListener 設置監(jiān)聽事件
?valueToFrequency(newVal); 刻度滑動選中的值,上升下降調(diào)整限制值FmConfig 配置文件里setLowerLimit(int lowLimit)在設置里選中地區(qū)時候的頻率取值范圍
?mPrefs.getFrequencyStepSize() ,獲得設置的步長大小
?mHandler.post(mRadioChangeFrequency); 更新在刻度盤上顯示頻率信息、
?tuneRadio(int frequency) 調(diào)整fm 頻率
?
?FMRadioService.java 類isFmOn() 方法:
?registerCallbacks 注冊回調(diào)IFMRadioServiceCallbacks 的對象、
?ServiceStub 繼承IFMRadioService.Stub 使用到WeakReferenc弱引用, WeakReference 是弱于 SoftReference 的引用類型。弱引用的特性和基本與軟引用相似,區(qū)別就在于弱引用所指向的對象只要進行系統(tǒng)垃圾回收,不管內(nèi)存使用情況如何,永遠對其進行回收(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
?/ * * * 構(gòu)造函數(shù)接收方對象,
?路徑
電臺和事件回調(diào)。
?
* @param devicePath 調(diào)頻設備路徑字符串。
回調(diào)事件回調(diào)處理
從調(diào)頻接收機* 事件。
?* /
?public FmReceiver(String devicePath, FmRxEvCallbacksAdaptorcallback) throws InstantiationException {
? mControl = new FmRxControls();
? mRxEvents = new FmRxEventListner();
//registerClient(callback);
? mCallback = callback;
?}
?
獲得FM 現(xiàn)在的狀態(tài)
?public int getFMState()
?{
?/* Current State of FM device */
? int currFMState =FmTransceiver.getFMPowerState();
? return currFMState;
?}
?
滑動刻度盤動畫效果:
?mAnimation= AnimationUtils.loadAnimation(this,R.anim.preset_select);
?靜音控件監(jiān)聽,點擊控件有聲無聲切換。
?mMuteButton.setOnClickListener(mMuteModeClickListener);
?喇叭與耳機監(jiān)聽切換
?mSpeakerButton.setOnClickListener(mSpeakerClickListener);
?開啟關(guān)閉fm 控件切換
?mOnOffButton.setOnClickListener(mTurnOnOffClickListener);
?向右調(diào)頻控件監(jiān)聽
?mForwardButton.setOnClickListener(mForwardClickListener);
?mForwardButton.setOnLongClickListener(mForwardLongClickListener);
?向左調(diào)頻控件監(jiān)聽
?mBackButton.setOnClickListener(mBackClickListener);
?mBackButton.setOnLongClickListener(mBackLongClickListener);
?單擊收藏按鈕把收藏頻率定位調(diào)頻到刻度盤上,長期按鈕將刻度盤頻率收藏到按鈕上存儲到data 分區(qū)下的fmradio_prefs.xml文件 key是tation_name0x1
?頻率顯示控件
?mTuneStationFrequencyTV= (TextView)findViewById(R.id.prog_frequency_tv);
?長按頻率顯示控件監(jiān)聽方法:
?mTuneStationFrequencyTV.setOnLongClickListener(mFrequencyViewClickListener);
?錄音視圖顯示與監(jiān)聽事件
?mRecordingMsgTV= (TextView)findViewById(R.id.record_msg_tv);
?if (mRecordingMsgTV != null) {
? mRecordingMsgTV.setOnClickListener(mRecordButtonListener);
?}
?
自動關(guān)掉FM 時間顯示
?mSleepMsgTV= (TextView)findViewById(R.id.sleep_msg_tv);
?fm 信號強度
?mRSSI =(ImageView)findViewById(R.id.signal_level);
?if (mRSSI != null) {
? mRSSI.setVisibility(View.INVISIBLE);
?}
?
顯示控件顯示信息等數(shù)據(jù)顯示
?protectedvoid setDisplayvalue()
?onRestart()
?獲取 IFMRadioService 請求焦點
?onStop 中方法調(diào)用private boolean isSleepTimerActive() 是否是睡眠。
?if(isSleepTimerActive()){
? mSleepUpdateHandlerThread.interrupt();
?}
?
如果是睡眠活動就睡眠更新線程中斷線程
當錄音開始,走onStop() 方法時候就停止錄音更新線程
?private boolean isRecording()
?if (null!= mRecordUpdateHandlerThread) {
? mRecordUpdateHandlerThread.interrupt();
?}
?
public void onStart()
?用戶選擇高清晰多媒體顯示創(chuàng)建命令失敗.
?1 、如果未保存則調(diào)用onCreateDialog(int) ,然后再調(diào)用onPrepareDialog(int, Dialog)
?(2) 如果保存了對話框?qū)ο?#xff0c;則直接調(diào)用onPrepareDialog(int,Dialog), 不會再去create,所以有時候當你再輸入框里無論如何輸入什么內(nèi)容,對話框的內(nèi)容都是第一次產(chǎn)生的。
?removeDialog(int) 是用來清除Activity 保存下來的Dialog對象,如果不加removeDialog將會導致無論在對話框里輸入什么內(nèi)容,
?彈出來的對話框始終都是第一次保留下來的,這里所以要加上它
?創(chuàng)建搜索dialog
?createSearchDlg(id,dlgBuilder)
?
?FMConfig.java 類
?public int getRdsStd () {
? return mRdsStd;
?}
?registerFMSettingListner(); 注冊收音機設置配置文件
?mPrefs.Load(); 調(diào)用FmSharedPreferences 的Load()方法獲取fmradio_prefs.xml數(shù)據(jù)
?遇見bug: 將設置地區(qū)自動選擇印度(外單項目)
?/* 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() 關(guān)閉收索
?初始化搜索列表
?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); 注冊回調(diào)service
?mService.cancelDelayedStop(FMRadioService.STOP_SERVICE); 延遲關(guān)閉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()注冊耳機監(jiān)聽事件,mHeadsetPlugged = (intent.getIntExtra("state", 0) == 1);狀態(tài)監(jiān)聽
?Intent 里的public static final String ACTION_HEADSET_PLUG =
?"android.intent.action.HEADSET_PLUG"; 耳機靜態(tài)常量定義。
?
再啟動FMRadioService 監(jiān)聽 registerHeadsetListener();耳機
?/ * *
?確定是否一個內(nèi)部天線。
?* FMOn
?返回緩存的值初始化。
?*
?* @return
?真正的如果內(nèi)部天線可用或連線
?*
?耳機插入,如果內(nèi)部天線是錯誤的
?*沒有和有線耳機不是插入。
?* /
?
public boolean isAntennaAvailable()FMRadioService 類
?public void readInternalAntennaAvailable() 確定是否有內(nèi)部天線,調(diào)用類FMReceivce 的父類FMTransceiver類發(fā)送接收信號類
?public boolean getInternalAntenna() 方法,通過FmReceiverJNI 類調(diào)用getControlNative方法。
?恢復設置默認地區(qū)設置private voidRestoreDefaults()
?當天線可以用的時候就調(diào)用UI 界面可用顯示
?private void enableRadioOnOffUI() {
? boolean bEnable = isFmOn();
? /* Disable if no antenna/headset isavailable */
? if (!isAntennaAvailable()) {
? bEnable = false;
? }
? enableRadioOnOffUI(bEnable);
?? }
?
調(diào)用此方法private void enableRadioOnOffUI(boolean bEnable) 顯示FMRadio.java UI界面
?boolean isCallActive() 電話呼叫活動,FMRadioService 類isCallActive當狀態(tài)不為零表示在通話中
?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() 收音機配置
?/ *
?設置調(diào)頻模塊自動切換到另一個頻率
?*
?站如果一個頻率的信號強度比
?*目前調(diào)諧頻率。
?*
?*布爾bEnable:真:自動切換到更強的交替頻率。
?*假:不要切換到備用頻率。
?* @return真實如果設置自動對焦模式api調(diào)用成功,錯誤如果api失敗了。
?*注:回調(diào)FmRxEvRadioTuneStatus時將調(diào)用
?*完成不同的頻率。
?* /
?調(diào)用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();
?
調(diào)用FmSharedPreferences 類的addStation方法添加到mListOfPlists列表里
?private void resetSearchProgress() 重置搜索進度
?updateSearchProgress() 更新搜索進度
?setupPresetLayout() 安裝頻率布局,收藏頻率至按鈕上初始化
?updateStationInfoToUI() 更新電臺信息界面信息
?private boolean isFmOn() 收音機是否開啟
?
/* 如果啟用了模擬路徑返回true */
?public boolean isAnalogModeEnabled() {
? return misAnalogPathEnabled;
?}
?
/ * 返回調(diào)頻(Soc) 音頻硬件是否有限。
?* @return真如果調(diào)頻音頻是柔和的,假如果不低調(diào)。
?* /
?public boolean isMuted() {
? return mMuted;
?}
?private boolean isScanActive() 掃描活動布爾值
?private boolean isSeekActive() 查找活動布爾值
?private boolean isSearchActive() 搜索活動布爾值
?public PresetStation getCurrentTunedStation() 獲得現(xiàn)在調(diào)整電臺
?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() 關(guān)閉搜索
?private void initiateSearchList() 初始化搜索列表
?private void initiateSleepTimer(long seconds)? 初始化睡眠計時器
?private void initiateSleepThread() 初始化睡眠線程
?private void endSleepTimer() 結(jié)束睡眠計時器時間
?private boolean hasSleepTimerExpired() 睡眠計時器停止
?private boolean isSleepTimerActive() 睡眠計時器活動是否活動
?private void updateExpiredSleepTime() 更新停止睡眠時間
?private String makeTimeString(long secs) 時間格式字符串
?private void tuneRadio(int frequency) 調(diào)整收音機頻率
?private void resetFMStationInfoUI() 從新設置收音機電臺顯示界面信息
?
IFMRadioServiceCallbacks 類回調(diào)時候
?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() 調(diào)整頻率
?Runnable mUpdateExtenRadioText = newRunnable() 更新延伸收音機文本信息
?Runnable mUpdateProgramService = newRunnable() 跟新service 進度
?private void DebugToasts(String str, intduration) 彈出提示信息
?private void registerFMSettingListner()? 注冊設置改變監(jiān)聽
?private void unRegisterReceiver(BroadcastReceiver myReceiver) 卸載注冊避免重復注冊報異常
?
?
下面分享的是FM模塊的保存方法,即FmSharedPreferences.java
?
??? FmSharedPreferences(Context context)在構(gòu)造方法中加載Load()方法,
??? public void? Load(){
????????? Log.d(LOGTAG, "Load preferences ");
????????? if(mContext == null)
????????? {
???????????? return;
????????? }
????????? SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE);
????????? mTunedFrequency = sp.getInt(PREF_LAST_TUNED_FREQUENCY, DEFAULT_NO_FREQUENCY);
????????? mRecordDuration = sp.getInt(LAST_RECORD_DURATION, RECORD_DUR_INDEX_0_VAL);
????????? mAFAutoSwitch = sp.getBoolean(LAST_AF_JUMP_VALUE, true);
????????? mAudioOutputMode = sp.getBoolean(AUDIO_OUTPUT_MODE, true);
????????? if(sp.getInt(FMCONFIG_COUNTRY, 0) == REGIONAL_BAND_USER_DEFINED) {
???????????? mBandMinFreq = sp.getInt(FMCONFIG_MIN, mBandMinFreq);
???????????? mBandMaxFreq = sp.getInt(FMCONFIG_MAX, mBandMaxFreq);
???????????? mChanSpacing = sp.getInt(FMCONFIG_STEP, mChanSpacing);
????????? }
??? ?
????????? int num_lists = sp.getInt(LIST_NUM, 1);
????????? if (mListOfPlists.size() == 0) {
????????? for (int listIter = 0; listIter < num_lists; listIter++) {
???????????????? String listName = sp.getString(LIST_NAME + listIter, "FM - " + (listIter+1));
???????????????? int numStations = sp.getInt(STATION_NUM + listIter, 1);
???????????????? if (listIter == 0) {
???????????????????? createFirstPresetList(listName);
???????????????? } else {
???????????????????? createPresetList(listName);
???????????????? }
???????????????? PresetList curList = mListOfPlists.get(listIter);
???????????????? for (int stationIter = 0; stationIter < numStations; stationIter++) {
????????????????????? String stationName = sp.getString(STATION_NAME + listIter + "x" + stationIter,
????????????????????????????????????????????????????????? DEFAULT_NO_NAME);
????????????????????? int stationFreq = sp.getInt(STATION_FREQUENCY + listIter + "x" + stationIter,
?????????????????????????????????????????????????????? DEFAULT_NO_FREQUENCY);
????????????????????? PresetStation station = curList.addStation(stationName, stationFreq);
??? ?
????????????????????? int stationId = sp.getInt(STATION_ID + listIter + "x" + stationIter,
????????????????????????????????????????????????? DEFAULT_NO_STATIONID);
????????????????????? station.setPI(stationId);
??? ?
????????????????????? int pty = sp.getInt(STATION_PTY + listIter + "x" + stationIter, DEFAULT_NO_PTY);
????????????????????? station.setPty(pty);
??? ?
????????????????????? int rdsSupported = sp.getInt(STATION_RDS + listIter + "x" + stationIter,
???????????????????????????????????????????????????? DEFAULT_NO_RDSSUP);
????????????????????? if (rdsSupported != 0) {
????????????????????????? station.setRDSSupported(true);
????????????????????? } else {
????????????????????????? station.setRDSSupported(false);
????????????????????? }
????????? /* Load Configuration */?? ?
??????????? setCountry(sp.getInt(FMCONFIG_COUNTRY, REGIONAL_BAND_NORTH_AMERICA));
????????? /* Last list the user was navigating */
????????? mListIndex = sp.getInt(LAST_LIST_INDEX, 0);
????????? if(mListIndex >= num_lists)
????????? {mListIndex=0;
????????? }
?????? }
??? 初始化取出sp一些數(shù)據(jù)
??? 根據(jù)系統(tǒng)初始化地區(qū)設置當?shù)仡l率 ?
??? /*Load Configuration */
???????? 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 */
??????????? 保存頻率
??? public void Save()
???????????? 設置默認的地區(qū)
??? public static void SetDefaults()
??????????? 設置調(diào)整頻率
??? public static void setTunedFrequency(intfrequency)
??????????? 獲取調(diào)整頻率
??? public static int getTunedFrequency()
??????????? 獲取下一個頻率
??? public static int getNextTuneFrequency(intfrequency)
??????????? 獲取上一個頻率
??? public static int getPrevTuneFrequency(intfrequency)
??? / * *
??? * @param mFMConfiguration mFMConfiguration設置
??? * /
??? public static void setFMConfiguration(FmConfig mFMConfig)
??? /**
??????? *@return the mFMConfiguration
?????? */
????? public static FmConfig getFMConfiguration() {
???????? return mFMConfiguration;
?????? }
??? 設置fm播放的頻率范圍
??? public static void setRadioBand(int band)
?????????? 獲取fm頻率范圍限制
??? public static int getRadioBand()
?? ? 獲取間隔
??? public static int getChSpacing()
??? 設置遠程數(shù)據(jù)服務
??? public static void setRdsStd(int std)
??? mFMConfiguration.setRdsStd(std);
?? ? 獲取遠程數(shù)據(jù)服務
??? public static int getRdsStd()
??? mFMConfiguration.getRdsStd();
??? 設置國家地區(qū)
??? public static void setCountry(intnCountryCode)
??? 獲取國家地區(qū)
??? public static int getCountry()
??????????? 設置聲音輸出模板
??? setAudioOutputMode
??? 設置錄音是否持續(xù)
??? public static void setRecordDuration(intdurationIndex)
?
?
總結(jié):個人認為以上只能作為參考,但是大體框架變化不大。
?
轉(zhuǎn)載于:https://www.cnblogs.com/kings-boke/p/4269825.html
總結(jié)
以上是生活随笔為你收集整理的Android FM 模块学习之四 源码解析(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tomcat-tunning
- 下一篇: Ubuntu18.04 安装WineHQ