本文是基于上一篇《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. Openstack组建部署 — Environment of Controller Node

    目录 目录 前文列表 Controller Node Install and configure components Setup DNS Server Setup NTP Server Instal ...

  2. Eureka 系列(02)Eureka 一致性协议

    目录 Eureka 系列(02)Eureka 一致性协议 0. Spring Cloud 系列目录 - Eureka 篇 1. 服务发现方案对比 1.1 技术选型 1.2 数据模型 2. Eureka ...

  3. Django中的缓存机制

    概述       对于中等流量网站来说,尽可能的减少开销是必要的.缓存数据就是为了保存那些需要很多计算资源大的结果,这样的的话就不必在下次重复消耗计算资源.     Django自带了一个健壮的缓存系 ...

  4. POJ 1052 MPI Maelstrom

    MPI Maelstrom Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 5547   Accepted: 3458 Des ...

  5. 完全卸载win10上的Ubuntu子系统 - Windows Subsystem for Linux(WSL)

    Ctrl + R 键入: lxrun /uninstall /full 具体请看 microsoft的说明:Frequently Asked Questions

  6. spring security 学习二

    doc:https://docs.spring.io/spring-security/site/docs/ 基于表单的认证(个性化认证流程): 一.自定义登录页面 1.在securityConfigy ...

  7. 我爱Linux

    这道题卡了好久,题是一张图片,打开看到看提示以为是用哪个Linux命令处理,直到后来知道后面是python序列化文件的数据,将FF D9后保存出来,将序列化文件读出来写脚本把它画出来 import p ...

  8. 安装python及编辑工具PyCharm

    win10下安装python环境,安装编辑工具PyCharm 1.安装 pythonpython安装包下载地址https://www.python.org/ftp/python/3.8.0/pytho ...

  9. java中this和super关键字的作用

    this是对象内部指代自身的引用,同时也是解决成员变量和局部变量同名问题:this可以调用成员变量,不能调用局部变量:this也可以调用成员方法,但是在普通方法中可以省略this,在构造方法中不允许省 ...

  10. kindeditor加入方法

    1.editor文件夹拷进来 2. editor里jsp子文件夹里找到几个jar拷贝到网站的web-app里的lib下 3.  网页里 head里加个这个 <link rel="sty ...