【转载】【腾讯优测干货分享】Android5.0-6.0双卡适配
转载自:【腾讯优测干货分享】Android5.0-6.0双卡适配指南
原作者:腾讯优测开发工程师 于长敏
这里仅以获取sim卡的IMSI接口(getSubscriberId)和发短信接口(sendTextMessage)为例来详细讲解一下Android5.0-6.0双卡适配的策略,其他方面的双卡适配方案跟4.4以前相比并无特别大的区别,之前我们已有专家对此进行过详细的总结,这里就不重复说明了。
从Android5.0开始,加入了对双卡的管理:
首先从数据库方面来看,其设计思路跟以前某平台是一样的,加入一个siminfo数据表到telephony.db来管理双卡的信息。数据表URL:content://telephony/siminfo
所有插入过的SIM卡的相关信息都会存储在这个表里,再看看表里的字段,这里截图只截了前面的几个比较关键的字段
从截图中可以看到我用红框所框的字段,这两个是尤为关键的字段,也是对我们在功能开发的极其关键的两个字段。下面简单说一下这两个字段的含义:
_id是表的主键,代表sim卡数据的索引值,从1开始,每次新插入的一张sim卡,数据表都会插入一行新数据,该行数据的_id值每次递增1(1,2,3,4,5……n这样递增),而不是更换一张卡更新一下对应卡槽的数据行的sim卡数据。
sim_id代表当前卡所在的卡槽值,只有0,1,-1三个值,0代表当前卡插在卡槽1当中(主卡槽),1代表当前卡插在卡槽2当中(副卡槽),-1代表曾经手机插入过此卡,现在已经移除。
其次从加入的API来看,Google加入了一系列管理双卡信息的类,其主要框架如下图所示:
这些API中除了SubscriptionManager之外主要都是供手机系统内部管理双卡信息使用,对上层功能开发的用途并不大,这里只供参考,不做详细讲解。
而SubscriptionManager这个类提供了一个关键的方法给上层开发来查询使用,这里只对它的这个关键方法进行一下简单分析。
方法名是:getSubId,获取指定卡槽sim卡的subId,对应的就是前面所讲的数据表中的_id值。 为什么说这个方法对上层开发来说非常关键,咱们先来看一下5.0-6.0的前面提到的要适配的两个接口是怎么设计的,大家就知道它为什么关键了,在这里先卖个关子。
Android5.0开始,不单加入了上面这些一系列管理双卡信息的API,也加入了获取各sim卡信息状态以及调用不同sim卡发短信的接口。
发送短信的方案
大家都知道平时咱们开发的发短信功能大多数是调用SmsManager类提供的sendTextMessage方法来发短信的,但是对于双卡手机来讲,按照之前常规的方式,只能是通过默认卡发出短信,那该怎么办呢?先来看一段代码截图:
这段代码的截图是SDK5.1中的SmsManager代码片段,英文注释就不详细翻译了,相信英文大神无处不在,我就不献丑了,大家自己翻译一下就好。
大家看到了什么?没错:
@param subId an SMS subscription id, typically accessed using
{@link android.telephony.SubscriptionManager}
@return the instance of the SmsManager associated with subId
这两行关键注释,进一步提炼一下,subId,subId,subId啊啊啊,关键词重复三遍。
到这里大家是不是豁然开朗,一目了然前面所卖的关子的原因:通过subId可以获取到对应sim卡的SmsManager实例,而subId正是通过SubscriptionManager的getSubId方法来获取的,有了对应sim卡的SmsManager实例之后,按照以前的方法通过调用该实例的sendTextMessage方法就可以实现通过不同卡发短信的需求了。
以上是双卡发短信适配的常规方式,先说到这里,但并没有完。
下面再说一下获取IMSI的适配方案:
理解了发短信的方案之后,下面这个方案也很好理解,直接看代码:
也是在TelephonyManager类里面新增了一个带参数的getSubscriberId方法,大家应该早已发现,参数也是subId,没错就是它。再一次证明了SubscriptionManager的getSubId方法的关键性。
适配方案就是通过phone服务获取TelephonyManager的实例,从而调用这个带参数的getSubscriberId方法来获取对应sim卡的IMSI,OK搞定。
说到这里,既然getSubId方法这么关键,咱们再返回来一睹SubscriptionManager的getSubId方法代码的风采:
由代码可见,getSubId方法也需要一个int类型的参数,通过参数名不难理解到,它需要的就是对应sim卡所在的卡槽值,也就是对应前面数据表中的sim_id,传入的合法有效值为0和1,就可以获取到对应的subId。 到目前为止该方法还是一个隐藏方法,外部不能直接调用,如果需要使用,可以通过反射的方式调用。另外还有一点需要注意的是,该方法返回的是一个int数组,使用的时候取它的第一个值就OK了,其实它里面也只有一个值,至于为什么要以数组形式返回而不是直接返回int,我也不是非常清楚,并表示不理解,有兴趣的朋友可以研究研究为什么要这样。
适配第三方 ROM
讲到这里,方案看上去貌似挺完美,但是真的是这样吗?甚至细心的朋友会怀疑:你这只是官方新增的标准API,称不上适配吧?
没错,事情并没有这么简单,这些只能算是5.0以后实现双卡需求的一个标准解决方案,只能应对极其少数几个有节操的大厂的ROM还可以,比如三星,做到现在发现5.0以后的三星ROM都很规矩(题外话)。
标准!标准!标准!有节操!有节操!有节操的大厂,那么针对那些大多数没节操的厂商该怎么办?
别急,方案还是有的,做到目前为止,发现会出现适配性问题的地方都是发生在SubscriptionManager和SmsManager身上,比如有的ROM没有了SubscriptionManager类,有的改名了,有点改方法名了等等。
那么怎么应对subId这个问题?
仔细想想,前面我说过subId就是数据表中_id的值,其实SubscriptionManager底层的实现也是通过查询这个表得到的。那么我们就管你改成什么了,果断通过传入卡槽值(sim_id)来查询sim卡的_id值不就OK了么。事实也证明此方案可行,具体代码如下:
通过这个方法来获取到subId值来提供给发短信和获取IMSI接口使用,完美解决了以上存在是适配性问题。
然而SmsManager如果也被修改了怎么办,继续看一下下面的代码截图:
通过这段代码,我们联想到是不是可以越过SmsManager,直接通过isms服务获取ISms接口的实例,通过调用ISms接口的sendTextForSubscriber方法来实现双卡发短信?答案是肯定的,我们看到方法的第一个参数是通过getSubscriptionId()方法获取到的一个值,没错,它也是我们前面提到的subId,都可以通过我们查询DB的方法来获取。
好在做到现在,ISms接口的sendTextForSubscriber方法还没有发现被修改的,相信也不会有厂商无节操到这种地步吧。估计他们也做不到。
最后补充一点,前面所用示例代码都是通过Android5.1获取的,其中5.0-6.0不同版本是有区别的:
- getSubscriberId各版本的区别:
subId在5.0传入的是long类型的参数,而5.1-6.0传入的是int类型的参数。 - sendTextForSubscriber各版本的区别:
subId在5.0传入的是long类型的参数,而5.1-6.0传入的是int类型的参数,并且6.0与5.1还有区别,就是方法参数列表末尾多出一个boolean类型的参数。
针对这几个小差别很好办,就是将查询到的subId给转换成对应的类型,6.0发短信方法的多传的boolean类型的参数传true值就OK了。
OK,讲到这里,真正的双卡适配才算讲完,如有更完美的方案,欢迎大家与我交流。
————————-
腾讯优测是备受客户信赖的移动云测试平台,为应用、游戏、H5混合应用的研发团队提供产品质量检测与问题解决服务。
不仅在线上平台提供app自动化测试、云真机远程操控与调试等多种质量检测工具,更为VIP客户配备了专家团队提供定制化综合测试解决方案。