Android中的几种定位方式调用详解
目前,移动端大致通过三种方式来进行设备定位:GPS、基站、wifi。本文就详细的讲解一下这几种定位方式和实现方法。
前言
android中我们一般使用LocationManager来获取位置信息,这里面有四中provider:
public static final String NETWORK_PROVIDER = "network";
public static final String GPS_PROVIDER = "gps";
public static final String PASSIVE_PROVIDER = "passive";
public static final String FUSED_PROVIDER = "fused";
其中fused已经被废弃了,其它三种区别如下:
(1)GPS_PROVIDER:通过 GPS 来获取地理位置的经纬度信息;优点:获取地理位置信息精确度高;缺点:只能在户外使用,获取经纬度信息耗时,耗电;
(2)NETWORK_PROVIDER:通过移动网络的基站或者 Wi-Fi 来获取地理位置;优点:只要有网络,就可以快速定位,室内室外都可;缺点:精确度不高;
(3)PASSIVE_PROVIDER:被动接收更新地理位置信息,而不用自己请求地理位置信息。
PASSIVE_PROVIDER 返回的位置是通过其他 providers 产生的,可以查询 getProvider() 方法决定位置更新的由来,需要 ACCESS_FINE_LOCATION 权限,但是如果未启用 GPS,则此provider 可能只返回粗略位置匹配;
我们通常使用gps和network这两种方式。但是我们还可以通过其它方式获取位置信息,这篇文章就详细的讲解一下在android中几种获取定位的方式。
一、GPS定位
这个用的最普遍,可以获取上次定位,也可以监听变化,代码如下:
先定义需要的权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
调用代码
var locManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
var loc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if(loc != null){Log.e("gpslocation", loc.toString())toast(loc.toString())
}
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0F, object : LocationListener{override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}override fun onProviderEnabled(provider: String?) {}override fun onProviderDisabled(provider: String?) {}override fun onLocationChanged(location: Location?) {Log.e("gpslocation", location.toString())toast(location.toString())}
})
二、NETWORK定位
与gps定位代码基本一致,只不过将provider改成LocationManager.NETWORK_PROVIDER
三、AGPS定位
实际上是将上面两种定位结合起来,具体原理如下:
AGPS手机首先将本身的基站地址通过网络传输到位置服务器;
位置服务器根据该手机的大概位置传输与该位置相关的GPS辅助信息(包含GPS的星历和方位俯仰角等)到手机;
该手机的AGPS模块根据辅助信息(以提升GPS信号的第一锁定时间TTFF能力)接收GPS原始信号;
手机在接收到GPS原始信号后解调信号,计算手机到卫星的伪距(伪距为受各种GPS误差影响的距离),并将有关信息通过网络传输到位置服务器;
位置服务器根据传来的GPS伪距信息和来自其他定位设备(如差分GPS基准站等)的辅助信息完成对GPS信息的处理,并估算该手机的位置;
位置服务器将该手机的位置通过网络传输到定位网关或应用平台。
我的理解就是通过网络位置和位置服务器判断出最佳的卫星,减少了获取卫星信号的时间。因为网络位置获取很快,所以可以减少整体的定位时间。
AGPS并不是一种定位方式,只是一种优化方案,代码与GPS一样,只不过在设置中将定位模式设成AGPS。
上面是android自带的定位方式,我们还可以获取一些原始信息(比如基站信息、wifi信息),通过公开的接口来获取位置信息。下面几种方式就是使用原始信息通过API来获取位置信息。
四、基站定位
通过TelephonyManager我们可以拿到基站信息,再通过相关的api接口就能得到经纬度,但是基站定位精度很差。
基站信息包含如下:
- MCC,Mobile Country Code,移动国家代码(中国的为460);
- MNC,Mobile Network Code,移动网络号码(中国移动为00,中国联通为01);
- LAC,Location Area Code,位置区域码;
- CID,Cell Identity,基站编号,是个16位的数据(范围是0到65535)。
拿到这个信息后,我们可以通过一些公开的api服务拿到经纬度,如下:
http://www.google.com/loc/json google的,post请求,好像停用了
http://www.cellocation.com/interfac/目前可用,是免费的
实例:
http://api.cellocation.com:81/cell/?mcc=460&mnc=1&lac=4301&ci=20986&output=json
返回:
{"errcode":0, "lat":"40.00598145", "lon":"116.48539734", "radius":"937", "address":"北京市朝阳区来广营地区东湖渠;溪阳东路与屏翠东路路口东70米"}
接口参数:
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
mcc | int | 是 | mcc国家代码:中国代码 460 |
mnc | int | 是 | mnc网络类型:0移动,1联通(电信对应sid),十进制 |
lac | int | 是 | lac(电信对应nid),十进制 |
ci | int | 是 | cellid(电信对应bid),十进制 |
coord | string | 否 | 坐标类型(wgs84/gcj02/bd09),默认wgs84 |
output | string | 否 | 返回格式(csv/json/xml),默认csv |
另外还有很多提供这种接口和数据的平台,自己搜索即可
代码如下:需要权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_CARSE_LOCATION"/>
基站定位,这里只实现了GSM的,CDMA的有些许不同
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if(telManager.cellLocation is GsmCellLocation) {val cellLoc = telManager.cellLocation as GsmCellLocationif (cellLoc != null) {val operator = telManager.networkOperatorval mcc = operator.substring(0, 3).toInt()val mnc = operator.substring(3).toInt()val cid = cellLoc.cidval lac = cellLoc.lacvar sb = StringBuilder("http://api.cellocation.com:81/cell/?")sb.append("mcc=")sb.append(mcc)sb.append("&mnc=")sb.append(mnc)sb.append("&lac=")sb.append(lac)sb.append("&ci=")sb.append(cid)sb.append("&output=json")Log.e("tellocation", sb.toString())doAsync {val result = URL(sb.toString()).readText()Log.e("tellocation", result)}}
}
五、WIFI定位
wifi定位是通过WifiManager拿到wifi的信息,主要是wifi的BSSID(即mac地址)。然后通过一些api查询经纬度,比如 http://www.cellocation.com/interfac/
实例:
http://api.cellocation.com:81/wifi/?mac=00:87:36:05:5d:ea&output=json
返回:
{"errcode":0, "lat":"39.950008", "lon":"116.230049", "radius":"222", "address":"北京市海淀区四季青镇益园文创基地c区9号楼;南平庄中路与西平庄路路口西北561米"}
接口参数:
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
mac | int | 是 | WIFI热点的MAC地址(BSSID) |
coord | string | 否 | 坐标类型(wgs84/gcj02/bd09),默认wgs84 |
output | string | 否 | 返回格式(csv/json/xml),默认csv |
代码如下:需要权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
wifi定位
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
if(wifiManager.isWifiEnabled) {var mac = wifiManager.connectionInfo.bssidif(TextUtils.isEmpty(mac)) {/*** 当未链接wifi时,可以使用扫描到的wifi列表中找一个信号强度最好的。这里没进行比较,直接使用第一个了* ScanReuslt有三个字段比较重要:SSID是wifi名称,BSSID是wifi的mac,level则是信号强度(负数)* 注意结果中同一个SSID可能会有多个,如果需要链接wifi可以通过信号强度过滤出最好的来链接*/val scanlist = wifiManager.scanResultsfor(info in scanlist){Log.e("wifiinfo", info.toString())}if(scanlist.size > 0) {mac = wifiManager.scanResults[0].BSSID}}if(!TextUtils.isEmpty(mac)) {var sb = StringBuilder("http://api.cellocation.com:81/wifi/?")sb.append("mac=")sb.append(mac)sb.append("&output=json")Log.e("wifilocation", sb.toString())doAsync {val result = URL(sb.toString()).readText()Log.e("wifilocation", result)}}
}
六、混合定位
混合定位就是获取附近的wifi列表信息(包括信号强度)和附近的基站列表信息(包括信号强度),通过一些api获取经纬度。
这种方式相对于单一的基站和wifi定位要更精确一些。
获取附近的wifi列表在WIFI定位已经提到过了,通过WifiManager的getScanResults函数获取扫描到的wifi列表,其中level就是信号强度,可能需要做一下去重。
获取附近的基站列表则有些问题。
官方提供了一个方式,通过TelephonyManager的getNeighboringCellInfo函数获得,其中mRssi就是信号强度。
需要权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
获取附近基站信息
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val neighborCells = telManager.neighboringCellInfo
for(cell in neighborCells){//这里将rssi转化为dBmval level = -131 + 2 * cell.rssiLog.e("neighboringCellInfo", "cid:${cell.cid} lac:${cell.lac} rssi:$level")
}
但是实际使用时发现cid和lac都是-1。
检查NeighboringCellInfo的构造方法,如下:
public NeighboringCellInfo(int rssi, String location, int radioType) {// set default valuemRssi = rssi;mNetworkType = NETWORK_TYPE_UNKNOWN;mPsc = UNKNOWN_CID;mLac = UNKNOWN_CID;mCid = UNKNOWN_CID;// pad location string with leading "0"int l = location.length();if (l > 8) return;if (l < 8) {for (int i = 0; i < (8-l); i++) {location = "0" + location;}}// TODO - handle LTE and eHRPD (or find they can't be supported)try {// set LAC/CID or PSC based on radioTypeswitch (radioType) {case NETWORK_TYPE_GPRS:case NETWORK_TYPE_EDGE:mNetworkType = radioType;// check if 0xFFFFFFFF for UNKNOWN_CIDif (!location.equalsIgnoreCase("FFFFFFFF")) {mCid = Integer.parseInt(location.substring(4), 16);mLac = Integer.parseInt(location.substring(0, 4), 16);}break;case NETWORK_TYPE_UMTS:case NETWORK_TYPE_HSDPA:case NETWORK_TYPE_HSUPA:case NETWORK_TYPE_HSPA:mNetworkType = radioType;mPsc = Integer.parseInt(location, 16);break;}} catch (NumberFormatException e) {// parsing location errormPsc = UNKNOWN_CID;mLac = UNKNOWN_CID;mCid = UNKNOWN_CID;mNetworkType = NETWORK_TYPE_UNKNOWN;}}
可以看到只对GPRS和EDGE网络进行了处理,而3G、4G网络都是UNKNOWN_CID,即-1。说明这种方法不支持,已经过时。
官方还有另外一个方式,通过TelephonyManager的getAllCellInfo函数获得。这个函数要求minsdkverison必须在17及以上
需要权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
获取基站信息,这里只处理了LTE网络的
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val cells = telManager.allCellInfo
for(cell in cells){if(cell is CellInfoLte) {Log.e("celllist", "cid:${cell.cellIdentity.ci} lac:${cell.cellIdentity.tac} dbm:${cell.cellSignalStrength.dbm} isRegistered:${cell.isRegistered}")}
}
得到的信息如下:
E/celllist: cid:3912981 lac:4154 dbm:-87 isRegistered:true
E/celllist: cid:2147483647 lac:2147483647 dbm:-101 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-102 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-108 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-101 isRegistered:false
其中第一个是我们正在使用的基站,可以看到正常的返回了信息,而其余的则返回默认的信息(Integer.MAX_VALUE)
说明这个方法也只能拿到当前使用的基站信息。而且据网上的说法,当使用2G网络,getAllCellInfo得到的是NULL。
这样目前没有更好的方式获取多个基站信息了。
当我们拿到附近的基站信息和wifi信息,可以通过http://www.cellocation.com/interfac/提供的混合定位接口查询位置信息,与上面类似,这里不细说了。
总结
一般情况下,我们使用系统提供的LocationManager即可获取位置信息,方便简单。如果我们有自己的基站或wifi信息库,也可以获取相关源信息通过接口来实现个性化服务。
相关文章:
Android中的几种定位方式调用详解
目前,移动端大致通过三种方式来进行设备定位:GPS、基站、wifi。本文就详细的讲解一下这几种定位方式和实现方法。 前言 android中我们一般使用LocationManager来获取位置信息,这里面有四中provider: public static final Strin…...

【软件测试】接口调不通排查分析+常遇面试题总结
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、接口调不通&am…...
c++基础学习第三天(指针,结构体)
c基础学习第三天(指针,结构体) 文章目录 1、指针1.1、指针的基本概念1.2、指针变量的定义和使用1.3、 指针所占内存空间1.4、空指针和野指针1.5、 const修饰指针1.5.1、const修饰指针-常量指针1.5.2、const修饰常量-指针常量1.5.3、const即修…...
【数仓】zookeeper软件安装及集群配置
相关文章 【数仓】基本概念、知识普及、核心技术【数仓】数据分层概念以及相关逻辑【数仓】Hadoop软件安装及使用(集群配置)【数仓】Hadoop集群配置常用参数说明 一、环境准备 准备3台虚拟机 Hadoop131:192.168.56.131Hadoop132ÿ…...

Qt 实现橡皮擦拭显示图片
1.简介 在一些游戏中看见类似解密破案的效果,使用手触摸去擦拭图片上的灰尘,然后显示最终的图片,所以也想试试Qt实现的效果。大家有自己想做的效果,都可以尝试。 以下是效果展示图。 可以控制橡皮擦的大小,进行擦拭…...
Vue3+Element-Plus中ELMessage样式丢失处理
Vu3Element-Plus项目中,element-plus使用按需引入有时会出现样式失效和在vscode中使用会报错[找不到名称“ElMessage”。ts(2304)]错误 ELMessage弹框样式丢失处理方法 使用按需引入就不能手动再引入 import { ElMessage } from "element-plus";ElMessage.success…...

97 spring 中的泛型类型注入
前言 呵呵 同样是 最近同事碰到的一个问题 他不太懂 英语, 看到的说明是 缺少一个 RedisTemplate 的实例, 但是找到了一个 RedisTemplate 的实例 呵呵 和我这里 spring 版本似乎是不太一样, 错误信息 有一些差异 以下环境基于 jdk8 spring-5.0.4-RELEASE 测试用例 BeanCon…...

C++设计模式
单例模式 单例模式保证一个类只能创建一个对象,并提供全局访问点。通常用于全局共享例如日志、数据库连接池等。 Lazy Initialization 优点:需要时才初始化,节省空间 缺点:线程不安全 class Singleton{ private:static Singlet…...

反向代购业务系统|无货源代购中国商品|反向海淘代购系统
什么是淘宝代购 淘宝代购是近年兴起的一种购物模式,是帮国外客户购买中国商品。主要是通过万邦科技的外贸代购模式,把淘宝、 天猫等电商平台的全站商品通过API接入到你的网站上,瞬间就可以架设一个有数亿产品的大型网上商城,而且…...

Linux 进程间通信
目录 管道 匿名管道(pipe) 有名管道(fifo) 小结 共享内存 消息队列 信号量 System V IPC的结构设计 Posix与System V的关系 管道 匿名管道(pipe) 我们知道,在Linux中通过fork创建的子…...

hippy 调试demo运行联调-mac环境准备篇
适用对于终端编译环境不熟悉的人看,仅mac端 hippy 调试文档官网地址 前提:请使用node16 联调预览效果图: 编译iOS Demo环境准备 未跑通,待补充 编译Android Demo环境准备 1、正常安装Android Studio 2、下载Android NDK&a…...

【golang】go module依赖的git tag被覆盖 如何处理 | 因测试产生大量的git tag 如何清除 最佳实践
一、场景 当我们把本地和远程git仓库的 tag全部删除,我们另外的项目依赖于这个被删除tag无法更新版本 如何处理? 如上图: 这里我创建了一个 v0.0.1 的tag,然后删除了这个tag,然后又创建了一个新的 v0.0.1的tag…...
Spring Cloud原理详解
Spring Cloud 是基于 Spring Boot 的微服务架构开发工具包,旨在帮助开发人员快速构建分布式系统中的一些常见模式,例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、领导选举、分布式会话和集群状态。Spring Cloud 是 Spring 生态系…...

力扣76. 最小覆盖子串(滑动窗口)
Problem: 76. 最小覆盖子串 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义两个map集合need和window(以字符作为键,对应字符出现的个数作为值),将子串t存入need中; 2.定义左右指针left、right均指向0ÿ…...

使用华为云云函数functiongraph
之前使用腾讯云serverless,但是突然开始收费了。所以改用functiongraph 首先登陆华为云。 目录 1.登录华为云 2.在控制台找到functiongraph并开通 3.添加依赖包: 3.1 制作依赖包 3.2引入依赖包 4.发送请求 4.1直接发送 4.1.1uri 4.1.2 请求头…...

Android logcat系统
一 .logcat命令介绍 android log系统: logcat介绍 : logcat是android中的一个命令行工具,可以用于得到程序的log信息. 二.C/Clogcat访问接口 Android系统中的C/C日志接口是通过宏来使用的。在system/core/include/android/log.h定义了日志的级别: /…...
android 使用协程CoroutineScope 实现定时器
满足延迟执行、立即执行,每次任务间隔时长,总时长的任务 使用1 class TimeViewModel:Viewmodel(){//测试延迟5秒开始执行任务,然后每隔1秒执行1次,总执行时间60秒fun testTime(){var startTime System.currentTimeMillis()log(…...

【algorithm】算法基础课---排序算法(附笔记 | 建议收藏)
🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:AcWing算法学习笔记 💬总结:希望你看完…...

UnityShader——09数学知识3
方阵 行与列数量相等的矩阵,n*n阶矩阵 对角矩阵 当对角线以外的矩阵内元素全为0,则称之为对角矩阵,对角矩阵的前提是必须是方阵 单位矩阵 对角线元素全为1,其余元素全为0,属于对角矩阵的一部分 矩阵和向量 把1 * n阶矩阵称…...

langchain学习笔记(九)
RunnableBranch: Dynamically route logic based on input | 🦜️🔗 Langchain 基于输入的动态路由逻辑,通过上一步的输出选择下一步操作,允许创建非确定性链。路由保证路由间的结构和连贯。 有以下两种方法执行路由 1、通过Ru…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...