Android Telephony分析(七) ---- 接口扩展(异步转同步)
本文是基于上一篇《Android Telephony分析(六) —- 接口扩展(实践篇)》来写的。
上一篇介绍的接口扩展的方法需要实现两部分代码:
1. 从APP至RIL,发送请求;
2. 从RIL至APP,上报结果。
由于这是一个异步请求,所以两部分流程都不能少,导致流程过于复杂。
而本文的目的就是为了将异步请求转换成同步请求,节省第二部分“上报结果”的流程,从而简化整个接口扩展的流程和代码量。(当然,虽然《Android Telephony分析(六) —- 接口扩展(实践篇)》代码流程复杂了些,但是它综合较多的知识点,其自身的价值还是有的。)
1. 具体的代码实现
1.1 扩展CommandsInterface接口
同《Android Telephony分析(六) —- 接口扩展(实践篇)》1.1小节。
1.2 扩展PhoneInternalInterface接口
同《Android Telephony分析(六) —- 接口扩展(实践篇)》1.2小节。
假如现在Phone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)中已有两个可用的接口:
@Override
public void setValueToModem(int input,Message resp){
mCi.setValueToModem(input,resp);
} @Override
public void getValueFromModem(Message resp){
mCi.getValueFromModem(resp);
}
1.3 扩展ITelephony接口
先在ITelephony.aidl(frameworks\base\telephony\java\com\android\internal\telephony)中新增接口:
boolean setValueToModem (int input);
String getValueFromModem();
请注意,此时接口的返回值已不再是void。
在PhoneInterfaceManager.java (packages\services\telephony\src\com\android\phone)中实现该接口:
@Override
public String getValueFromModem() {
//本小节的最后会讲解sendRequest()方法
String value = (String)sendRequest(CMD_GET_VALUE,null);
return value;
} @Override
public boolean setValueToModem(int input) {
Boolean success = (Boolean)sendRequest(CMD_SET_VALUE,input);
return success;
}
同时需要定义四个消息:
private static final int CMD_GET_VALUE = 100;
private static final int EVENT_GET_VALUE_DONE = 101;
private static final int CMD_SET_VALUE = 102;
private static final int EVENT_SET_VALUE_DONE = 103;
以及在内部类MainThreadHandler的handleMessage()方法中添加对这四个消息的处理:
//发送get请求时的处理
case CMD_GET_VALUE:
request = (MainThreadRequest) msg.obj;
//将在sendRequest()方法中创建的MainThreadRequest对象封装进新的Message中。
onCompleted = obtainMessage(EVENT_GET_VALUE_DONE, request);
//在这里调用Phone中的接口
mPhone.getValueFromModem(onCompleted);
break; //对于get请求modem返回结果的处理
case EVENT_GET_VALUE_DONE:
ar = (AsyncResult) msg.obj;
//取出发送请求时创建的MainThreadRequest对象
request = (MainThreadRequest) ar.userObj;
//如果没有出现异常且返回的结果不为空
if (ar.exception == null && ar.result != null) {
request.result = ar.result;// String
} else {
//get请求出现异常,返回默认值
request.result = "";
if (ar.result == null) {
loge("getValueFromModem: Empty response");
} else if (ar.exception instanceof CommandException) {
loge("getValueFromModem: CommandException: " +
ar.exception);
} else {
loge("getValueFromModem: Unknown exception");
}
}
synchronized (request) {
//唤醒所有正在等待该对象的线程,退出wait的状态
request.notifyAll();
}
break; //get请求,同理
case CMD_SET_VALUE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_VALUE_DONE, request);
mPhone.setValueToModem((Integer) request.argument, onCompleted);
break; case EVENT_SET_VALUE_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
if (ar.exception == null) {
request.result = true;
} else {
request.result = false;
if (ar.exception instanceof CommandException) {
loge("setValueToModem: CommandException: " + ar.exception);
} else {
loge("setValueToModem: Unknown exception");
}
}
synchronized (request) {
request.notifyAll();
}
break;
在PhoneInterfaceManager.java中的代码是本文的核心。在sendRequest()方法中会进入死循环,调用object.wait()强行阻塞线程,直到modem返回结果上来后,object的notifyAll()才停止,最后直接把结果返回给APP,所以这就是将异步请求强行转换成同步的解决方案。
sendRequest()方法是android原生的,不用我们添加代码:
/**
* Posts the specified command to be executed on the main thread,
* waits for the request to complete, and returns the result.
*/
private Object sendRequest(int command, Object argument, Integer subId) {
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
throw new RuntimeException("This method will deadlock if called from the main thread.");
}
//创建Request对象
MainThreadRequest request = new MainThreadRequest(argument, subId);
Message msg = mMainThreadHandler.obtainMessage(command, request);
msg.sendToTarget(); //锁住request对象
synchronized (request) {
//进入死循环
while (request.result == null) {
try {
//让线程进入等待状态,直到它被notifyAll唤醒
request.wait();
} catch (InterruptedException e) {
//就算异常也不退出,不return。
}
}
}
return request.result;
}
//其中MainThreadRequest只是一个普通的内部类,不是线程。
//所以上面request.wait()调用的时Object类wait()方法。
private static final class MainThreadRequest {
/** The argument to use for the request */
public Object argument;
/** The result of the request that is run on the main thread */
public Object result;
// The subscriber id that this request applies to. Defaults to
// SubscriptionManager.INVALID_SUBSCRIPTION_ID
public Integer subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; public MainThreadRequest(Object argument) {
this.argument = argument;
} public MainThreadRequest(Object argument, Integer subId) {
this.argument = argument;
if (subId != null) {
this.subId = subId;
}
}
}
接着在TelephonyManager.java (frameworks\base\telephony\java\android\telephony)中封装Phone Service的方法:
/**@hide*/
public String getValueFromModem() {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.getValueFromModem();
} catch (RemoteException ex) {
Rlog.e(TAG, "getValueFromModem RemoteException", ex);
} catch (NullPointerException ex) {
Rlog.e(TAG, "getValueFromModem NPE", ex);
}
return "";
} /**@hide*/
public boolean setValueToModem(int input) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.setValueToModem(input);
} catch (RemoteException ex) {
Rlog.e(TAG, "setValueToModem RemoteException", ex);
} catch (NullPointerException ex) {
Rlog.e(TAG, "setValueToModem NPE", ex);
}
return false;
}
整个过程的时序图如下:
2. APP如何使用接口
在APP中可以这样调用并调试接口:
//set值
boolean setResult = TelephonyManager.getDefault().setValueToModem(1);
//get值
String getResult = TelephonyManager.getDefault().getValueToModem();
在APP侧来看,确实简单省事了很多,调用接口就可以马上得到返回值,但是有点需要注意的是,为了防止这种接口阻塞主线程,所以最好在子线程中调用这类接口。
3. 总结
将异步请求转换成同步请求,紧紧依赖着Object类的wait和notifyAll方法才能实现。当然Android代码中不仅仅只有PhoneInterfaceManager.java这个地方使用了这种方法,高通也实现了类似的代码提供API给APP侧调用,进而可以动态修改某些NV的值,这里只能点到为止。最后附上wait和notifyAll方法的详解:
void wait() :
导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
void notifyAll() :
解除所有那些在该对象上调用wait方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
————————————————
版权声明:本文为CSDN博主「linyongan」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/linyongan/article/details/52189217
Android Telephony分析(七) ---- 接口扩展(异步转同步)的更多相关文章
- Android Telephony分析(六) ---- 接口扩展(实践篇)
本文将结合前面五篇文章所讲解的知识,综合起来,实现一个接口扩展的功能.如果还没有阅读过前面五篇文章的内容,请先阅读:<Android Telephony分析(一) — Phone详解 >& ...
- Android Telephony分析(三) ---- RILJ详解
前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\andro ...
- Android Telephony分析(五) ---- TelephonyRegistry详解
本文紧接着上一篇文章<Android Telephony分析(四) —- TelephonyManager详解 >的1.4小节.从TelephonyRegistry的大部分方法中: 可以看 ...
- Android Telephony分析(二) ---- RegistrantList详解
前言 本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程.在Telephony模块中,在RIL.Tracker(ServiceStateTrac ...
- Android Telephony分析(四) ---- TelephonyManager详解
前言 TelephonyManager主要提供Telephony相关信息的查询/修改功能,以及Phone状态监听功能,封装的方法主要是提供给APP上层使用.TelephonyManager.java ...
- Android Telephony分析(一) ---- Phone详解
目录: Phone的继承关系与PhoneFactory(GsmCdmaPhone.ImsPhone.SipPhone) Phone进程的启动 Phone对象的初始化(DefaultPhoneNotif ...
- Android 短信模块分析(七) MMS数据库定义及结构整理
一. mmssms.db 数据库mmssms.db中表的定义见表4.1至4.18所示: 表4.1 addr(彩信地址) 字段名 类型 描述 备注 _id INTEGER PRIMARY_KEY 主键I ...
- Tornado源码分析系列之一: 化异步为'同步'的Future和gen.coroutine
转自:http://blog.nathon.wang/2015/06/24/tornado-source-insight-01-gen/ 用Tornado也有一段时间,Tornado的文档还是比较匮乏 ...
- Android Wear
数据类型和接口的发送和同步数据概述
Android Wear数据层API,这是google play service部分,通信信道,以你的手持设备和耐磨应用. Api它包含一系列数据对象,可以让系统通过监控和通知行app重要的事件数据层 ...
随机推荐
- java对象属性为date oracle数据库字段为Timestamp 处理方式
解决方案 SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); java.util.Date da ...
- LOJ #103. 子串查找 (Hash)
题意 给定两个字符串 \(A\) 和 \(B\),求 \(B\) 在 \(A\) 中的出现次数. 思路 这是一道 \(KMP\) 的模板题. 不过 \(Hash\) 是个好东西,可以用 \(Hash\ ...
- 201⑨湘潭邀请赛 Chika and Friendly Pairs(HDU6534)
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6534 题意: 给你一个数组,对于第i个数来说,如果存在一个位置j,使得j>i并且a[j]-k&l ...
- html 局部打印
首先有个调用的方法.printView(). function printView() { bdhtml = window.document.body.innerHTML;//获取当前页的html代码 ...
- Rust <3>:控制流
if.loop.for.while: fn main() { if 3 > 4 { println!{">"}; } else { println!{"< ...
- 防止重复提交 aspx.net
:服务器控制.后台生成一个token,存入session或者其他缓存里面.渲染表单时,给form一个隐藏的token(令牌).用户提交表单时:先判断表单里面的token是否存在,不存在拒绝接受此数据: ...
- UML指南系列——用例图
可以用用例来描述正在开发的系统想要实现的行为,而不必说明这些行为如何实现. 结构良好的用例只表示系统或者子系统的基本行为,而且既不过于笼统也不过于详细.
- leetcode.矩阵.240搜索二维矩阵II-Java
1. 具体题目 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵具有以下特性:每行的元素从左到右升序排列:每列的元素从上到下升序排列. 示例: 现有矩阵 ...
- echarts 给formatter文字添加不同颜色
legend: { x : 'center', y : 'bottom', icon: "circle", itemWidth: 8, // 设置宽度 itemHeight: 8, ...
- 小程序图片在安卓上拉伸的问题&导航&返回首页
今天提了一个bug,有几张图片在安卓上面加载会先变大拉伸再恢复正常 出现这样的问题应该是用widthFix造成的 具体原因还不是很清楚,因为都是本地图片,所以我就直接把高也设置好就暂时没有这个问题 ...