本文是基于上一篇《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分析(七) ---- 接口扩展(异步转同步)的更多相关文章

  1. Android Telephony分析(六) ---- 接口扩展(实践篇)

    本文将结合前面五篇文章所讲解的知识,综合起来,实现一个接口扩展的功能.如果还没有阅读过前面五篇文章的内容,请先阅读:<Android Telephony分析(一) — Phone详解 >& ...

  2. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\andro ...

  3. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) —- TelephonyManager详解 >的1.4小节.从TelephonyRegistry的大部分方法中: 可以看 ...

  4. Android Telephony分析(二) ---- RegistrantList详解

    前言 本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程.在Telephony模块中,在RIL.Tracker(ServiceStateTrac ...

  5. Android Telephony分析(四) ---- TelephonyManager详解

    前言 TelephonyManager主要提供Telephony相关信息的查询/修改功能,以及Phone状态监听功能,封装的方法主要是提供给APP上层使用.TelephonyManager.java ...

  6. Android Telephony分析(一) ---- Phone详解

    目录: Phone的继承关系与PhoneFactory(GsmCdmaPhone.ImsPhone.SipPhone) Phone进程的启动 Phone对象的初始化(DefaultPhoneNotif ...

  7. Android 短信模块分析(七) MMS数据库定义及结构整理

    一. mmssms.db 数据库mmssms.db中表的定义见表4.1至4.18所示: 表4.1 addr(彩信地址) 字段名 类型 描述 备注 _id INTEGER PRIMARY_KEY 主键I ...

  8. Tornado源码分析系列之一: 化异步为'同步'的Future和gen.coroutine

    转自:http://blog.nathon.wang/2015/06/24/tornado-source-insight-01-gen/ 用Tornado也有一段时间,Tornado的文档还是比较匮乏 ...

  9. Android Wear 数据类型和接口的发送和同步数据概述

    Android Wear数据层API,这是google play service部分,通信信道,以你的手持设备和耐磨应用. Api它包含一系列数据对象,可以让系统通过监控和通知行app重要的事件数据层 ...

随机推荐

  1. CentOS安装 netdata 实时监视 Linux 系统性能

    作为一个 Linux 系统的管理员,为了随时了解系统资源的占用情况,有必要使用专门的系统监视工具.如果你需要对 Linux 系统.应用程序.SNMP 设备进行实时的性能监视,那么 netdata 这个 ...

  2. Gym 102021D : Down the Pyramid(思维)

    Do you like number pyramids? Given a number sequence that represents the base, you are usually suppo ...

  3. 洛谷 P3455 [POI2007]ZAP-Queries (莫比乌斯函数)

    题目链接:P3455 [POI2007]ZAP-Queries 题意 给定 \(a,b,d\),求 \(\sum_{x=1}^{a} \sum_{y=1}^{b}[gcd(x, y) = d]\). ...

  4. jmeter 不同线程组之间传递变量2

    方法1  通过变量传递参数: 第一个脚本: HTTP Request_新建出差申请单_登录,关联出参数token.companyId.userId.userName 1.添加后置处理器:BeanShe ...

  5. http(python)

    1.client 1) httpie http -f POST example.org hello=World http POST http://192.168.200.251:55101/Api/C ...

  6. sysprep

    今天做虚拟机模板,以及克隆.单纯的克隆会造成很多冲突问题的产生,所以在这里,windows自带的sysprep功能很好的解决了这一点. 路径位于:C:\Windows\System32\Sysprep ...

  7. No symbol table is loaded. Use the "file" command.

    No symbol table is loaded.  Use the "file" command. gdb 1. 首先使用gcc   -g    .c文件   -o  可执行文 ...

  8. 【CSS】div的背景图完整图片覆盖

    最初的代码: .container_first { width: 100%; height: 100%; background: url(10176581.jpg); background-size: ...

  9. Element UI - 打开弹出框(el-dialog)页面会抖动

    当 dialog 出现时,页面右边缩小了 5px,模态框看起来会抖动 解决方案 发现 body 多了样式:padding-right: 5px 和 overflow: hidden.紧接着我就在全局设 ...

  10. jq enter键发送

    $('.content').keypress(function(e) { if(e.keyCode === 13) { //调用接口 return false; } }) .