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在国内,未使用三方融合定位的情况下获取 经纬度信息.暂未获取位置的其他信息,这个有坑...以后扩展了再增加吧.

相关推荐
刘龙超1 小时前
如何应对 Android 面试官 -> 玩转 RxJava (基础使用)
android·rxjava
柿蒂2 小时前
从动态缩放自定义View,聊聊为什么不要把问题复杂化
android·ai编程·android jetpack
kerli2 小时前
kotlin协程系列:callbackFlow
android·kotlin
一枚小小程序员哈3 小时前
基于Android的车位预售预租APP/基于Android的车位租赁系统APP/基于Android的车位管理系统APP
android·spring boot·后端·struts·spring·java-ee·maven
诸神黄昏EX4 小时前
Android SystemServer 系列专题【篇四:SystemServerInitThreadPool线程池管理】
android
用户2018792831674 小时前
pm path 和 dumpsys package 的区别
android
是店小二呀4 小时前
【C++】智能指针底层原理:引用计数与资源管理机制
android·java·c++
DoubleYellowIce5 小时前
一次混淆XLog导致的crash分析记录
android
你听得到116 小时前
弹窗库1.1.0版本发布!不止于统一,更是全面的体验升级!
android·前端·flutter