Android Telephony分析(七) ---- 接口扩展(异步转同步)

来源:互联网 发布:cf数据异常得多长时间 编辑:程序博客网 时间:2024/05/30 04:28

本文是基于上一篇《Android Telephony分析(六) —- 接口扩展(实践篇)》来写的。 
上一篇介绍的接口扩展的方法需要实现两部分代码: 
1. 从APP至RIL,发送请求; 
2. 从RIL至APP,上报结果。

APPAPPTelephonyManagerTelephonyManagerPhonePhoneRILRIL发送Requestmodem处理返回结果返回结果

由于这是一个异步请求,所以两部分流程都不能少,导致流程过于复杂。 
而本文的目的就是为了将异步请求转换成同步请求,节省第二部分“上报结果”的流程,从而简化整个接口扩展的流程和代码量。(当然,虽然《Android Telephony分析(六) —- 接口扩展(实践篇)》代码流程复杂了些,但是它综合较多的知识点,其自身的价值还是有的。)


http://blog.csdn.net/linyongan 


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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.3 扩展ITelephony接口

先在ITelephony.aidl(frameworks\base\telephony\java\com\android\internal\telephony)中新增接口:

boolean setValueToModem (int input);String getValueFromModem();
  • 1
  • 2
  • 1
  • 2

请注意,此时接口的返回值已不再是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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

同时需要定义四个消息:

    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;
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

以及在内部类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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

在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;            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

接着在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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

整个过程的时序图如下: 
这里写图片描述

2. APP如何使用接口

在APP中可以这样调用并调试接口:

//set值boolean setResult = TelephonyManager.getDefault().setValueToModem(1);//get值String getResult = TelephonyManager.getDefault().getValueToModem();
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在APP侧来看,确实简单省事了很多,调用接口就可以马上得到返回值,但是有点需要注意的是,为了防止这种接口阻塞主线程,所以最好在子线程中调用这类接口。

3. 总结

将异步请求转换成同步请求,紧紧依赖着Object类的wait和notifyAll方法才能实现。当然Android代码中不仅仅只有PhoneInterfaceManager.java这个地方使用了这种方法,高通也实现了类似的代码提供API给APP侧调用,进而可以动态修改某些NV的值,这里只能点到为止。最后附上wait和notifyAll方法的详解: 
void wait() : 
导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。 
void notifyAll() : 

解除所有那些在该对象上调用wait方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。


原文地址: http://blog.csdn.net/linyongan/article/details/52189217

0 0
原创粉丝点击