手机定位功能在人们的日常生活中,给人们(尤其是笔者这样的路痴)带来了极大的便利,而如今大部分安卓设备,已非复吴下阿蒙,不仅仅支持过去的GPS定位,还兼容了北斗、伽利略、GLONASS等全球卫星定位系统,以及网络定位技术。本文将介绍实时经纬度,以及海拔高度(波动较大,参考价值小)的获取。
确认手机是否开启“位置信息” 当手机电量低,系统开启“省电模式”的时候,首当其冲的(多数情况下)就是“位置信息”功能,并且用户有的时候也会关闭系统的定位功能,届时所有app都无法获取定位信息,检查手机是否开启定位操作如下:
//这个是在Activity内的,context.getSystemService(LOCATION_SERVICE),不在Activity里面记得先拿到context
val mlocationManager=getSystemService(LOCATION_SERVICE) as LocationManager
//以下是检查系统是否开启GPS权限,即“位置信息”。这个检查的是系统的,不是当前app是否获得权限,即使系统开启了定位,
//当前app仍然有可能没开,别搞混了
if (!mlocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
//将前往“位置信息”开启的设置界面
startActivityForResult(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),0)
}
确认当前app是否具有定位权限:
val LOCATION_PERMISSION=1 //这个仅仅是跟后面返回时候的onRequestPermissionsResult配合,确认是不是自己的
//返回结果,取值的时候一定一定要取大等于0的,不然会翻车
//检查是否具有高精度的定位权限
val hasLocationPermission =checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
//如果权限获得,返回就会等于PackageManager.PERMISSION_GRANTED
if (hasLocationPermission != PackageManager.PERMISSION_GRANTED) {
//requestPermissions是异步执行的,返回的结果会交到一个onRequestPermissionsResult方法里面处理
//如果权限没获得就会弹窗请求获得权限
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION, //这里放要请求的权限列表
Manifest.permission.ACCESS_FINE_LOCATION
),
LOCATION_PERMISSION //可能会多次调用权限获取弹窗,为了返回时候知道当前返回的是哪次请求,来做出不同的
) //应对,这里传的requestCode就是用来区分不同的请求的
}
获取权限后的处理逻辑:
//这个跟上面那个方法↑↑↑是一一对应的。
//申请位置权限后,请求弹窗,用户请求的结果会在此返回,返回后刷新位置信息
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION) {//这个requestCode是用来区分此次返回是谁调用的
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "获取了权限", Toast.LENGTH_LONG).show()
//成功获取定位权限后的刷新处理逻辑写在这
}else{
Toast.makeText(this, "你咋还拒绝了", Toast.LENGTH_LONG).show()
//请求权限失败的处理逻辑写在这
}
}
}
设置查询条件:
val criteria = Criteria()
// 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略,Criteria.ACCURACY_FINE则比较精细
criteria.setAccuracy(Criteria.ACCURACY_FINE)
// 设置是否要求速度
criteria.speedRequired=false
//如果设置要求速度,那么就可以用:
//criteria.setSpeedAccuracy(Criteria.ACCURACY_HIGH)来设置速度的精度;
//Criteria.ACCURACY_HIGH:精度高,误差在100米内
//ACCURACY_MEDIUM精度中等,误差在100-500米之间
//ACCURACY_LOW精度低,误差大于500米
// 设置是否允许运营商收费
criteria.costAllowed=false
// 设置是否需要方位信息
criteria.bearingRequired=false
// 设置是否需要海拔信息
criteria.altitudeRequired=false
// 设置对电源的需求
//Criteria.POWER_LOW:耗电低,Criteria.POWER_MEDIUM:中度耗电
//Criteria.POWER_HIGH:耗电高,但是精确度也高
criteria.powerRequirement=Criteria.POWER_LOW
val bestProvider = mLocationManager.getBestProvider(criteria, true)
// 获取位置信息
// 如果不设置查询要求,getLastKnownLocation方法传人的参数为LocationManager.GPS_PROVIDER
val location = mLocationManager.getLastKnownLocation(bestProvider)
获取位置信息:
``
//获取位置Location对象
fun getLocation(locationManager: LocationManager): Location? {
var location: Location? = null
if (this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
//没获得权限的处理逻辑写在这
Toast.makeText(this, "无权限", Toast.LENGTH_LONG).show();
} else if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
//没开GPS定位的处理逻辑写在这(可能开了数据/WiFi,则可以使用精度略低的网络定位)
Toast.makeText(this, "没开GPS", Toast.LENGTH_LONG).show();
} else {
//如果是同时有GPS和网络定位的权限,但是当前用的是网络,就先试图切换回GPS,精度会比网络定位高
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (location == null) {//切换失败,基本上说明位置信息权限没开
//则试图退而求其次通过网络来完成粗略定位
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
if (location == null) { //连网络定位都没开(流量/WiFi都没开)
//前往“设置”界面
startActivityForResult(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),0)
Toast.makeText(this ,"请开启wifi或流量以实现网络定位",Toast.LENGTH_LONG).show();
}
}
}
return location
}
//获取经度: getLocation(locationManager)?.latitude
//获取纬度: getLocation(locationManager)?.longitude
//获取海拔: getLocation(locationManager)?.altitude
设置位置改变监听器:
val locationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
//位置改变的时候的处理逻辑写这
}
override fun onProviderEnabled(provider:String) {
//从无定位权限,到有定位的切换过程,会触发这个函数。
//provider有三个可能取值:LocationProvider.OUT_OF_SERVICE:在服务范围外
//LocationProvider.TEMPORARILY_UNAVAILABLE:服务暂时不可用
//LocationProvider.AVAILABLE:服务可用
}
override fun onProviderDisabled(provider:String) {
//从有定位权限,到无定位的切换过程,会触发这个函数。
}
}
把监听器放到LocationManager对象中:
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
val hasLocationPermission = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
if ((locationManager != null) && ((hasLocationPermission == PackageManager.PERMISSION_GRANTED))) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, //Provider类型:有四种:NETWORK_PROVIDER:网络定
5000, //刷新间隔 //位,GPS_PROVIDER:卫星定位,PASSIVE_PROVIDER:通过其他应用
0F, // //发出请求,被动更新。FUSED_PROVIDER:混合模式
locationListener //监听器
)
}
监听器在退出Activity时要删除:
override fun onPause() {
super.onPause()
if(locationManager!=null){
locationManager.removeUpdates(locationListener)//去掉所有的更新状态的监听器
}
}
对应的,在进入Activity的时候,自然要把监听器加回来:
override fun onResume() {
//挂上LocationListener, 在状态变化时刷新位置显示,因为requestPermissionss是异步执行的,所以要先确认是否有权限
super.onResume()
val hasLocationPermission = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
if ((locationManager != null) && ((hasLocationPermission == PackageManager.PERMISSION_GRANTED))) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000,
0F,
locationListener
)
}
}



