前言
某天,QA给我提了一个Bug,说是包裹物流追踪的电话不能自动识别了。正常情况如下图所示:
在8.1的机器上,TextView突然不能自动识别电话号码了。
1.电话号码识别
Android SDK中,TextView提供了一个autoLink属性来帮助我们识别各种各样的链接,我们只需要将autoLink属性设置为"phone",就能轻易识别出电话号码。
这个属性在8.1之前的机器上是可以正常工作的,然而在8.1的机器上突然不可以了。那么原因是为何呢?
2.原因探究
通过查看TextView的源码,我们可以知道,系统是借助了一个名叫Linkify的类来识别电话号码的,具体可以查看文章——autoLink实现原理
在stackOverFlow上查找关于autolink无效的问题,android:autoLink for phone numbers doesn't always work
第一个回答中解释了为何autoLink会无效,我们可以了解到,autoLink跟手机的语言有关(英文或中文)。
首先看下Linkify的关键代码在8.1之前的实现:
private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
spec.start = match.start();
spec.end = match.end();
links.add(spec);
}
}
从上面代码我们可以看到,phoneUtil.findNumbers函数中传入了Locale.getDefault().getCountry(),即国家码,所以如果当前手机的地区变动,那么可能某些电话号码就不会被正确识别了。
然后再看下8.1上Linkify代码的实现:
private static void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s,
@Nullable Context context) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
final TelephonyManager tm = (context == null)
? TelephonyManager.getDefault()
: TelephonyManager.from(context);
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
tm.getSimCountryIso().toUpperCase(Locale.US),
Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
spec.start = match.start();
spec.end = match.end();
links.add(spec);
}
}
以上代码可以看到,原本传入地区码的地方,变成了getSimCountryIso()。那么看下这个函数的注解:
/**
* Returns the ISO country code equivalent of the current registered
* operator's MCC (Mobile Country Code).
* <p>
* Availability: Only when user is registered to a network. Result may be
* unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
* on a CDMA network).
*/
public String getNetworkCountryIso() {
return getNetworkCountryIsoForPhone(getDefaultPhone());
}
翻译过来就是获取当前运营商注册的国家码。而QA的测试机是没有插入sim卡的,那么这个函数返回的CountryCode绝对不是CN(具体返回啥由底层ROM决定)
在测试机插入sim卡之后,TextView的autolink又正确工作了,所以可以确定问题是出在这里。
3.总结
8.1之前的手机,TextView的电话号码匹配和当前手机的地区有关,切换语言时有可能导致号码匹配失效。
大于等于8.1的手机上,号码匹配和当前手机插入的sim卡有关,当使用国外的SIM卡或者不插入SIM卡时,有可能会导致号码匹配失效。