如何在 Android 中获取当前位置?
作为一名开发人员,当您在 Android 中处理位置时,您总是对选择最适合您需求的最佳和有效方法有一些疑问。所以在本文中,我们将讨论如何在 Android 中获取用户的当前位置。有两种方法可以获取任何 Android 设备的当前位置:
- Android 的位置管理器 API
- 融合位置提供者:Google Play 服务位置 API
Question: Which one is efficient and why?
Answer: Fused Location Provider because it optimizes the device’s use of battery power.
在移动上述任何方法之前,我们必须获得位置许可。
取得位置许可
步骤 1:在清单文件中定义用于位置访问的使用权限
Note:
In order to receive location updates from NETWORK_PROVIDER or GPS_PROVIDER, you must request the user’s permission by declaring either the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission, respectively, in your Android manifest file. Without these permissions, your application will fail at runtime when requesting location updates.
If you are using both NETWORK_PROVIDER and GPS_PROVIDER, then you need to request only the ACCESS_FINE_LOCATION permission, because it includes permission for both providers. Permission for ACCESS_COARSE_LOCATION allows access only to NETWORK_PROVIDER.
第 2 步:定义使用互联网访问权限,因为我们将使用互联网提供商。
第 3 步:编写一个函数来检查位置权限是否被授予。如果未授予权限,则在运行时请求权限。
Kotlin
private fun isLocationPermissionGranted(): Boolean {
return if (ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION
),
requestcode
)
false
} else {
true
}
}
Kotlin
private var currentLocation: Location? = null
lateinit var locationManager: LocationManager
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
Kotlin
val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
//------------------------------------------------------//
val hasNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
Kotlin
val gpsLocationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
locationByGps= location
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
//------------------------------------------------------//
val networkLocationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
locationByNetwork= location
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
Kotlin
if (hasGps) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000,
0F,
gpsLocationListener
)
}
//------------------------------------------------------//
if (hasNetwork) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
5000,
0F,
networkLocationListener
)
}
Kotlin
val lastKnownLocationByGps =
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
lastKnownLocationByGps?.let {
locationByGps = lastKnownLocationByGps
}
//------------------------------------------------------//
val lastKnownLocationByNetwork =
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
lastKnownLocationByNetwork?.let {
locationByNetwork = lastKnownLocationByNetwork
}
//------------------------------------------------------//
if (locationByGps != null && locationByNetwork != null) {
if (locationByGps.accuracy > locationByNetwork!!.accuracy) {
currentLocation = locationByGps
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
} else {
currentLocation = locationByNetwork
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
}
}
Kotlin
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
// LocationRequest - Requirements for the location updates, i.e.,
// how often you should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest
// LocationCallback - Called when FusedLocationProviderClient
// has a new Location
private lateinit var locationCallback: LocationCallback
// This will store current location info
private var currentLocation: Location? = null
Kotlin
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
Kotlin
locationRequest = LocationRequest().apply {
// Sets the desired interval for
// active location updates.
// This interval is inexact.
interval = TimeUnit.SECONDS.toMillis(60)
// Sets the fastest rate for active location updates.
// This interval is exact, and your application will never
// receive updates more frequently than this value
fastestInterval = TimeUnit.SECONDS.toMillis(30)
// Sets the maximum time when batched location
// updates are delivered. Updates may be
// delivered sooner than this interval
maxWaitTime = TimeUnit.MINUTES.toMillis(2)
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
Kotlin
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
super.onLocationResult(locationResult)
locationResult?.lastLocation?.let {
currentLocation = locationByGps
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
} ?: {
Log.d(TAG, "Location information isn't available.")
}
}
}
Kotlin
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
Kotlin
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG, "Location Callback removed.")
} else {
Log.d(TAG, "Failed to remove Location Callback.")
}
}
现在处理位置权限后,我们将学习如何在android中获取位置。
第一种方法:通过 Android 的位置管理器 API
我们假设用户已被授予位置权限。如果没有,请先询问。
步骤 1:在 LOCATION_SERVICE 的上下文中创建 LocationManager 的实例。
科特林
private var currentLocation: Location? = null
lateinit var locationManager: LocationManager
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
第 2 步:检查 GPS 和网络是否可用,如果两者都可用,那么我们使用更准确的一个。
科特林
val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
//------------------------------------------------------//
val hasNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
第 3 步:为(GPS 和网络)创建一个 LocationListener 实例(包:android.location)。
科特林
val gpsLocationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
locationByGps= location
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
//------------------------------------------------------//
val networkLocationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
locationByNetwork= location
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
第 4 步:如果启用了任何 GPS 或网络提供商,那么我们将使用 LocationListener 从 LocationManager 请求当前位置更新。
科特林
if (hasGps) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000,
0F,
gpsLocationListener
)
}
//------------------------------------------------------//
if (hasNetwork) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
5000,
0F,
networkLocationListener
)
}
第 5 步:现在我们检查哪个提供商为我们提供了更准确的位置,然后我们将根据我们的要求使用该位置。
科特林
val lastKnownLocationByGps =
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
lastKnownLocationByGps?.let {
locationByGps = lastKnownLocationByGps
}
//------------------------------------------------------//
val lastKnownLocationByNetwork =
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
lastKnownLocationByNetwork?.let {
locationByNetwork = lastKnownLocationByNetwork
}
//------------------------------------------------------//
if (locationByGps != null && locationByNetwork != null) {
if (locationByGps.accuracy > locationByNetwork!!.accuracy) {
currentLocation = locationByGps
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
} else {
currentLocation = locationByNetwork
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
}
}
现在,在了解了如何通过 Location Manager API 获取 android 中的位置后,我们将转向第二种方法Fused Location Provider (Google Play Services Location APIs) 。
第二种方法:通过融合位置提供者
我们假设用户已被授予位置权限。如果没有,请先询问。 Fused Location Provider 是 Google Play 服务的位置 API。它提供了一个简单的 API,用于获取高、中和低精度的位置。它还优化了设备对电池电量的使用。所以我们应该更喜欢这种方法。
第一步:声明FusedLocationProviderClient、LocationRequest、LocationCallback、Location的变量。
科特林
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
// LocationRequest - Requirements for the location updates, i.e.,
// how often you should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest
// LocationCallback - Called when FusedLocationProviderClient
// has a new Location
private lateinit var locationCallback: LocationCallback
// This will store current location info
private var currentLocation: Location? = null
第二步:初始化 fusedLocationProviderClient。
科特林
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
第三步:初始化locationRequest。
科特林
locationRequest = LocationRequest().apply {
// Sets the desired interval for
// active location updates.
// This interval is inexact.
interval = TimeUnit.SECONDS.toMillis(60)
// Sets the fastest rate for active location updates.
// This interval is exact, and your application will never
// receive updates more frequently than this value
fastestInterval = TimeUnit.SECONDS.toMillis(30)
// Sets the maximum time when batched location
// updates are delivered. Updates may be
// delivered sooner than this interval
maxWaitTime = TimeUnit.MINUTES.toMillis(2)
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
第四步:初始化locationCallback。
科特林
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
super.onLocationResult(locationResult)
locationResult?.lastLocation?.let {
currentLocation = locationByGps
latitude = currentLocation.latitude
longitude = currentLocation.longitude
// use latitude and longitude as per your need
} ?: {
Log.d(TAG, "Location information isn't available.")
}
}
}
第 5 步:既然您已经初始化了所有内容,您需要让 FusedLocationProviderClient 知道您想要接收更新。所以订阅位置变化。
科特林
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
第 6 步:当应用不再需要访问位置信息时,取消订阅位置更新非常重要。
科特林
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG, "Location Callback removed.")
} else {
Log.d(TAG, "Failed to remove Location Callback.")
}
}
现在还有一点要支持 Android 10 和 Android R。那么让我们来谈谈吧……
支持安卓 10
步骤 1:在 build.gradle 文件中进行这些更改。
- 将 compileSdkVersion 设置为 29。
- 将 buildToolsVersion 设置为“29.0.3”。
- 将 targetSdkVersion 设置为 29。
您的代码应如下所示:
android {
compileSdkVersion 29
buildToolsVersion “29.0.3”
defaultConfig {
applicationId “com.example.android.location”
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName “1.0”
}
…
}
第 2 步:如果您有前台服务,请在清单中声明位置的 foregroundServiceType。
android:foregroundServiceType=”location”
您的代码应如下所示:
…
android:name=”com.example.android.location.ForegroundOnlyLocationService” android:enabled=”true” android:exported=”false” android:foregroundServiceType=”location” />
支持 Android 11 或 Android R
好消息,除了 build.gradle 文件之外,您不需要更改任何文件!进行这些更改:
- compileSdkVersion 为“android-R”
- targetSdkVersion 到“R”
您的代码应如下所示:
android {
compileSdkVersion “android-R”
buildToolsVersion “29.0.2”
defaultConfig {
applicationId “com.example.android.location”
minSdkVersion 26
targetSdkVersion “R”
versionCode 1
versionName “1.0”
}
…
}