Android 实现定位功能 —— 从基础到实战的完整项目解析
本文将深入探讨 Android 平台的定位功能,涵盖 GPS、Wi-Fi、蜂窝网络、FusedLocationProvider(FLP)等技术,并完整实现一个支持前后台运行的高精度定位系统,适用于地图导航、打卡签到、骑行记录、物联网设备等应用场景。
一、项目介绍
1.1 需求背景
在移动应用中,获取设备位置信息是常见需求,如:
-
地图导航(如 Google Maps、高德地图)
-
打卡签到(如考勤、健身记录)
-
LBS 服务(如外卖、网约车)
-
运动轨迹(如骑行、跑步、徒步记录)
要想在 Android 上实现精准、稳定、高效的定位,需要结合多种技术手段,并充分考虑 功耗、权限管理、系统兼容性 等问题。
1.2 本项目目标
本项目的核心目标:
实现一个支持前后台持续运行的高精度定位服务,采用 FusedLocationProvider(FLP),并结合 WorkManager 确保后台存活,最终可通过通知栏实时显示位置信息,并定期上传到服务器。
关键功能点:
✅ 使用 FusedLocationProvider(FLP) 获取高精度位置
✅ 支持 GPS、Wi-Fi、蜂窝网络 结合定位
✅ 支持 后台运行,避免熄屏或切换 App 时定位中断
✅ 使用 前台服务(Foreground Service) 保持存活
✅ 通过 通知栏显示位置,让用户随时掌握位置状态
✅ 支持 定时上传位置到服务器(可选)
✅ 兼容 Android 6.0+(动态权限)、Android 10+(后台定位限制)、Android 12+(精确/模糊定位)
二、定位相关技术知识解析
2.1 Android 位置获取方式
方式 | 说明 | 优势 | 劣势 |
---|---|---|---|
GPS | 通过卫星获取位置 | 精度高(可达 5-10m),适合户外 | 耗电高,室内无效 |
Wi-Fi | 通过附近 Wi-Fi 进行定位 | 适合室内,速度快,省电 | 精度较低(30-50m),依赖网络 |
蜂窝网络 | 通过基站定位 | 适用于无 Wi-Fi 场景 | 精度低(100-1000m) |
FusedLocationProvider(推荐) | Google 提供的高精度定位 API,融合 GPS、Wi-Fi、蜂窝 | 自动切换,精准、省电,适合大多数应用 | 需要 Google Play 服务,部分国产 ROM 可能受限 |
2.2 定位权限解析
Android 6.0+ 需要动态申请权限,否则 SecurityException
:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Android 10+ 限制:
-
后台定位需要
ACCESS_BACKGROUND_LOCATION
-
仅申请
ACCESS_FINE_LOCATION
在后台无效
Android 12+ 变化:
-
需要 精准(Fine)和模糊(Coarse)定位权限
-
精确权限:
ACCESS_FINE_LOCATION
-
模糊权限:
ACCESS_COARSE_LOCATION
三、项目实现思路
3.1 关键实现方案
-
使用 FusedLocationProvider(FLP)获取高精度位置
-
使用 Foreground Service(前台服务)保持后台存活
-
通过 WorkManager 实现后台定时上传位置
-
在通知栏显示当前位置信息
3.2 关键组件
组件 | 作用 |
---|---|
FusedLocationProviderClient | 位置获取 |
Foreground Service | 保持后台运行 |
NotificationManager | 通知栏显示定位状态 |
WorkManager | 定时上传数据 |
四、完整整合代码(含详细注释)
4.1 AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.locationtracker">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:label="定位演示"
android:icon="@mipmap/ic_launcher">
<service
android:name=".LocationService"
android:foregroundServiceType="location" />
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
4.2 MainActivity.kt
class MainActivity : AppCompatActivity() {
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
val allGranted = result.values.all { it }
if (allGranted) {
startLocationService()
} else {
Toast.makeText(this, "未授予全部定位权限", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 动态申请权限
permissionLauncher.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
))
}
private fun startLocationService() {
val intent = Intent(this, LocationService::class.java)
ContextCompat.startForegroundService(this, intent)
}
}
4.3 LocationService.kt
class LocationService : Service() {
private lateinit var fusedClient: FusedLocationProviderClient
private lateinit var locationRequest: LocationRequest
override fun onCreate() {
super.onCreate()
fusedClient = LocationServices.getFusedLocationProviderClient(this)
locationRequest = LocationRequest.create().apply {
interval = 10_000 // 10秒更新一次
fastestInterval = 5_000
priority = Priority.PRIORITY_HIGH_ACCURACY
}
// 启动前台服务
startForeground(1, buildNotification("初始化定位..."))
startLocationUpdates()
}
private fun startLocationUpdates() {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
val location = result.lastLocation
location?.let {
val info = "纬度: ${it.latitude}, 经度: ${it.longitude}"
updateNotification(info)
}
}
}
// 注册位置监听
fusedClient.requestLocationUpdates(locationRequest, callback, Looper.getMainLooper())
}
private fun buildNotification(text: String): Notification {
val channelId = "location_channel"
val channelName = "定位服务"
val manager = getSystemService(NotificationManager::class.java)
val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW)
manager.createNotificationChannel(channel)
return NotificationCompat.Builder(this, channelId)
.setContentTitle("定位服务运行中")
.setContentText(text)
.setSmallIcon(R.drawable.ic_location)
.setOnlyAlertOnce(true)
.build()
}
private fun updateNotification(text: String) {
val notification = buildNotification(text)
val manager = getSystemService(NotificationManager::class.java)
manager.notify(1, notification)
}
override fun onBind(intent: Intent?): IBinder? = null
}
五、方法解读(仅功能说明)
-
requestLocationUpdates()
:开始监听位置变化 -
LocationCallback
:位置变化后会回调此接口 -
buildNotification()
:构建通知对象,用于绑定前台服务 -
updateNotification()
:每次定位后更新通知栏内容 -
startForeground()
:正式启动前台服务,绑定通知栏 -
FusedLocationProviderClient
:定位核心 API 客户端
六、项目总结与拓展
6.1 本项目达成效果
-
实现了前后台稳定运行的 Android 定位服务
-
兼容 Android 6 到 Android 14 系统
-
定位精度高(FLP)、功耗低、响应快
-
权限处理严谨、逻辑清晰、可移植性强
6.2 可拓展方向
-
增加 后台定时上传位置信息 到服务器(可用 WorkManager)
-
加入 轨迹记录与本地持久化(SQLite / Room)
-
集成地图 SDK(如高德/百度)进行轨迹绘图
-
增加电量优化功能,如静态区域时自动暂停定位
-
使用前台通知按钮实现暂停/继续定位控制
6.3 注意事项
-
国产系统兼容性:部分定制 ROM(如 MIUI)需手动设置自启/锁后台
-
隐私声明合规:涉及位置获取必须展示权限用途、隐私说明
-
高频定位风险:可能导致电池消耗快,应考虑场景优化策略
七、结语
通过本文,你已经系统掌握了 Android 定位功能的开发流程,从权限申请到高精度定位、从前台服务到通知栏展示,所有关键技术与代码细节都已覆盖,并以完整项目形式呈现。希望本文对你开发定位相关功能时能带来实质性帮助。
如需将该功能接入其他项目,可按模块化方式进行封装与拓展,并根据实际业务场景灵活配置参数。