前言
因为项目中经常会遇到要上传一系列设备信息的功能,为了方便使用,所以就拆分成以下系列文章来单独介绍如何获取各类设备信息
- 手机运营商获取
- AndroidID、IMEI、OAID获取
- 地理位置信息经纬度获取
- 公网IP地址获取:移动网络IP、Wifi IP
- Build类获取相关设备信息
- 屏幕相关信息:密度、物理尺寸获取
- BuildConfig获取的一系列基础信息
- UA、网络状态…等持续更新
1. 基础学习
在介绍之前,先讲一些获取经纬度信息相关的基础知识
1.1 LocationManager
位置管理器
想要获取经纬度信息,主要是通过LocationManager这个类,该类不能直接new,需要通过如下方式获取
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
获取位置信息是通过该类的getLastKnownLocation(provider:String)
方法,可以看到,使用此方法需要我们传递个provider,位置提供器。
1.2 LocationProvider
位置提供器
位置提供器一共有三种
NETWORK_PROVIDER
:通过蜂窝塔(基站)和WIFI接入点来获取GPS_PROVIDER
:通过GPS的方式获取PASSIVE_PROVIDER
:被动的通过其他程序获取经纬度
locationManager.getProviders(true)
该方法会返回一个手机所支持的位置提供器集合
1.3 精度
假如我们通过以上三种provider都能获取到Location,那么选哪种作为经纬度最合适呢?
Location有getAccuracy()
方法来获取精度,看官方getAccuracy()
的解释:
/**
* Get the estimated horizontal accuracy of this location, radial, in meters.
*
* <p>We define horizontal accuracy as the radius of 68% confidence. In other
* words, if you draw a circle centered at this location's
* latitude and longitude, and with a radius equal to the accuracy,
* then there is a 68% probability that the true location is inside
* the circle.
*
* <p>This accuracy estimation is only concerned with horizontal
* accuracy, and does not indicate the accuracy of bearing,
* velocity or altitude if those are included in this Location.
*
* <p>If this location does not have a horizontal accuracy, then 0.0 is returned.
* All locations generated by the {@link LocationManager} include horizontal accuracy.
*/
简单的理解就是:
若以该经纬度作为圆心,精度作为半径画圆,那么真实的位置落在该圆内的概率为68%。因为落在该圆内的概率是固定的68%,那么肯定是圆的半径即精度越小,就越准确。所以我们选取getAccuracy()
返回值最小的那个provider。
1.4 LocationListener
有时候可能会因为没有网或者在室内,通过以上方式获取到的location均为null,那么此时我们可以开启位置信息的连续监听,当时间超过设定的秒数或者位置移动超过设定的米数时,更新位置信息。
private var locationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.i(TAG, "onLocationChanged: 经纬度发生变化")
//调用更新位置
updateToNewLocation(location)
}
override fun onProviderDisabled(provider: String) {
updateToNewLocation(null)
Log.i(TAG, "onProviderDisabled: ")
}
override fun onProviderEnabled(provider: String) {
Log.i(TAG, "onProviderEnabled: ")
}
}
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
60000.toLong(), //每隔1分钟重新获取一次
8.toFloat(), //移动距离超过8米重新获取一次
locationListener
)
2. 所需权限
权限 | 说明 |
INTERNET | 允许使用网络 |
ACCESS_FINE_LOCATION | 允许使用GPS定位 |
ACCESS_COARSE_LOCATION | 允许使用WIFI热点或基站来获取粗略的定位 |
3. 获取步骤
3.1 申请权限
只有申请到了ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION后,才可以进行下一步获取经纬度信息的操作,这里不多讲,完整代码如下:
class LocationActivity : AppCompatActivity() {
companion object {
const val LOCATION_REQUEST_CODE = 1
const val LISTENER_REQUEST_CODE = 2
}
override fun onCreate(savedInstanceState: Bundle?) {
//申请权限
if (PermissionUtil.requestPermission(LOCATION_REQUEST_CODE, permissionList.toList(), this)) {
//获取经纬度信息
getLocationInfo()
}
}
/**
* 权限申请回调
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
//LOCATION_REQUEST_CODE权限申请
LOCATION_REQUEST_CODE -> {
if (PermissionUtil.verifyResult(grantResults, this)) {
getLocationInfo()
} else {
Toast.makeText(this, "没有权限", Toast.LENGTH_SHORT).show()
}
}
}
}
}
object PermissionUtil {
/**
* 验证是否有权限,没有则申请
*/
fun requestPermission(requestCode: Int, permissionList: List<String>, context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//没有同意需要申请的权限
val requestPermissionList = mutableListOf<String>()
for (permission in permissionList) {
if (ContextCompat.checkSelfPermission(
context,
permission
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissionList.add(permission)
}
}
if (requestPermissionList.size > 0) {
ActivityCompat.requestPermissions(
context as Activity,
requestPermissionList.toTypedArray(),
requestCode
)
return false
} else {
return true
}
} else {
return true
}
}
/**
*验证权限申请的结果
*/
fun verifyResult(grantResults: IntArray,context: Context): Boolean {
if (grantResults.isNotEmpty()) {
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(context, "必须同意所有权限才能使用该功能", Toast.LENGTH_SHORT).show()
return false
}
}
return true
} else {
Toast.makeText(context, "发生未知错误", Toast.LENGTH_SHORT).show()
return false
}
}
}
3.2 获取经纬度信息
在申请到权限后,进行如下操作来获取经纬度信息
- 判断是否开启位置服务,没有则跳转至系统设置打开定位服务
判断定位服务有没有开启主要通过以下方法
var gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
当返回true,代表GPS定位服务开启,NETWORK_PROVIDER同理
- 获取所有支持的provider,然后通过provider来获取定位信息
- 当返回true,代表GPS定位服务开启,NETWORK_PROVIDER同理
- 若所支持的provider获取到的经纬度均为空,则开启连续定位监听
完整代码如下:
fun getLocationInfo() {
//判断是否开启位置服务,没有则跳转至设置来开启
if (isLocationServiceOpen()) {
//获取所有支持的provider
val providers = locationManager.getProviders(true)
//用来存储最优的结果
var betterLocation: Location? = null
for (provider in providers) {
val location = locationManager.getLastKnownLocation(provider)
location?.let {
Log.i(TAG, "$provider 精度为:${it.accuracy}")
if (betterLocation == null) {
betterLocation = it
} else {
//因为半径等于精度,所以精度越低代表越准确
if (it.accuracy < betterLocation!!.accuracy)
betterLocation = it
}
}
if (location == null) {
Log.i(TAG, "$provider 获取到的位置为null")
}
}
betterLocation?.let {
Log.i(TAG, "精度最高的获取方式:${it.provider} 经度:${it.longitude} 纬度:${it.latitude}")
}
//(四)若所支持的provider获取到的位置均为空,则开启连续定位服务
if (betterLocation == null) {
for (provider in locationManager.getProviders(true)) {
locationMonitor(provider)
}
Log.i(TAG, "getLocationInfo: 获取到的经纬度均为空,已开启连续定位监听")
}
} else {
Toast.makeText(this, "请跳转到系统设置中打开定位服务", Toast.LENGTH_SHORT).show()
}
}
/**
* 判断定位服务是否开启
*/
private fun isLocationServiceOpen(): Boolean {
var gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
var network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
//有一个开启就可
return gps || network
}
3.2 开启连续监听
在1.4中我们已经讲了LocationListener,如何开启连续监听,这里就不再详细讲述
具体代码如下
fun locationMonitor(provider: String) {
if (PermissionUtil.requestPermission(
LISTENER_REQUEST_CODE,
permissionList.toList(),
this
)
) {
locationManager.requestLocationUpdates(
provider,
60000.toLong(), //超过1分钟则更新位置信息
8.toFloat(), //位置超过8米则更新位置信息
locationListener
)
}
}
4. 总结
以上就是安卓原生获取经纬度信息的全部内容,如果通过以上方法都获取不到的话,那就使用高德或百度等第三方SDK吧。
如果本文对你有帮助,请别忘记点赞start,如果有不恰当的地方也请提出来,下篇文章见。
项目地址
觉得不错还望老板打个赏呗,帮我买条秋裤穿 (*꒦ິ⌓꒦ີ)
5. 参考文章
10.14 Android GPS初涉 | 菜鸟教程 (runoob.com)