前阵子,刚好遇到个问题:

收听FM,当前频道显示为105.3,拔出耳机.在FM界面选择89.9频道,再次插入耳机收听的FM不是选中的89.9频道,而是105.3频道。

这个的根本原因是拔出耳机后,fmr会监听action为ACTION_HEADSET_PLUG 的广播,然后在activity中禁用相关的view控件(这些控件只能在插入耳机,也就是能正常使用收音机的情况下使用)。但是由于广播会延迟,拔出耳机后并不是立即就会收到广播,而且相关的方法处理也会耗时,所以,拔出耳机后的二到三秒之间是可以操作view的,这个view被用户操作后,值就会变化,但在service解除绑定并被销毁的时候,有些数据是没有被保存的。

因此,当再一次插入耳机之后,按照之前的配置还原,有一定的误差。

解决办法: 可以使用,当拔出耳机后直接退出fm,但这样貌似不太友好,可能客户只是不小心拔出了呢?另外就是处理如何规避广播延迟的影响呢?

基于这个问题,特地看了下fmr的源代码。没有具体去看,只看了个大概。

收音机插拔耳机处理

当耳机插入和拔出,framework层AudioService.java都有广播发出。

对应的广播Action(in Intent.java, android.media.AudioManager.java)

public static final String ACTION_HEADSET_PLUG =android.media.AudioManager.ACTION_HEADSET_PLUG;

public static final String ACTION_HEADSET_PLUG =

"android.intent.action.HEADSET_PLUG";

Fmr在FMRadioService.java中注册耳机插拔的广播。

插拔耳机的广播intent,会携带两个参数,state  取值 0 拔出;1 插入  ,micphone  1  携带micphone的耳机,0 不携带micphone的耳机。

<span style="font-size:12px;">mReceiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {String action = intent.getAction();if (action == null) {return;}Log.i(TAG, "Received intent: " + action);//#########ACTION_HEADSET_PLUG beginif (action.equals(Intent.ACTION_HEADSET_PLUG)) {if (mFMServiceState != FM_SERVICE_ON) {//the service is running good.return;}final boolean headsetPlugin = (intent.getIntExtra("state",0) == 1);//in 1 ,out 0final boolean supportInAntenna = Util.supportInternalAntenna(context);//headset modelif (DEBUG) Log.d(TAG, "HEADSET is pluged in? : " + headsetPlugin);if (!supportInAntenna && !headsetPlugin) {// The headset is plugged out, stop FMR.Intent Stopintent = new Intent("android.intent.action.STOP_FM");sendBroadcast(Stopintent);mFMServiceState = FM_SERVICE_OPENED;//when exit the fm//update the widget .if (mAppWidgetProvider != null) {mAppWidgetProvider.notifyChange(FMRadioService.this, FM_CLOSED);}/*enable touch sound when headset plugged out, add by RC */mAudioInterface.enableTouchSound();/*enable touch sound when headset plugged out, add by RC */clearFMService();// >>>Toast.makeText(FMRadioService.this,R.string.fmradio_no_headset_in_listen,Toast.LENGTH_LONG).show();//stopSelf(mServiceStartId);}</span>

当FMRadioService.java接收到广播之后,会停止所有活动包括Service。但是,广播有一定的延迟,可能抽出耳机之后,UI界面在短时间内仍然可以操作(大概2s-5s)。

在注册广播代码中,

iFilter.addAction(Intent.ACTION_HEADSET_PLUG);

iFilter.addAction(FMAudioInterface.FM_ROUTE_HEADSET);

iFilter.addAction(FMAudioInterface.FM_ROUTE_LOUDSPEAKER);

有FM_ROUTE_HEADSET和FM_ROUTE_LOUDSPEAKER  与耳机类型相关,耳机插拔后是否允许使用扬声器,通常是只能插入耳机才能收听。

二,监听sd卡插拔

In  Intent.java

public static final String ACTION_MEDIA_UNMOUNTED ="android.intent.action.MEDIA_UNMOUNTED"

<span style="font-size:12px;">mSdCardListener = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {String action = intent.getAction();if (action == null) {return;}if (DEBUG) Log.i(TAG, "Received intent: " + action);if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {if (mFMServiceState == FM_SERVICE_ON) {Intent Stopintent = new Intent("android.intent.action.STOP_FM");sendBroadcast(Stopintent);}}}};</span>

三 ,aidl (androidinterface definition language)

FMRadio.java

FMRadioService.java

IFMRadioService.aidl

IRemoteServiceCallBack.aidl

AIDL文件主要声明相关接口,供client使用

而这些接口是在对应Service中实现的(Server)。

需要的几个步骤:

1.      创建AIDL文件,和java包在同一个包下面。

2.      对应service实现Stub

3.      客户端使用的Activity创建ServiceConnection,绑定

下面查看fmr中是如何实现的Service和Activity通信的。

FMRService.java   内部实现对应IFMRadioService.Stub()

// Implementation of IFMRadioService AIDL Interfaceprivate final IFMRadioService.Stub mBinder = new IFMRadioService.Stub() {public int getFMServiceStatus() {if (DEBUG) Log.i(TAG, "AIDL: getFMServiceStatus: " + mFMServiceState);return mFMServiceState;}……
}

该binder对象和Service绑定

 @Overridepublic IBinder onBind(Intent intent) {if (DEBUG) Log.i(TAG, "onBind() called");return mBinder;}

在客户端的Activity中,需要创建ServiceConnection 实例,实现onServiceConnected和onServiceDisconnected方法。FMRadio.java

    @Overridepublic void onCreate(Bundle savedInstanceState) {Log.i(TAG, "onCreate()");super.onCreate(savedInstanceState);……// bind to FMRadioServicebindToService();……}

另外:

我们在绑定activity到service的时候,调用bindService方法,第一个参数中是个intent意图,参数是需要绑定的Service的报名+类名,第二个参数Context.BIND_AUTO_CREATE 为自动绑定机制,即使你不创建也会绑定。

private boolean bindToService() {Log.i(TAG, "Start to bind to FMRadio service");return bindService(new Intent("com.pekall.fmradio.FMRADIO_SERVICE"),mServConnection, Context.BIND_AUTO_CREATE);}
/#######test ###### bindprivate ServiceConnection mServConnection = new ServiceConnection() {public void onServiceConnected(ComponentName className,android.os.IBinder service) {Log.w(TAG, "onServiceConnected");mIsBound = true;//initialize FMRadioService//创建IFMRadioService对象mFMService = IFMRadioService.Stub.asInterface(service);if (mFMService == null) {Log.e(TAG, "onServiceConnected error, mFMService null");return;}updateTrackInfo();updateDisplayPanel(mCurrentFreq);updateSwithcButton();try {mFMService.registerCallback(mCallback);} catch (RemoteException e) {Log.e(TAG, "", e);}}public void onServiceDisconnected(ComponentName className) {Log.i(TAG, "onServiceDisconnected");if (mFMService == null) {Log.e(TAG, "onServiceDisconnected error, mFMService null");return;}try {mFMService.unregisterCallback(mCallback);} catch (RemoteException e) {Log.e(TAG, "", e);}mFMService = null;finish();}};

获取手机存储列表。internal or  sdcar

在fmr源码中,看到有个方式是获取手机的存储列表路径。

源代码如下,SDCardInfo.java

 private class SDCardInfo {public static final int INTERNAL_SD = 1;public static final int EXTERNAL_SD = 2;String path;String state;int type;public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getState() {return state;}public void setState(String state) {this.state = state;}public int getType() {return type;}public void setType(int type) {this.type = type;}boolean isMounted() {if (Environment.MEDIA_MOUNTED.equals(this.state)) {return true;}return false;}public String toString() {return "[ SDCardInfo: path=" + path + ", state=" + state +", isMounted =" + isMounted() + ", type is " + type+ "]";}}
private ArrayList<SDCardInfo> initSDCardList() {StorageManager storageManager = (StorageManager)FMRadio.this.getSystemService(Context.STORAGE_SERVICE);ArrayList<SDCardInfo> sdCardInfos = new ArrayList<SDCardInfo>();StorageVolume[] storgerVolumeList = storageManager.getVolumeList();String[] storgerPaths = storageManager.getVolumePaths();if (storgerVolumeList != null) {for (int i = 0; i < storgerVolumeList.length; i++) {SDCardInfo sdCardInfo = new SDCardInfo();sdCardInfo.path = storgerPaths[i];boolean isCanRemoved = storgerVolumeList[i].isRemovable();if (isCanRemoved) {sdCardInfo.type = SDCardInfo.EXTERNAL_SD;} else {sdCardInfo.type = SDCardInfo.INTERNAL_SD;}sdCardInfo.state = storageManager.getVolumeState(storgerPaths[i]);sdCardInfos.add(sdCardInfo);}}Log.i(TAG, "getExternalStorageDirectory = " + Environment.getExternalStorageDirectory());Log.i(TAG, "sdCard info = " + sdCardInfos);return sdCardInfos;}

但是查看了StorageVolume.java后发现,这个类是hide的,sdk里面是用不到的。因此,我们可以使用java的反射机制来调用该类方法,从而达到我们的目的。

public String[] getPaths(Context ctx) {StorageManager manager = (StorageManager) ctx.getSystemService(Context.STORAGE_SERVICE);String path[] = null;try {Method method = manager.getClass().getMethod("getVolumePaths");path = (String[]) method.invoke(manager);} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();Log.e("getPaths...<<<", "No such method name .<<<<");} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();Log.e("getPaths...<<<", "IllegalArgumentException .<<<<");} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();Log.e("getPaths...<<<", "IllegalAccessException .<<<<");} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();Log.e("getPaths...<<<", "InvocationTargetException .<<<<");}return path;}

比较奇怪为何在fmr里面是可以调用的呢?

fmr 收音机源代码相关分相关推荐

  1. 效率最高的Excel数据导入---(c#调用SSIS Package将数据库数据导入到Excel文件中【附源代码下载】)...

     本文目录: (一)背景 (二)数据库数据导入到Excel的方法比较    (三)SSIS的简介    (四)数据库中存储过程示例(SSIS应用需要) (五)Excel模板的制作(这步这么简单,稍微介 ...

  2. OPEN ERP相关财务的基本概念

    http://blog.sina.com.cn/s/blog_6cd44dbc0101bmwz.html OpenERP相关财务概念的讲解 一.记账凭证(Account Move) 会计上的记账凭证, ...

  3. 计算机硬盘计入哪个会计科目,电脑加装固态硬盘如何做分录

    电脑加装固态硬盘如何做分录 在实际工作中,企业购买电脑硬盘,我们先看价值高不高,一般来说,不高的,费用不是很多的,就直接费用化处理就好. 具体的分录如下: 借:管理费用-办公费 贷:银行存款 管理费用 ...

  4. JAVA相关编译知识

    JAVA相关编译知识 总览 语言进化史 整体流程 编译器整体流程 词法分析 语法分析 语义分析 中间代码生成 目标代码生成器 代码优化 编译相关知识 编译种类 热点探测技术 编译什么 什么时候进行检测 ...

  5. 趣店纽交所上市,市值近百亿美金;百度国际事业部将独立分拆;摩拜单车进入韩国水原市丨价值早报

    第[745]期早报由[周四]赞助播出 01 今日头条 趣店登陆纽交所,市值近百亿美金 10月19日消息,中国在线消费分期平台趣店在纽交所上市,交易代码为"QD".上市首日报收于29 ...

  6. 不软的科幻——源代码浅析

    源代码 相关影评: <源代码>(危机解密)"炫- 平行世界 佛经.超弦.源代码=悟入 源代码,评分满分! 一个字:好 更多>> 我也看过这部电影 从大一接触刘慈欣开始 ...

  7. 效率最高的Excel数据导入---(c#调用SSIS Package将数据库数据导入到Excel文件中【附源代码下载】) 转...

    效率最高的Excel数据导入---(c#调用SSIS Package将数据库数据导入到Excel文件中[附源代码下载])  本文目录: (一)背景 (二)数据库数据导入到Excel的方法比较    ( ...

  8. WiBS区块链女性领导力系列研讨会分论坛圆满成功,打破性别偏见,重塑女性价值观

    活动时间:2020年8月16日 2020年8月16日,WiBS(Women in Blockchain-BSV)区块链女性领导力系列研讨会分论坛于北京正式举办.本次分论坛主题为<女性在职场与创新 ...

  9. 一个64位操作系统的设计与实现 源代码,效果图

    一个64位操作系统的设计与实现 源代码 相关下载:https://download.csdn.net/download/qq_35540932/10588541?utm_source=bbsseo 一 ...

最新文章

  1. 使用pyinstaller打包,subprocess报“句柄无效”错误的解决方法
  2. 【五校联考3day2】B
  3. 【C++教程】03.第一个程序解析
  4. Google API:如何访问Google Analytics(分析)数据?
  5. anaconda 设置python3为主_【windows】下Anaconda详细安装过程
  6. C++ 编译发现 error C2146: syntax error : missing ';' before identifier 'm_ctrlserver'
  7. Xilinx - WP509阅读笔记 - 了解射频采样数据转换器的关键参数
  8. 红米note3android驱动,红米Note3手机驱动
  9. 百度员工:上午11点上班,晚上9点下班,年薪80万买房太轻松了
  10. 2021高考查询成绩公众号,微信怎么查高考成绩2021 微信高考成绩查询系统入口
  11. uni-app——如何获取页面容器的高度
  12. .NET Framework各个版本(3.5 - 4.0)
  13. 计算机程序占用端口,程序启动发现端口被占?3步查出它是谁!
  14. openpyxl超详细笔记
  15. 用命令操作方式创建和管理数据库
  16. gitee如何删除仓库
  17. 芝村乡大学生投资理财
  18. 【2021考研】政治做题策略
  19. 【LaTeX】制作 PPT(更新中)
  20. java编写视频播放器_基于Java的视频播放器可以足够快吗?

热门文章

  1. 2021CodePen炫酷无比的作品
  2. 做电商直播卖货前,不妨先种草引流
  3. 【Apache之 Karaf 介绍】
  4. motan yar php,motan学习笔记 四 motan Demo 之yar 分析
  5. html mint ui,vue中Mint UI是什么?
  6. vivado创建自定义IP核
  7. 你的应用如何进入VR市场
  8. SpringBoot图片保存与读取
  9. signature=91a39d56ee3f1c2eb35c4ca5adda65a1,Anomalous signature splitting effects in 79Rb
  10. mkfs.ext3 快速格式化_mac怎么格式化u盘?