Android 原生定位实现(替代融合定位收费,获取经纬度方案)

商用项目,用到了定位服务,也只用了定位服务,高德说免费额度超了需要收费了,每年最低五万.

哈哈哈资本家顿时坐不住了....

所以程序狗工作来了,自己想法去获取经纬度去(也不想想为啥用高德,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在国内,未使用三方融合定位的情况下获取 经纬度信息.暂未获取位置的其他信息,这个有坑...以后扩展了再增加吧.

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work3 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work3 天前
ch21 签名、校验与发版
kotlin
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui