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

相关推荐
Kapaseker4 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴4 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭14 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab15 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe20 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter