11 changed files with 707 additions and 384 deletions
@ -1,7 +1,12 @@ |
|||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.yunqiinnovation.location_service"> |
package="com.yunqiinnovation.location_service"> |
||||
|
<!-- 必须:精确定位权限(包含GPS) --> |
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> |
<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_COARSE_LOCATION" /> |
||||
|
<!-- Android 10+ 后台定位需额外声明(若需要后台持续定位) --> |
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> |
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> |
||||
|
<!-- 辅助:获取Wi-Fi/基站信息提升网络定位精度 --> |
||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> |
||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||
</manifest> |
</manifest> |
||||
|
|||||
@ -0,0 +1,499 @@ |
|||||
|
package com.yunqiinnovation.location_service |
||||
|
|
||||
|
import android.Manifest |
||||
|
import android.app.Activity |
||||
|
import android.content.Context |
||||
|
import android.content.pm.PackageManager |
||||
|
import android.location.* |
||||
|
import android.os.Build |
||||
|
import android.os.Bundle |
||||
|
import android.os.Handler |
||||
|
import android.os.Looper |
||||
|
import android.util.Log |
||||
|
import androidx.core.app.ActivityCompat |
||||
|
import androidx.core.content.ContextCompat |
||||
|
import kotlinx.coroutines.CoroutineScope |
||||
|
import kotlinx.coroutines.Dispatchers |
||||
|
import kotlinx.coroutines.launch |
||||
|
import java.util.* |
||||
|
|
||||
|
object NativeLocationService { |
||||
|
private const val TAG = "NativeLocationService" |
||||
|
private const val PERMISSION_REQUEST_CODE = 1002 |
||||
|
|
||||
|
// 高精度定位阈值 |
||||
|
private const val HIGH_ACCURACY_THRESHOLD = 50.0f // 只接受50米以内的高精度定位 |
||||
|
private const val LOCATION_TIMEOUT_MS = 60000L // 60秒超时 |
||||
|
private const val MIN_TIME_BETWEEN_UPDATES = 5000L // 5秒最小更新间隔 |
||||
|
private const val MIN_DISTANCE_CHANGE_FOR_UPDATES = 10f // 10米最小距离变化 |
||||
|
|
||||
|
private var appContext: Context? = null |
||||
|
private var locationManager: LocationManager? = null |
||||
|
private var initialized = false |
||||
|
private val serviceCallbacks = mutableListOf<LocationCallback>() |
||||
|
private var cachedLocationData: LocationData? = null |
||||
|
private var locationTimeoutHandler: Handler? = null |
||||
|
private var locationStartTime: Long = 0 |
||||
|
private var gpsLocationListener: LocationListener? = null |
||||
|
private var networkLocationListener: LocationListener? = null |
||||
|
|
||||
|
// 定位回调接口 |
||||
|
interface LocationCallback { |
||||
|
fun onLocationSuccess(latitude: Double, longitude: Double) |
||||
|
fun onLocationError(errorCode: Int, errorMessage: String) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化原生定位服务 |
||||
|
*/ |
||||
|
fun initialize(context: Context) { |
||||
|
if (initialized) { |
||||
|
Log.d(TAG, "原生定位服务已初始化") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
appContext = context.applicationContext |
||||
|
locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager |
||||
|
|
||||
|
if (locationManager == null) { |
||||
|
Log.e(TAG, "无法获取LocationManager") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
initialized = true |
||||
|
Log.d(TAG, "原生定位服务初始化完成") |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查GPS是否可用 |
||||
|
*/ |
||||
|
private fun isGpsEnabled(): Boolean { |
||||
|
return locationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER) ?: false |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查网络定位是否可用 |
||||
|
*/ |
||||
|
private fun isNetworkEnabled(): Boolean { |
||||
|
return locationManager?.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ?: false |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取位置信息(异步) |
||||
|
*/ |
||||
|
fun requestLocation(callback: LocationCallback) { |
||||
|
val ctx = appContext |
||||
|
if (ctx == null) { |
||||
|
callback.onLocationError(-1, "定位服务未初始化") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (!checkPermissions(ctx)) { |
||||
|
callback.onLocationError(-2, "没有定位权限") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (!initialized) { |
||||
|
callback.onLocationError(-3, "定位服务未初始化") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 检查定位服务是否可用 |
||||
|
if (!isGpsEnabled() && !isNetworkEnabled()) { |
||||
|
callback.onLocationError(-4, "GPS和网络定位都不可用,请检查设置") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 优先返回缓存的位置 |
||||
|
cachedLocationData?.let { cached -> |
||||
|
val currentTime = System.currentTimeMillis() |
||||
|
// 如果缓存时间在5分钟内,直接返回 |
||||
|
if (currentTime - cached.timestamp < 5 * 60 * 1000) { |
||||
|
callback.onLocationSuccess(cached.latitude, cached.longitude) |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 添加到回调列表 |
||||
|
serviceCallbacks.add(callback) |
||||
|
|
||||
|
// 记录定位开始时间 |
||||
|
locationStartTime = System.currentTimeMillis() |
||||
|
|
||||
|
// 启动超时计时器 |
||||
|
startLocationTimeout() |
||||
|
|
||||
|
// 使用协程在后台执行定位 |
||||
|
CoroutineScope(Dispatchers.IO).launch { |
||||
|
try { |
||||
|
startLocationUpdates() |
||||
|
} catch (e: Exception) { |
||||
|
stopLocationTimeout() |
||||
|
callback.onLocationError(-5, "启动定位失败: ${e.message}") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始位置更新 |
||||
|
*/ |
||||
|
private fun startLocationUpdates() { |
||||
|
val ctx = appContext ?: return |
||||
|
val locManager = locationManager ?: return |
||||
|
|
||||
|
// 检查权限 |
||||
|
if (ActivityCompat.checkSelfPermission( |
||||
|
ctx, |
||||
|
Manifest.permission.ACCESS_FINE_LOCATION |
||||
|
) != PackageManager.PERMISSION_GRANTED && |
||||
|
ActivityCompat.checkSelfPermission( |
||||
|
ctx, |
||||
|
Manifest.permission.ACCESS_COARSE_LOCATION |
||||
|
) != PackageManager.PERMISSION_GRANTED |
||||
|
) { |
||||
|
Log.e(TAG, "没有定位权限") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
Log.d(TAG, "=== 启动混合定位模式 ===") |
||||
|
Log.d(TAG, "网络定位可用: ${isNetworkEnabled()}") |
||||
|
Log.d(TAG, "GPS定位可用: ${isGpsEnabled()}") |
||||
|
|
||||
|
// 优先启动网络定位 |
||||
|
if (isNetworkEnabled()) { |
||||
|
Log.d(TAG, "🌐 启动网络定位(优先)") |
||||
|
|
||||
|
networkLocationListener = object : LocationListener { |
||||
|
override fun onLocationChanged(location: Location) { |
||||
|
Log.d(TAG, "🌐 网络定位更新: 精度=${location.accuracy}米") |
||||
|
handleLocationResult(location, "NETWORK") |
||||
|
} |
||||
|
|
||||
|
override fun onProviderEnabled(provider: String) { |
||||
|
Log.d(TAG, "网络提供者已启用: $provider") |
||||
|
} |
||||
|
|
||||
|
override fun onProviderDisabled(provider: String) { |
||||
|
Log.d(TAG, "网络提供者已禁用: $provider") |
||||
|
} |
||||
|
|
||||
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { |
||||
|
Log.d(TAG, "网络状态变化: $provider, status: $status") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
locManager.requestLocationUpdates( |
||||
|
LocationManager.NETWORK_PROVIDER, |
||||
|
MIN_TIME_BETWEEN_UPDATES, |
||||
|
MIN_DISTANCE_CHANGE_FOR_UPDATES, |
||||
|
networkLocationListener!!, |
||||
|
Looper.getMainLooper() |
||||
|
) |
||||
|
Log.d(TAG, "✅ 网络定位已启动") |
||||
|
} |
||||
|
|
||||
|
// GPS作为补充定位 |
||||
|
if (isGpsEnabled()) { |
||||
|
Log.d(TAG, "🛰️ 启动GPS定位(补充)") |
||||
|
|
||||
|
gpsLocationListener = object : LocationListener { |
||||
|
override fun onLocationChanged(location: Location) { |
||||
|
Log.d(TAG, "🛰️ GPS定位更新: 精度=${location.accuracy}米") |
||||
|
handleLocationResult(location, "GPS") |
||||
|
} |
||||
|
|
||||
|
override fun onProviderEnabled(provider: String) { |
||||
|
Log.d(TAG, "GPS提供者已启用: $provider") |
||||
|
} |
||||
|
|
||||
|
override fun onProviderDisabled(provider: String) { |
||||
|
Log.d(TAG, "GPS提供者已禁用: $provider") |
||||
|
} |
||||
|
|
||||
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { |
||||
|
when (status) { |
||||
|
LocationProvider.AVAILABLE -> Log.d(TAG, "GPS状态: 可用") |
||||
|
LocationProvider.OUT_OF_SERVICE -> Log.d(TAG, "GPS状态: 服务中断") |
||||
|
LocationProvider.TEMPORARILY_UNAVAILABLE -> Log.d(TAG, "GPS状态: 暂时不可用") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
locManager.requestLocationUpdates( |
||||
|
LocationManager.GPS_PROVIDER, |
||||
|
MIN_TIME_BETWEEN_UPDATES, |
||||
|
MIN_DISTANCE_CHANGE_FOR_UPDATES, |
||||
|
gpsLocationListener!!, |
||||
|
Looper.getMainLooper() |
||||
|
) |
||||
|
Log.d(TAG, "✅ GPS定位已启动") |
||||
|
} else { |
||||
|
Log.w(TAG, "GPS未启用,仅使用网络定位") |
||||
|
} |
||||
|
|
||||
|
if (!isNetworkEnabled() && !isGpsEnabled()) { |
||||
|
Log.e(TAG, "❌ 所有定位服务都不可用") |
||||
|
serviceCallbacks.forEach { callback -> |
||||
|
callback.onLocationError(-6, "定位服务不可用") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理定位结果 |
||||
|
*/ |
||||
|
// 修改handleLocationResult方法,对网络定位进行更细致的处理 |
||||
|
private fun handleLocationResult(location: Location, provider: String) { |
||||
|
Log.d(TAG, "定位结果: 来源=$provider, 精度=${location.accuracy}米, 坐标=(${location.latitude}, ${location.longitude})") |
||||
|
|
||||
|
// 混合定位策略:优先接受可用的定位结果 |
||||
|
when (provider) { |
||||
|
"NETWORK" -> { |
||||
|
// 网络定位优先策略 |
||||
|
when { |
||||
|
location.accuracy <= 200f -> { |
||||
|
Log.d(TAG, "✅ 接受网络定位: ${location.accuracy}米") |
||||
|
processLocationSuccess(location) |
||||
|
} |
||||
|
location.accuracy <= 1000f -> { |
||||
|
Log.d(TAG, "⚠️ 接受中等精度网络定位: ${location.accuracy}米") |
||||
|
// 短暂等待,看是否有更好的定位 |
||||
|
val elapsedTime = System.currentTimeMillis() - locationStartTime |
||||
|
if (elapsedTime > 5000) { // 5秒后接受 |
||||
|
processLocationSuccess(location) |
||||
|
} else { |
||||
|
Log.d(TAG, "等待更高精度定位中...") |
||||
|
} |
||||
|
} |
||||
|
location.accuracy <= 3000f -> { |
||||
|
Log.w(TAG, "⚠️ 网络定位精度较低: ${location.accuracy}米") |
||||
|
// 10秒后接受低精度定位 |
||||
|
val elapsedTime = System.currentTimeMillis() - locationStartTime |
||||
|
if (elapsedTime > 10000) { |
||||
|
Log.d(TAG, "接受低精度网络定位") |
||||
|
processLocationSuccess(location) |
||||
|
} |
||||
|
} |
||||
|
else -> { |
||||
|
Log.w(TAG, "网络定位精度过低(${location.accuracy}米),继续等待") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
"GPS" -> { |
||||
|
// GPS作为高精度补充 |
||||
|
if (location.accuracy <= HIGH_ACCURACY_THRESHOLD) { |
||||
|
Log.d(TAG, "✅ 接受高精度GPS定位: ${location.accuracy}米") |
||||
|
processLocationSuccess(location) |
||||
|
} else if (location.accuracy <= HIGH_ACCURACY_THRESHOLD * 2) { |
||||
|
Log.d(TAG, "✅ 接受GPS定位: ${location.accuracy}米") |
||||
|
processLocationSuccess(location) |
||||
|
} else { |
||||
|
Log.w(TAG, "GPS定位精度不足(${location.accuracy}米),继续等待") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理定位成功 |
||||
|
*/ |
||||
|
private fun processLocationSuccess(location: Location) { |
||||
|
// 停止超时计时器 |
||||
|
stopLocationTimeout() |
||||
|
|
||||
|
// 停止定位更新 |
||||
|
stopLocationUpdates() |
||||
|
|
||||
|
// 获取地址信息(可选) |
||||
|
val address = getAddressFromLocation(location) |
||||
|
|
||||
|
// 缓存位置数据 |
||||
|
val locationData = LocationData( |
||||
|
latitude = location.latitude, |
||||
|
longitude = location.longitude, |
||||
|
timestamp = System.currentTimeMillis() |
||||
|
) |
||||
|
cachedLocationData = locationData |
||||
|
|
||||
|
// 处理成功的定位结果 |
||||
|
serviceCallbacks.forEach { callback -> |
||||
|
try { |
||||
|
callback.onLocationSuccess( |
||||
|
location.latitude, |
||||
|
location.longitude |
||||
|
) |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(TAG, "服务回调失败: ${e.message}", e) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 清空服务回调列表 |
||||
|
serviceCallbacks.clear() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止定位更新 |
||||
|
*/ |
||||
|
private fun stopLocationUpdates() { |
||||
|
val locManager = locationManager ?: return |
||||
|
|
||||
|
gpsLocationListener?.let { |
||||
|
locManager.removeUpdates(it) |
||||
|
gpsLocationListener = null |
||||
|
} |
||||
|
|
||||
|
networkLocationListener?.let { |
||||
|
locManager.removeUpdates(it) |
||||
|
networkLocationListener = null |
||||
|
} |
||||
|
|
||||
|
Log.d(TAG, "已停止定位更新") |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从位置获取地址信息 |
||||
|
*/ |
||||
|
private fun getAddressFromLocation(location: Location): String { |
||||
|
return try { |
||||
|
val ctx = appContext ?: return "" |
||||
|
val geocoder = Geocoder(ctx, Locale.getDefault()) |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
||||
|
// Android 13+ 使用新的异步API |
||||
|
var addressResult = "" |
||||
|
geocoder.getFromLocation(location.latitude, location.longitude, 1) { addresses -> |
||||
|
if (addresses.isNotEmpty()) { |
||||
|
addressResult = addresses[0].getAddressLine(0) ?: "" |
||||
|
} |
||||
|
} |
||||
|
addressResult |
||||
|
} else { |
||||
|
// 旧版本使用同步API |
||||
|
@Suppress("DEPRECATION") |
||||
|
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1) |
||||
|
if (!addresses.isNullOrEmpty()) { |
||||
|
addresses[0].getAddressLine(0) ?: "" |
||||
|
} else { |
||||
|
"" |
||||
|
} |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
Log.w(TAG, "获取地址失败: ${e.message}") |
||||
|
"" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查定位权限 |
||||
|
*/ |
||||
|
private fun checkPermissions(context: Context): Boolean { |
||||
|
return ContextCompat.checkSelfPermission( |
||||
|
context, |
||||
|
Manifest.permission.ACCESS_FINE_LOCATION |
||||
|
) == PackageManager.PERMISSION_GRANTED || |
||||
|
ContextCompat.checkSelfPermission( |
||||
|
context, |
||||
|
Manifest.permission.ACCESS_COARSE_LOCATION |
||||
|
) == PackageManager.PERMISSION_GRANTED |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查精确定位权限 |
||||
|
*/ |
||||
|
private fun hasFineLocationPermission(context: Context): Boolean { |
||||
|
return ContextCompat.checkSelfPermission( |
||||
|
context, |
||||
|
Manifest.permission.ACCESS_FINE_LOCATION |
||||
|
) == PackageManager.PERMISSION_GRANTED |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查粗略定位权限 |
||||
|
*/ |
||||
|
private fun hasCoarseLocationPermission(context: Context): Boolean { |
||||
|
return ContextCompat.checkSelfPermission( |
||||
|
context, |
||||
|
Manifest.permission.ACCESS_COARSE_LOCATION |
||||
|
) == PackageManager.PERMISSION_GRANTED |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查后台定位权限 |
||||
|
*/ |
||||
|
private fun hasBackgroundLocationPermission(context: Context): Boolean { |
||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |
||||
|
ContextCompat.checkSelfPermission( |
||||
|
context, |
||||
|
Manifest.permission.ACCESS_BACKGROUND_LOCATION |
||||
|
) == PackageManager.PERMISSION_GRANTED |
||||
|
} else { |
||||
|
true // Android 9 及以下没有这个权限 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 位置数据类 |
||||
|
*/ |
||||
|
data class LocationData( |
||||
|
val latitude: Double, |
||||
|
val longitude: Double, |
||||
|
val timestamp: Long |
||||
|
) |
||||
|
|
||||
|
/** |
||||
|
* 启动定位超时计时器 |
||||
|
*/ |
||||
|
private fun startLocationTimeout() { |
||||
|
// 如果已经有超时计时器在运行,先停止它 |
||||
|
stopLocationTimeout() |
||||
|
|
||||
|
locationTimeoutHandler = Handler(Looper.getMainLooper()) |
||||
|
locationTimeoutHandler?.postDelayed({ |
||||
|
Log.w(TAG, "定位超时,停止定位请求") |
||||
|
|
||||
|
// 停止定位 |
||||
|
stopLocationUpdates() |
||||
|
|
||||
|
// 通知所有等待的回调超时错误 |
||||
|
serviceCallbacks.forEach { callback -> |
||||
|
try { |
||||
|
callback.onLocationError(-6, "定位超时,未能在${LOCATION_TIMEOUT_MS/1000}秒内获取到位置") |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(TAG, "超时回调失败: ${e.message}", e) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 清空回调列表 |
||||
|
serviceCallbacks.clear() |
||||
|
|
||||
|
// 清理超时处理器 |
||||
|
locationTimeoutHandler = null |
||||
|
}, LOCATION_TIMEOUT_MS) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止定位超时计时器 |
||||
|
*/ |
||||
|
private fun stopLocationTimeout() { |
||||
|
locationTimeoutHandler?.removeCallbacksAndMessages(null) |
||||
|
locationTimeoutHandler = null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理资源 |
||||
|
*/ |
||||
|
fun cleanup() { |
||||
|
stopLocationUpdates() |
||||
|
stopLocationTimeout() |
||||
|
serviceCallbacks.clear() |
||||
|
cachedLocationData = null |
||||
|
initialized = false |
||||
|
Log.d(TAG, "原生定位服务已清理") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 调整精度阈值,更适合混合定位 |
||||
|
private const val HIGH_ACCURACY_THRESHOLD = 100.0f // 放宽到100米 |
||||
|
private const val LOCATION_TIMEOUT_MS = 30000L // 缩短到30秒 |
||||
|
private const val MIN_TIME_BETWEEN_UPDATES = 2000L // 2秒更新 |
||||
|
private const val MIN_DISTANCE_CHANGE_FOR_UPDATES = 5f // 5米距离变化 |
||||
Loading…
Reference in new issue