TextView autoLink="phone" 在8.1机器上无效

前言

某天,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卡时,有可能会导致号码匹配失效。

推荐阅读更多精彩内容