Android FM模块学习之二 FM搜索频率流程

Posted Brenda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android FM模块学习之二 FM搜索频率流程相关的知识,希望对你有一定的参考价值。

    上一篇大概分析了一下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);方法。

技术分享


技术分享


技术分享

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

技术分享

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搜索频率流程的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中实现FM发射器?

FM信号调制信号和载波

Android 自定义View:实现一个 FM 刻度尺

机器学习之Kmeans

采样频率采样点数分辨率谱线数

Android学习之 换肤功能模块的实现&lt;二&gt;