
商用项目,用到了定位服务,也只用了定位服务,高德说免费额度超了需要收费了,每年最低五万.
哈哈哈资本家顿时坐不住了....
所以程序狗工作来了,自己想法去获取经纬度去(也不想想为啥用高德,WC!!!!)
所以开始了
定位:原生API经纬度获取
1.常见定位方式以及国内厂商定位支持
- GPS/北斗/伽利略
- 网络定位(网络/基站/wifi)
- Android 缓存的位置信息
- 融合定位(google/hms/高德百度等三方)
1.1. Android 常用定位方式
方式 | 描述 | 数据来源 | 优点 | 缺点 |
---|---|---|---|---|
GPS / GNSS/北斗/... | 卫星定位 | GPS、北斗、GLONASS、Galileo | 高精度(米级) | 户外可用,室内弱,耗电高 |
网络定位(Cell/Wi-Fi) | 基站 + Wi-Fi | 移动基站 + Wi-Fi 定位数据库 | 省电,室内可用 | 精度低(几十米到几百米) |
FusedLocationProviderClient(融合定位) | 多种定位融合 | 卫星 + 基站 + Wi-Fi + 传感器 | 高精度、省电、易用 | 依赖 Google Play Services / HMS,无法单独控制卫星类型 |
高德/腾讯 SDK 定位 | 第三方融合定位 | GPS/北斗 + 基站 + Wi-Fi | 国内适配好,可商用 | 依赖 SDK,App 包体积增大,收费 |
GnssStatus / GnssMeasurements | 原始卫星信息 | GNSS 硬件 | 可获取卫星类型(北斗/GPS)和信号 | 开发复杂,需要自己计算定位 |
1.2.国内 vs 国外定位差异
特点 | 国内 | 国外 |
---|---|---|
卫星系统 | GPS + 北斗 + GLONASS + Galileo | GPS + GLONASS + Galileo |
高精度融合定位 | 国内 App 多用高德/腾讯 SDK 或 FusedLocationProviderClient(国产手机可能没有 Google 服务,需要高德/腾讯) | Google FusedLocationProviderClient(默认 GPS+GLONASS+Wi-Fi) |
网络辅助 | 基站/Wi-Fi 定位稳定,国内数据库完善 | 基站/Wi-Fi 定位也可用,但覆盖密度因国家而异 |
设备差异 | 华为/Huawei → HMS Location Kit,支持北斗卫星信息;小米/OPPO/Vivo → Google 服务少,需高德/腾讯 SDK | 主要依赖 Google 服务,FusedLocationProviderClient 可用 |
1.3.各厂商定位方案及特点
厂商 | 定位 API | 特点 | 优缺点 |
---|---|---|---|
华为 | HMS Location Kit | 高精度,北斗支持,获取卫星信息 | 无 Google 服务设备可用,国内兼容好 |
小米/OPPO/Vivo | 高德 / 腾讯 SDK 或原生 LocationManager | 融合北斗/GPS/Wi-Fi | 无 Google 服务也可用,但原生 API 开发复杂 |
Google Pixel / 海外设备 | FusedLocationProviderClient | 高精度,融合定位 | 依赖 Google Play Services |
原生 Android(通用) | LocationManager + GnssStatus | 可低级获取卫星信息 | 开发复杂,定位逻辑需要自己管理 |
2.Android原生代码实现获取定位信息
项目要求:
- 国内
- 非三方(非融合定位方式(收费))
- 经纬度
- 城市信息(
todo 哈哈后台说通过ip获取,目测坑很大,但是有人接所以无所谓了
)
特么就是为了省钱....高德最低套餐5W
实现:
- 网络形式获取
- GPS形式获取
- Android维护的定位缓存
简介:
大致思路是优先获取网络定位和GPS的定位信息,指定时间内不返回数据直接返回Android缓存的定位信息.
网络定位和GPS的定位 并行执行,缓存定位兜底.
代码实现:
权限配置
xml
<!--需要动态申请-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
核心代码
kotlin
package com.wkq.util
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
/**
*
*@Author: wkq
*
*@Time: 2025/8/27 9:28
*
*@Desc: 获取定位地址
*/
class NativeLocationHelper(
private val context: Context, private val timeout: Long = 5000L, // 默认10秒超时
private val callback: (location: Location) -> Unit
) : LocationListener {
private val locationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private var isStartLocation = false
private val handler = Handler(Looper.getMainLooper())
private var timeoutRunnable: Runnable? = null
@SuppressLint("MissingPermission")
fun startLocation() {
if (isStartLocation) return
if (!hasPermission()) {
throw SecurityException("需要 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限")
}
// 网络定位
getNetLocation()
// GPS 定位
getGpsLocation()
//超时返回缓存定位
timeoutRunnable = Runnable {
getCacheLocation()
}
// 设置超时回调
handler.postDelayed(timeoutRunnable!!, timeout)
isStartLocation = true
}
fun release() {
locationManager.removeUpdates(this)
timeoutRunnable?.let {
handler.removeCallbacks(it)
}
}
@SuppressLint("MissingPermission")
fun getNetLocation() {
if (!hasPermission()) return
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 5000L, 5f, this
)
}
}
@SuppressLint("MissingPermission")
fun getGpsLocation() {
if (!hasPermission()) return
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 5000L, 5f, this
)
}
}
fun getCacheLocation() {
val lastLocation = getLastKnownLocation()
lastLocation?.let { callback(lastLocation) }
}
private fun hasPermission(): Boolean {
return ActivityCompat.checkSelfPermission(
context, Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
context, Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
@SuppressLint("MissingPermission")
private fun getLastKnownLocation(): Location? {
val providers = locationManager.getProviders(true)
var bestLocation: Location? = null
for (provider in providers) {
val l = locationManager.getLastKnownLocation(provider) ?: continue
if (bestLocation == null || l.accuracy < bestLocation.accuracy) {
bestLocation = l
}
}
return bestLocation
}
override fun onLocationChanged(location: Location) {
if (isStartLocation) {
timeoutRunnable?.let {
handler.removeCallbacks(it)
}
val latitude: Double
val longitude: Double
if (location == null) {
latitude = 39.9042 // 北京
longitude = 116.4074
} else {
latitude = location.getLatitude()
longitude = location.getLongitude()
}
Log.d("LocationManager", "纬度:" + latitude + ", 经度:" + longitude)
callback(location)
isStartLocation = false
}
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
注意:
只处理了经纬度信息
- 注意申请定位权限
- 三种定位信息的顺序问题
不是精准的定位信息
- 暂未处理国外定位
- 暂未处理位置的其他信息
总结
简单的实现了Android在国内,未使用三方融合定位的情况下获取 经纬度信息.暂未获取位置的其他信息,这个有坑...以后扩展了再增加吧.