上一篇大概分析了一下FM启动流程,若不了解Fm启动流程的,能够去打开前面的链接先了解FM启动流程,接下来我们简单分析一下FM的搜索频率流程。

在了解源代码之前。我们先看一下流程图:

事实上从图中能够看到,实现搜索频率的功能是在底层CPP文件。java层仅仅操作和更新一些界面(GUI),Java调用JNI实现功能。Java app基本核心,通过方法回调实现a类和b类方法。b类调a类方法信息交互相互控制融为一体。App实现一些JNI接口终于实现核心功能是cpp文件,最后通过Service类(耗时操作)调用New一个线程循环不断的获取cpp里的信息,去更新UI界面活动状态。

搜索流程简单分析:点击搜索button,通过互调方法,最后调到FMReceiverJNI类中的方法实现功能。通过FMRxEventListner类不断获取cpp变频的频率。每获取一次频率(直到频率搜索完毕停止调用)就回调FMRadioService内部FmRxEvCallbacksAdaptor的方法在回调到FMRadio类中方法,将频率存入FmSharedPreferences类xml文档中。发送Handler更新UI。即刻度盘,对话框,左右箭头中间显示的频率一致跳动。

接下来具体代码分析:

FMRadio中的菜单搜索功能,onOptionsItemSelected(MenuItem item)监听中走initiateSearch(mScanPtyIndex);方法。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGZzbG92ZXhpemk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

调用FMRadioService的scan()方法(mService.scan(pty))进行扫描频率

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGZzbG92ZXhpemk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

updateSearchProgress()里加了同步方法对象锁

调用了private Dialog createProgressDialog(int id)对话框进行搜索信息

标准耳机FmSharedPreferences.isRBDSStd()

private Dialog <strong>createProgressDialog</strong>(int id) {
String msgStr = "";
String titleStr = "";
String []items;
double frequency = mTunedStation.getFrequency() / 1000.0;
boolean bSearchActive = false; if (isSeekActive()) {
msgStr = getString(R.string.msg_seeking);
bSearchActive = true;
}else if (isScanActive()) {
if(FmSharedPreferences.isRBDSStd()) {<span style="font-family:KaiTi_GB2312;">//标准耳机</span>
items = getResources().
getStringArray(R.array.search_category_rbds_entries);
}else { // if(FmSharedPreferences.isRDSStd())
items = getResources().
getStringArray(R.array.search_category_rds_entries);
}String ptyStr = "";
if (items.length > mScanPtyIndex)
ptyStr = items[mScanPtyIndex];
if (!TextUtils.isEmpty(ptyStr)) {
msgStr = getString(R.string.msg_scanning_pty, ptyStr);
}else {
Log.d(LOGTAG, "pty is null\n");
msgStr = getString(R.string.msg_scanning);
}
titleStr = getString(R.string.msg_search_title, ("" + frequency));
bSearchActive=true;
}else if (isSearchActive()) {
msgStr = getString(R.string.msg_searching);
titleStr = getString(R.string.msg_searching_title);
bSearchActive = true;
}
if (bSearchActive) {mProgressDialog = new ProgressDialog(FMRadio.this);
if (mProgressDialog != null) {
mProgressDialog.setTitle(titleStr);
mProgressDialog.setMessage(msgStr);
mProgressDialog.setIcon(R.drawable.ic_launcher_fmradio);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.setButton(DialogInterface.BUTTON_POSITIVE,
getText(R.string.button_text_stop),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
<strong>cancelSearch();</strong>
}
});
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) {
cancelSearch();
}
});
mProgressDialog.setOnKeyListener(new OnKeyListener() {
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
Log.d(LOGTAG, "OnKeyListener event received in ProgressDialog" + keyCode);
switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case 126: //KeyEvent.KEYCODE_MEDIA_PLAY:
case 127: //KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_STOP:
return true;
} return false;
}
});
}
Message msg = new Message();
msg.what = TIMEOUT_PROGRESS_DLG;
mSearchProgressHandler.sendMessageDelayed(msg, SHOWBUSY_TIMEOUT);
}
return mProgressDialog;
}

调用FMRadioService类中的Scan()方法扫描

调用 FMReceiver的searchStations()方法进行扫描

public boolean<strong> scan(int pty)</strong>
{
boolean bCommandSent=false;
if (mReceiver != null)
{
Log.d(LOGTAG, "scan: PTY: " + pty);
if(FmSharedPreferences.isRBDSStd())
{
/* RBDS : Validate PTY value?? */
if( ((pty > 0) && (pty <= 23)) || ((pty >= 29) && (pty <= 31)) )
{bCommandSent = <strong>mReceiver.searchStations</strong>(FmReceiver.FM_RX_SRCHRDS_MODE_SCAN_PTY,
FmReceiver.FM_RX_DWELL_PERIOD_2S,
FmReceiver.FM_RX_SEARCHDIR_UP,
pty,
0);
}
else
{
bCommandSent = <strong>mReceiver.searchStations</strong>(FmReceiver.FM_RX_SRCH_MODE_SCAN,
FmReceiver.FM_RX_DWELL_PERIOD_2S,
FmReceiver.FM_RX_SEARCHDIR_UP);
}}
else
{
/* RDS : Validate PTY value?? */
if( (pty > 0) && (pty <= 31) )
{
bCommandSent = <strong>mReceiver.searchStations</strong>(FmReceiver.FM_RX_SRCHRDS_MODE_SCAN_PTY,
FmReceiver.FM_RX_DWELL_PERIOD_2S,
FmReceiver.FM_RX_SEARCHDIR_UP,
pty,
0);
}
else{
bCommandSent =<strong> mReceiver.searchStations</strong>(FmReceiver.FM_RX_SRCH_MODE_SCAN,
FmReceiver.FM_RX_DWELL_PERIOD_2S,
FmReceiver.FM_RX_SEARCHDIR_UP);
}
}
}
return bCommandSent;
}

FmReceiver类的public boolean searchStations (int mode,int dwellPeriod,intdirection,int pty,Int pi)  方法

获得FMState状态

int state = getFMState();

/ * 验证參数* /

调用setSearchState(subSrchLevel_ScanInProg);

re = mControl.searchStations(sFd, mode,dwellPeriod, direction, pty, pi);

public boolean <strong>searchStations </strong>(int mode,
int dwellPeriod,
int direction){ <strong> int state = getFMState();</strong>//<span style="font-family:KaiTi_GB2312;font-size:18px;">获得FMState状态</span>
boolean bStatus = true;
int re; /* Check current state of FM device */
if (state == FMState_Turned_Off || state == FMState_Srch_InProg) {
Log.d(TAG, "searchStations: Device currently busy in executing another command.");
return false;
} Log.d (TAG, "Basic search...");
/* Validate the arguments */
if ( (mode != FM_RX_SRCH_MODE_SEEK) &&
(mode != FM_RX_SRCH_MODE_SCAN))
{
Log.d (TAG, "Invalid search mode: " + mode );
bStatus = false;
}
if ( (dwellPeriod < FM_RX_DWELL_PERIOD_0S ) ||
(dwellPeriod > FM_RX_DWELL_PERIOD_7S))
{
Log.d (TAG, "Invalid dwelling time: " + dwellPeriod);
bStatus = false;
}
if ( (direction != FM_RX_SEARCHDIR_DOWN) &&
(direction != FM_RX_SEARCHDIR_UP))
{
Log.d (TAG, "Invalid search direction: " + direction);
bStatus = false;
}
if (bStatus)
{
Log.d (TAG, "searchStations: mode " + mode + "direction: " + direction); if (mode == FM_RX_SRCH_MODE_SEEK)
setSearchState(subSrchLevel_SeekInPrg);
else if (mode == FM_RX_SRCH_MODE_SCAN)
setSearchState(subSrchLevel_ScanInProg);
Log.v(TAG, "searchStations: CURRENT-STATE : FMRxOn ---> NEW-STATE : SearchInProg"); <strong> re = mControl.searchStations(sFd, mode, dwellPeriod, direction, 0, 0);</strong>
if (re != 0) {
Log.e(TAG, "search station failed");
if (getFMState() == FMState_Srch_InProg)
setSearchState(subSrchLevel_SrchComplete);
return false;
} state = getFMState();
if (state == FMState_Turned_Off) {
Log.d(TAG, "searchStations: CURRENT-STATE : FMState_Off (unexpected)");
return false;
}
}
return bStatus;
}

设置FM搜索电源状态

static void <strong>setSearchState</strong>(int state)
{
mSearchState = state;
switch(mSearchState) {
case subSrchLevel_SeekInPrg:
case subSrchLevel_ScanInProg:
case subSrchLevel_SrchListInProg:
setFMPowerState(FMState_Srch_InProg);
break;
case subSrchLevel_SrchComplete:
/* Update the state of the FM device */
mSearchState = subSrchLevel_NoSearch;
setFMPowerState(FMState_Rx_Turned_On);
break;
case subSrchLevel_SrchAbort:
break;
default:
mSearchState = subSrchLevel_NoSearch;
break;
}
}

setFMPowerState(FMState_Rx_Turned_On); 是调用FmTransceiver类发射器类,FM电源状态

/*==============================================================
FUNCTION: setFMPowerState
==============================================================*/
/**
* Sets the FM power state
*
* <p>
* This method sets the FM power state.
*
* <p>
*/
static void <strong>setFMPowerState(</strong>int state)
{
FMState = state;
}

调用FMRxControls.java类的

/ * 配置各种搜索參数,開始搜索* /

public int searchStations (int fd, int mode,int dwell, int dir, int pty, int pi)

设置一些參数

FmReceiverJNI.setControlNative();

设置的搜索模式

设置扫描居住的时间

设置的企业

设置PI

/* configure various search parameters and start search */
public int <strong>searchStations </strong>(int fd, int mode, int dwell,
int dir, int pty, int pi){
int re = 0; Log.d(TAG, "Mode is " + mode + " Dwell is " + dwell);
Log.d(TAG, "dir is " + dir + " PTY is " + pty);
Log.d(TAG, "pi is " + pi + " id " + V4L2_CID_PRIVATE_TAVARUA_SRCHMODE); <strong> re = FmReceiverJNI.setControlNative (fd, V4L2_CID_PRIVATE_TAVARUA_SRCHMODE, mode);</strong>
if (re != 0) {
Log.e(TAG, "setting of search mode failed");
return re;
}
<strong>re = FmReceiverJNI.setControlNative (fd, V4L2_CID_PRIVATE_TAVARUA_SCANDWELL, dwell);</strong>
if (re != 0) {
Log.e(TAG, "setting of scan dwell time failed");
return re;
}
if (pty != 0)
{ re = FmReceiverJNI.setControlNative (fd, V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY, pty);
if (re != 0) {
Log.e(TAG, "setting of PTY failed");
return re;
}
} if (pi != 0)
{
<strong> re = FmReceiverJNI.setControlNative (fd, V4L2_CID_PRIVATE_TAVARUA_SRCH_PI, pi);</strong>
if (re != 0) {
Log.e(TAG, "setting of PI failed");
return re;
}
} <strong>re = FmReceiverJNI.startSearchNative (fd, dir );</strong>
return re;
}

启动搜索 FmReceiverJNI.startSearchNative (fd, dir );

关闭搜索

FMRadio 调用 FMRadioService 的CancelSearch()方法

public boolean cancelSearch()

public boolean <strong>cancelSearch()</strong>
{
boolean bCommandSent=false;
if (mReceiver != null)
{
Log.d(LOGTAG, "cancelSearch");
bCommandSent = <strong>mReceiver.cancelSearch();</strong>
}
return bCommandSent;
}

调用FRReceiver的cancelSearch()

mReceiver.cancelSearch()

更新搜索 FMRadio.java中

updateSearchProgress();

private void <strong>updateSearchProgress()</strong> {
boolean searchActive = isScanActive() || isSeekActive() || isSearchActive();
if (searchActive) {
synchronized (this) {
if(mProgressDialog == null) {
showDialog(DIALOG_PROGRESS_PROGRESS);
}else {
Message msg = new Message();
msg.what = UPDATE_PROGRESS_DLG;
mSearchProgressHandler.sendMessage(msg);
}
}
}else {
Message msg = new Message();
msg.what = END_PROGRESS_DLG;
mSearchProgressHandler.sendMessage(msg);
}
}

初始化菜单  invalidateOptionsMenu();

调用FMRxControls类的public void cancelSearch (int fd)方法

最后调用FMReceiver类的cancelSearchNative()

/* cancel search in progress */
public void cancelSearch (int fd){
<strong> FmReceiverJNI.cancelSearchNative(fd);</strong>
}

最后发送一个mSearchProgressHandler

msg.what = END_PROGRESS_DLG;

mSearchProgressHandler.sendMessage(msg)

删除handler发送消息关闭对话框

private Handler mSearchProgressHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == UPDATE_PROGRESS_DLG) {
if(mProgressDialog != null) {
double frequency = mTunedStation.getFrequency() / 1000.0;
String titleStr = getString(R.string.msg_search_title, ("" + frequency));
mProgressDialog.setTitle(titleStr);
}
}else if (msg.what == END_PROGRESS_DLG) {
<strong>mSearchProgressHandler.removeMessages(END_PROGRESS_DLG);
mSearchProgressHandler.removeMessages(UPDATE_PROGRESS_DLG);
mSearchProgressHandler.removeMessages(TIMEOUT_PROGRESS_DLG);
removeDialog(DIALOG_PROGRESS_PROGRESS);
mProgressDialog = null;</strong>
}else if (msg.what == TIMEOUT_PROGRESS_DLG) {
cancelSearch();
}
}
};

在搜索中更新FMRadioUI界面的监听类FmRxEventListner.java

 public void startListner (final int fd, final FmRxEvCallbacks cb) {
/* start a thread and listen for messages */
mThread = new Thread(){
public void run(){
byte [] buff = new byte[STD_BUF_SIZE];
Log.d(TAG, "Starting listener " + fd); while ((!Thread.currentThread().isInterrupted())) { try {
int index = 0;
int state = 0;
Arrays.fill(buff, (byte)0x00);
int freq = 0;
int eventCount = <strong>FmReceiverJNI.getBufferNative (fd, buff, EVENT_LISTEN);</strong> if (eventCount >= 0)
Log.d(TAG, "Received event. Count: " + eventCount); for ( index = 0; index < eventCount; index++ ) {
Log.d(TAG, "Received <" +buff[index]+ ">" ); switch(buff[index]){
case 0:Log.d(TAG, "Got READY_EVENT");
if(FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMRx_Starting) {
/*Set the state as FMRxOn */
FmTransceiver.setFMPowerState(FmTransceiver.FMState_Rx_Turned_On);
Log.v(TAG, "RxEvtList: CURRENT-STATE : FMRxStarting ---> NEW-STATE : FMRxOn");
cb.FmRxEvEnableReceiver();
}
else if (FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMTurning_Off) {
/*Set the state as FMOff */
FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off);
Log.v(TAG, "RxEvtList: CURRENT-STATE : FMTurningOff ---> NEW-STATE : FMOff");
FmTransceiver.release("/dev/radio0");
cb.FmRxEvDisableReceiver();
Thread.currentThread().interrupt();
}
break;case 1:
Log.d(TAG, "Got TUNE_EVENT");
<strong>freq = FmReceiverJNI.getFreqNative(fd);</strong>
state = FmReceiver.getSearchState();
switch(state) {
case FmTransceiver.subSrchLevel_SeekInPrg :
Log.v(TAG, "Current state is " + state);
FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
<strong> cb.FmRxEvSearchComplete(freq);</strong>
break;
default:
if (freq > 0)
cb.FmRxEvRadioTuneStatus(freq);
else
Log.e(TAG, "get frequency command failed");
break;
}
break;
case 2:Log.d(TAG, "Got SEEK_COMPLETE_EVENT");
state = FmReceiver.getSearchState();
switch(state) {
case FmTransceiver.subSrchLevel_ScanInProg:
Log.v(TAG, "Current state is " + state);
FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
cb.FmRxEvSearchComplete(FmReceiverJNI.getFreqNative(fd));
break;
case FmTransceiver.subSrchLevel_SrchAbort:
Log.v(TAG, "Current state is SRCH_ABORTED");
Log.v(TAG, "Aborting on-going search command...");
FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
cb.FmRxEvSearchComplete(FmReceiverJNI.getFreqNative(fd));
break;
}
break;
case 3:Log.d(TAG, "Got SCAN_NEXT_EVENT");
cb.FmRxEvSearchInProgress();
break;
case 4:
Log.d(TAG, "Got RAW_RDS_EVENT");
cb.FmRxEvRdsGroupData();
break;
case 5:
Log.d(TAG, "Got RT_EVENT");
cb.FmRxEvRdsRtInfo();
break;
case 6:
Log.d(TAG, "Got PS_EVENT");
cb.FmRxEvRdsPsInfo();
break;
case 7:
Log.d(TAG, "Got ERROR_EVENT");
break;
case 8:
Log.d(TAG, "Got BELOW_TH_EVENT");
cb.FmRxEvServiceAvailable (false);
break;
case 9:Log.d(TAG, "Got ABOVE_TH_EVENT");
cb.FmRxEvServiceAvailable(true);
break;
case 10:
Log.d(TAG, "Got STEREO_EVENT");
cb.FmRxEvStereoStatus (true);
break;
case 11:
Log.d(TAG, "Got MONO_EVENT");
cb.FmRxEvStereoStatus (false);
break;
case 12:
Log.d(TAG, "Got RDS_AVAL_EVENT");
cb.FmRxEvRdsLockStatus (true);
break;
case 13:
Log.d(TAG, "Got RDS_NOT_AVAL_EVENT");
cb.FmRxEvRdsLockStatus (false);
break;
case 14:Log.d(TAG, "Got NEW_SRCH_LIST");
state = FmReceiver.getSearchState();
switch(state) {
case FmTransceiver.subSrchLevel_SrchListInProg:
Log.v(TAG, "FmRxEventListener: Current state is AUTO_PRESET_INPROGRESS");
FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
cb.FmRxEvSearchListComplete ();
break;
case FmTransceiver.subSrchLevel_SrchAbort:
Log.v(TAG, "Current state is SRCH_ABORTED");
Log.v(TAG, "Aborting on-going SearchList command...");
FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
cb.FmRxEvSearchCancelled();
break;
}
break;
case 15:Log.d(TAG, "Got NEW_AF_LIST");
cb.FmRxEvRdsAfInfo();
break;
case 18:
Log.d(TAG, "Got RADIO_DISABLED");
if (FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMTurning_Off) {
/*Set the state as FMOff */
FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off);
Log.v(TAG, "RxEvtList: CURRENT-STATE : FMTurningOff ---> NEW-STATE : FMOff");
FmTransceiver.release("/dev/radio0");
cb.FmRxEvDisableReceiver();
Thread.currentThread().interrupt();
} else {
Log.d(TAG, "Unexpected RADIO_DISABLED recvd");
cb.FmRxEvRadioReset();
}
break;
case 19:FmTransceiver.setRDSGrpMask(0);
break;
case 20:
Log.d(TAG, "got RT plus event");
cb.FmRxEvRTPlus();
break;
case 21:
Log.d(TAG, "got eRT event");
cb.FmRxEvERTInfo();
break;
default:
Log.d(TAG, "Unknown event");
break;
}
}//end of for
} catch ( Exception ex ) {
Log.d( TAG, "RunningThread InterruptedException");
ex.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
};
mThread.start();
}

Switch case取1的时候就FMReceiverJNI类中获取频率。调FmRxEvRadioTuneStatus接收读取频率

freq= FmReceiverJNI.getFreqNative(fd);

cb.FmRxEvRadioTuneStatus(freq);

将频率保存起来

FmSharedPreferences.setTunedFrequency(frequency);

mPrefs.Save();

清除状态信息 clearStationInfo();

调用改变界面状态 mCallbacks.onTuneStatusChanged();

可用存储,设置可用模拟器 enableStereo(FmSharedPreferences.getAudioOutputMode());

public void <strong>FmRxEvRadioTuneStatus</strong>(int frequency)
{
Log.d(LOGTAG, "FmRxEvRadioTuneStatus: Tuned Frequency: " +frequency);
try
{
<strong>FmSharedPreferences.setTunedFrequency(frequency);
mPrefs.Save();</strong>
//Log.d(LOGTAG, "Call mCallbacks.onTuneStatusChanged");
/* Since the Tuned Status changed, clear out the RDSData cached */
if(mReceiver != null) {
<strong> clearStationInfo();</strong>
}
if(mCallbacks != null)
{
<strong> mCallbacks.onTuneStatusChanged();</strong>
}
/* Update the frequency in the StatusBar's Notification */
startNotification();
enableStereo(FmSharedPreferences.getAudioOutputMode());
}
catch (RemoteException e)
{
e.printStackTrace();
}
}

最后调究竟层

FmReceiverJNI.setMonoStereoNative (fd, 1)

/* force mono/stereo mode */
public int stereoControl(int fd, boolean stereo) { if (stereo){
return FmReceiverJNI.setMonoStereoNative (fd, 1);
}
else {
return FmReceiverJNI.setMonoStereoNative (fd, 0);
} }

通过mCallbacks.onTuneStatusChanged();调用到FMRadio.java的内部存根类IFMRadioServiceCallbacks.stub类的public void onTuneStatusChanged()方法进行存入fm频率。数据最后调用FMRadio的resetFMStationInfoUI()刷新UI

 public void <strong>onTuneStatusChanged() </strong> {
Log.d(LOGTAG, "mServiceCallbacks.onTuneStatusChanged: ");
if (mIsScaning) {
Log.d(LOGTAG, "isScanning....................");
SharedPreferences sp = getSharedPreferences(SCAN_STATION_PREFS_NAME, 0);
SharedPreferences.Editor editor = sp.edit();
int station_number = sp.getInt(NUM_OF_STATIONS, 0);
station_number++;
editor.putInt(NUM_OF_STATIONS, station_number);
editor.putString(STATION_NAME + station_number, station_number + "");
editor.putInt(STATION_FREQUENCY + station_number,
FmSharedPreferences.getTunedFrequency());
editor.commit();
}
<strong>cleanupTimeoutHandler();
mHandler.post(mUpdateStationInfo);
mHandler.post(mOnStereo);</strong>
}

发送一handler跟新UI,调用此回调方法Runnable mUpdateStationInfo = new Runnable()

 Runnable mUpdateStationInfo = new Runnable() {
public void run() {
cleanupTimeoutHandler();
PresetStation station = new PresetStation("", FmSharedPreferences.getTunedFrequency());
if (station != null) {
mTunedStation.Copy(station);
}
<strong>updateSearchProgress();
resetFMStationInfoUI();</strong>
}
};

updateStationInfoToUI()。

private void <strong>updateStationInfoToUI()</strong> {
double frequency = mTunedStation.getFrequency() / 1000.0;
mTuneStationFrequencyTV.setText("" + frequency + "MHz");
if ((mPicker != null) && mUpdatePickerValue) {
mPicker.setValue(((mTunedStation.getFrequency() - mPrefs.getLowerLimit())
/ mPrefs.getFrequencyStepSize()));
}
mStationCallSignTV.setText(mTunedStation.getPIString());
mProgramTypeTV.setText(mTunedStation.getPtyString());
mRadioTextTV.setText("");
mERadioTextTV.setText("");
mRadioTextScroller.mOriginalString = "";
mRadioTextScroller.mStringlength = 0;
mRadioTextScroller.mIteration = 0;
mERadioTextScroller.mOriginalString = "";
mERadioTextScroller.mStringlength = 0;
mERadioTextScroller.mIteration = 0;
mProgramServiceTV.setText("");
mStereoTV.setText("");
setupPresetLayout();
}

FM启动和关闭搜索都是通过JNI调究竟层实现,代码路径是:vendor\qcom\opensource\fm\jni

android_hardware_fm.cpp

/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "acquireFdNative", "(Ljava/lang/String;)I",
(void*)android_hardware_fmradio_FmReceiverJNI_acquireFdNative},
{ "closeFdNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_closeFdNative},
{ "getFreqNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getFreqNative},
{ "setFreqNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setFreqNative},
{ "getControlNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getControlNative},
{ "setControlNative", "(III)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setControlNative},
{ "startSearchNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_startSearchNative},
{ "cancelSearchNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_cancelSearchNative}, { "getRSSINative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getRSSINative},
{ "setBandNative", "(III)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setBandNative},
{ "getLowerBandNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getLowerBandNative},
{ "getUpperBandNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getUpperBandNative},
{ "getBufferNative", "(I[BI)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getBufferNative},
{ "setMonoStereoNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setMonoStereoNative},
{ "getRawRdsNative", "(I[BI)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getRawRdsNative},
{ "setNotchFilterNative", "(IIZ)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setNotchFilterNative},
{ "startRTNative", "(ILjava/lang/String;I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_startRTNative},
{ "stopRTNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_stopRTNative},
{ "startPSNative", "(ILjava/lang/String;I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_startPSNative}, { "stopPSNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_stopPSNative},
{ "setPTYNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setPTYNative},
{ "setPINative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setPINative},
{ "setPSRepeatCountNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setPSRepeatCountNative},
{ "setTxPowerLevelNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setTxPowerLevelNative},
{ "setAnalogModeNative", "(Z)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setAnalogModeNative},
{ "SetCalibrationNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_SetCalibrationNative},
{ "configureSpurTable", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_configureSpurTable}, };

上面写明了从jni的调用关系。

详细的函数实现,请到Android_hardware_fm.cpp中去查看。我就不一一写出来了。以上就是FM搜索频率与取消搜索频率的操作与实现。

搜索到频率后就能够听FM了。

Android FM模块学习之二 FM搜索频率流程的更多相关文章

  1. Android FM模块学习之二 FM搜索频道

    最近在学习FM模块,FM是一个值得学习的模块,可以从上层看到底层.上层就是FM的按扭操作和界面显示,从而调用到FM底层驱动来实现广播收听的功能. 看看FM启动流程:如下图: 先进入FMRadio.ja ...

  2. Android FM模块学习之三 FM手动调频

    前一章主要是FM的自动调频, 接下来我们就看看FM手动调频是如何进行的.如果不清楚FM自动调频的过程,请打开超链接查看FM搜索频率流程. 首先来看一下流程图: 2.滑动刻度盘HorizontalNum ...

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

    Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 前一章我们了解了FM手动调频,接下 ...

  4. Android FM模块学习之一 FM启动流程

    最近在学习FM模块,FM是一个值得学习的模块,可以从上层看到底层. 上层就是FM的按扭操作和界面显示,从而调用到FM底层驱动来实现广播收听的功能. FM启动流程:如下图: 先进入FMRadio.jav ...

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

    转自:http://blog.csdn.net/tfslovexizi/article/details/41516149?utm_source=tuicool&utm_medium=refer ...

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

    接着看FM模块的其他几个次要的类的源码.这样来看FM上层的东西不是太多. 请看android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\ ...

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

    前几章我们分析了FM模块的几个主要的类文件,今天要分析的是:FMTransceiver.java   // 某些工程中名称为FMRadioService.java public class FmTra ...

  8. Android Bluetooth模块学习笔记

    一.蓝牙基础知识 1.蓝牙( Bluetooth )是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换.蓝牙基于设备低成本的收发器芯片,传输距离近.低功耗. 2.微波频段: ...

  9. android NDK 实用学习(二)-java端对象成员赋值和获取对象成员值

    1,关于java端类及接口定义请参考: android NDK 实用学习-获取java端类及其类变量 2,对传过来的参数进行赋值: 对bool类型成员进行赋值  env->SetBooleanF ...

随机推荐

  1. PHP之序列化与反序列化讲解

    serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 eg: $stooges = array('Moe','Larry','Curly');$new = ...

  2. 读写UTF-8、Unicode文件(加上了文件头,貌似挺好用)

    conf配置文件一些为UTF-8和Unicode格式,这样便可良好的支持多语言,从网上查阅资料后,将读写UTF-8.Unicode文件写了几个最精简的函数,更新后加了是否写文件头的功能,以适应更多需要 ...

  3. 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】

    原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...

  4. Android 常用UI控件之TabHost(2)简单示例

    1,布局 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tool ...

  5. iconv装换文件编码格式

    最近在mac上编译xml文本文件的时候用vim打开文件汉字总是显示乱码,修改.vimrc,修改iterm编码格式各种方法都使用遍了.最后通过iconv工具将原来的文件编码格式直接转为UTF-8解决掉. ...

  6. 关于JQuery与AJAX验证

    AJAX验证,其实就是JS代码,他就是先利用Jquery或JS获取一个值,然后偷偷的把值传送到验证界面,然后在偷偷的把验证后的结果给传回来,利用传回来的结果在进行JS判断,从而不会刷新界面. 用图片解 ...

  7. Webform和MVC,为什么MVC更好一些?

    前言 如果你看了最近微软的议程,你会发现他们现在的焦点除了MVC,还是MVC.问题在于为什么微软如此热衷于丢弃传统的APS.NET Webform而转向ASP.NET MVC?本文就主要来讨论这个问题 ...

  8. Web服务器排行:Nginx超越Apache 成为全球

    Apache(34.5%)第一名的位置.不过,纵观全球,Apache仍然是最受欢迎的Web服务器,有65.3%的网站使用. 在排名前100万的网站中,主流服务器仍为Apache,占据了60.6%的份额 ...

  9. Http get,post,soap协议的区别

    转自:http://www.cnblogs.com/c2303191/articles/1107027.html 1.Http作为web服务的首选协议,具有4大优点:1)http非常简单,以纯文本(超 ...

  10. NOIP2010关押罪犯 二分+二染色

    这个题一上来 没有思路,后来想没有思路就二分吧 那么我们来二分 首先,大于当前的mid值的关系,不能出现在一个集合里 (即关系形成的图是一个二分图,判定二分图可以二染色) 如果不能形成二分图,那么说明 ...