Android 解析手机号码归属地
本文主要介绍下android 项目中解析手机号码归属地的一种实现方式.
1: libphonenumber库
https://mvnrepository.com/artifact/com.googlecode.libphonenumber/geocoder/3.8
仓库地址.
https://github.com/google/libphonenumber/
目前是在kotlin工程中接入.接入方式build.gradle.kts中添加依赖:
dependencies {
implementation("com.googlecode.libphonenumber:geocoder:3.8")
.....
}
由于新工程,编译下载比较慢, 我们可以更新下maven源. 可以在settings.gradle.kts中增加aliyun,huawei的.具体代码如下:
pluginManagement {
repositories {
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://repo.huaweicloud.com/repository/maven") }
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://repo.huaweicloud.com/repository/maven") }
google()
mavenCentral()
}
}
rootProject.name = "PhoneParser"
include(":app")
完成后,我们编译下就可以具体的开始解析操作了.
下面是简单的实现代码:
public class PhoneUtils {
// 初始化 PhoneNumberUtil 实例,用于解析、格式化、判断手机号有效性等操作
private static final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
// 初始化 PhoneNumberOfflineGeocoder 实例,用于根据手机号码获取归属地描述(如省份和城市)
private static final PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
public static String parseAndGetLocation(String phoneNumber) {
try {
// 解析手机号,默认使用中国大陆地区规则("CN")进行解析
Phonenumber.PhoneNumber numberProto = phoneUtil.parse(phoneNumber, "CN");
// 检查解析后的号码是否有效
if (!phoneUtil.isValidNumber(numberProto)) {
return "无效的手机号";
}
// 获取号码归属地描述信息,使用中文环境输出结果(例如:"浙江省杭州市")
return geocoder.getDescriptionForNumber(numberProto, Locale.CHINA);
} catch (NumberParseException e) {
return "解析失败";
}
}
}
调用:
val phoneNumber = "+86183xxxxxxxx" // 示例号码
val location = PhoneUtils.parseAndGetLocation(phoneNumber)
Log.d("PhoneParser", "parseAndGetLocation: $location")
运行后可在控制台打印对应的输出结果.
2: 遇到的问题.
下面是遇到的问题, 遇到某些手机号码的归属地与在线以及拨打出去显示的归属地不一致.
简单看了下libphonenumber库获取手机号码归属地的逻辑, 可以看到每次传入的手机号, 根据方法转换得到对应的countryCode,从而根据此拿到对应路径下的指定文件.
查看源码后,我们可以看到项目对应的resources下找到对应的86.txt
https://github.com/google/libphonenumber/tree/master/resources/geocoding/zh
可以看下对应的文件内容如下:
# Copyright (C) 2011 The Libphonenumber Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generated from:
# Fixedline mappings(10 and [2-9]*) are generated from Internal statistical data (2011-06-01)
# Mobile mappings(13*,14*,15*,17*,18*) are contributed by farmer1992@gmail.com
8610|北京市
861300000|山东省济南市
861300001|江苏省常州市
861300002|安徽省巢湖市
861300006|江苏省南京市
861300008|湖北省武汉市
861300010|北京市
861300011|北京市
861300012|天津市
861300013|天津市
861300014|天津市
861300015|山东省淄博市
861300016|山东省烟台市
861300017|山东省济南市
861300018|天津市
861300019|天津市
861300020|上海市
861300021|上海市
861300022|上海市
861300023|上海市
861300024|上海市
861300025|江苏省南京市
861300026|江苏省南京市
861300027|山东省烟台市
861300028|江苏省南京市
861300029|江苏省南京市
86130003|江苏省无锡市
861300030|江苏省南京市
861300031|江苏省南京市
861300032|江苏省南京市
861300040|广东省广州市
861300041|广东省广州市
861300042|广东省广州市
861300043|广东省广州市
861300044|广东省广州市
861300045|北京市
861300046|北京市
861300047|北京市
861300048|北京市
861300049|北京市
......
可以看到该文件以86开头, 以手机号码的前几位来判断地区.
根据对应的手机号码是因为该文件中前四位判断是一个市,前五位判断成另一个导致.
3: 本地读取归属地文件
基于现有的86.txt 文件, 放置assets下,自己解析该文件获取归属地.
public class PrefixMatcher {
// 加载文件内容,构建前缀映射表
public static Map<String, String> loadPrefixes(Context context){
Map<String, String> prefixMap = new HashMap<>();
try {
InputStream is = context.getAssets().open("86.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split("\\|");
if (parts.length == 2) {
prefixMap.put(parts[0], parts[1]);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
return prefixMap;
}
// 查找最长匹配的前缀
public static String findLongestMatch(Context context,String number) {
Map<String, String> prefixMap = loadPrefixes(context);
for (int i = number.length(); i > 0; i--) {
String prefix = number.substring(0, i);
if (prefixMap.containsKey(prefix)) {
return prefixMap.get(prefix);
}
}
return "未知地区";
}
}
匹配规则为匹配到最长的前缀则返回归属地.