Skip to content

获取位置信息

在安卓中获取位置信息以及后台位置信息通常使用 FusedLocationProviderClientLocationManager。为了在后台获取位置信息,需要结合使用位置服务、WorkManagerJobScheduler 等后台任务管理工具来确保即使应用被关闭或处于后台时仍能获取位置。

其中FusedLocationProviderClient 是推荐的获取位置信息的方式,它继承了多个 LocationProvider ,能提供一个准确、低功耗的定位服务。但是需要能访问GMS的API(Google Play服务)才能够调用。

1. AndroidManifest中声明定位权限

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.ACCESS_BACKGROUND_LOCATION" android:deprecated="true"/> <!-- 后台定位权限 -->

注意: 如果只声明精确定位权限和粗略定位权限而不申请后台定位权限的话,在设置中的位置权限是没有 Allow all the time 这一选项的。前台定位权限均为运行时权限。后台定位权限申请不会有任何权限弹窗提示。Android 10及以后版本,需要请求 ACCESS_BACKGROUND_LOCATION 才能在后台获取位置。但是即便授予了这个权限也需要让用户到设置项中手动将位置权限置为 Allow all the time,这样应用在后台也能更新位置信息。

代码示例

  1. FusedLocationProviderClient方法
kotlin
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import android.location.Location
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private lateinit var fusedLocationClient: FusedLocationProviderClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        // 请求权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            getLocation()
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
        }
    }

    private fun getLocation() {
        lifecycleScope.launch(Dispatchers.Main) {
            try {
                val location: Location? = fusedLocationClient.lastLocation.await()
                if (location != null) {
                    // 获取到位置,更新UI等操作
                    println("Location: ${location.latitude}, ${location.longitude}")
                } else {
                    // 无法获取到位置
                    println("Location not available")
                }
            } catch (e: Exception) {
                // 处理异常
                println("Error: ${e.localizedMessage}")
            }
        }
    }
}

2. LocationManager

kotlin
import android.location.Location
import android.location.LocationListener
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.Manifest
import android.content.pm.PackageManager

class MainActivity : AppCompatActivity() {

    private lateinit var locationManager: LocationManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager

        // 检查权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            getLocationOnce()
        } else {
            // 请求权限
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
        }
    }

    private fun getLocationOnce() {
        try {
            // 请求单次位置更新
            locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, object : LocationListener {
                override fun onLocationChanged(location: Location) {
                    // 获取到位置后更新UI
                    println("Latitude: ${location.latitude}, Longitude: ${location.longitude}")
                }

                override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
                override fun onProviderEnabled(provider: String?) {}
                override fun onProviderDisabled(provider: String?) {
                    // 如果 GPS 被禁用,可以提示用户开启
                    println("GPS is disabled")
                }
            }, null) // 在此传入 Looper,可以为 null 以在主线程回调
        } catch (e: SecurityException) {
            e.printStackTrace()
        }
    }

    // 如果需要持续获取位置更新,可以使用 requestLocationUpdates 方法,它允许你设置更新频率、最小距离等。
    private fun getContinuousLocationUpdates() {
        try {
            // 设置位置更新间隔和最小移动距离
            val minTime = 1000L  // 1秒钟更新一次
            val minDistance = 5f  // 最小移动距离为 5 米

            locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, minTime, minDistance, object : LocationListener {
                    override fun onLocationChanged(location: Location) {
                        // 获取到新位置
                        println("Latitude: ${location.latitude}, Longitude: ${location.longitude}")
                    }

                    override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
                    override fun onProviderEnabled(provider: String?) {}
                    override fun onProviderDisabled(provider: String?) {}
                })
        } catch (e: SecurityException) {
            e.printStackTrace()
        }
    }
}

Released under the MIT License.