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

原文地址:https://www.cnblogs.com/yz123/p/12024829.html

时间: 2024-10-10 08:18:07

Android Telephony分析(七) ---- 接口扩展(异步转同步)的相关文章

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

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

Tornado源码分析系列之一: 化异步为&#39;同步&#39;的Future和gen.coroutine

转自:http://blog.nathon.wang/2015/06/24/tornado-source-insight-01-gen/ 用Tornado也有一段时间,Tornado的文档还是比较匮乏的,但是幸好其代码短小精悍,很有可读性,遇到问题时总是习惯深入到其源码中.这对于提升自己的Python水平和对于网络及HTTP的协议的理解也很有帮助.本文是Tornado源码系列的第一篇文章,网上关于Tornado源码分析的文章也不少,大多是从Event loop入手,分析Event loop的工作

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

Android Wear数据层API,这是google play service部分,通信信道,以你的手持设备和耐磨应用. Api它包含一系列数据对象,可以让系统通过监控和通知行app重要的事件数据层. 下面是几种对象类型. Data Item 提供数据存储用于手机和wear的自己主动同步. Messages 消息API能够实现fire-and-forget模式,如在wear中控制手机播放器控制或者通过手机发送一个意图给wear,当建立连接或者发生传输错误的时候.都会提供消息.消息是一个典型的单

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

一. mmssms.db 数据库mmssms.db中表的定义见表4.1至4.18所示: 表4.1 addr(彩信地址) 字段名 类型 描述 备注 _id INTEGER PRIMARY_KEY 主键ID 系统生成 msg_id INTEGER The ID of MM whcich this Address entry belongs to. Pdu主键关联 contact_id INTEGER The ID of contact entry in Phone Book   address TE

非主流node.js异步转同步

异步转同步方案分类 说起nodejs的异步转同步,估计大家不陌生.因为nodejs回调实在太多了,稍微复杂一点的程序就会有很多层的回调嵌套.为了处理这些令人抓狂的回调,我们一般需要使用一些框架或工具将这些异步过程转换成相对比较容易理解的同步过程,也就是我们本文所说的异步转同步.而完成这种转换的工具或库大体上可以分为三类:1. 回调链管理类 2. 编译工具类 3. 底层实现修改类. 第一类是最工具常见的,以Promise.async为代表.这类工具一般需要调用一个方法将我们 的处理函数包裹然后进行

Android多线程分析之五:使用AsyncTask异步下载图像

Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<Android多线程分析之中的一个:使用Thread异步下载图像>中.曾演示了怎样使用 Thread 来完毕异步任务. Android 为了简化在 UI 线程中完毕异步任务(毕竟 UI 线程是 app 最重要的线程).实现了一个名为 AysncTask 的模板类.使用 AysncTask 能够在异步任务进行的同

Android多线程分析之一:使用Thread异步下载图像

罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 打算整理一下对 Android Framework 中多线程相关知识的理解,主要集中在 Framework 层的 Thread, Handler, Looper, MessageQueue, Message, AysncTask,当然不可避免地要涉及到 native 方法,因此也会分析 dalvik 中和线程以及消息处理相关的代码:如 dalvik 中的 C++ Thread 类以及 Message

[gitbook] Android框架分析系列之Android Binder详解

请支持作者原创: https://mr-cao.gitbooks.io/android/content/android-binder.html Android Binder详解 Table of Contents 1. binder简介 2. binder的实现 2.1. IBinder类简介 2.2. IInterface类简介 2.3. BpBinder和BBinder简介 2.4. ProcessState和IPCThreadState简介 2.5. ServiceManager简介 2.

Android多线程分析之二:Thread的实现

Android多线程分析之二:Thread 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前文<Android多线程分析之一:使用Thread异步下载图像>中演示了如何使用 Thread 处理异步事务.示例中这个 Java Thread 类都是位于 Framework 层的类,它自身是通过 JNI 转调 dalvik 里面的 Thread 相关方法实现的.因此要分析 Androd 中的线程,就需要分析这两层中的与线程相关的代码,这就是本文要