定位的几种方式
在Android系统中,给我们提供了四种定位方式:
- network
它是依靠信号塔或者WIFI定位的。对应的provider字段是LocationManager.NETWORK_PROVIDER,是一种低精度,低耗电的粗略定位的方式 - gps
它是依靠GPS来定位的,对应的Provider字段是LocationManager.GPS_PROVIDER,是高精度,高耗电的精准定位方式 - passive
被动的获取定位信息,通过接受其他APP或者Service的定位信息,不过需要这个权限ACCESS_FINE_LOCATION - fused
Google已经将这个定位方式hide了
常用有两种技术可以实现:一种是通过GPS定位,一种是通过网络定位。
GPS定位:
GPS定位的工作原理是基于手机内置的GPS硬件直接和卫星交互来获取当前面的经纬度信息这种定位方式精度非常高,但缺点是只能在室外使用,室内基本无法接收到卫星的信号。
网络定位:
网络定位的工作原理是根据手机当前网络附近的三个基站进行测速,基于此计算出手机和每个基站之间的距离,再通过三角定位确定出一个大概的位置,这种定位方式精确度一般,但优点是在室内室外都可以使用
Android对这两种定位方法都提供了相应的API支持,但是由于一些特殊的原因,Google 的网络服务在中国不可访问,从而导致网络定位方式的API失效。而GPS定位虽然不需要网络,但是必须要在室外才可以使用,因此你在室内开发很有可能会遇到不管使用哪种定位方式都无法成功定位的情况
GPS定位
相关类
-
LocationManager:位置服务管理器类 是获取位置信息的入口级类,要获取位置信息,首先需要获取一个LocationManager对象:
LocationManager pLocationManager = (LocationManager) Context.getSystemService(Context.LOCATION_SERVICE);
-
LocationProvider:位置源提供者 用于描述位置提供者信息,可以先使用方法获取最佳提供者的名称:
String providerName = LocationManger.getBestProvider(Criteria criteria, boolean enabledOnly);
-
Location:位置对象
描述地理位置信息的类,记录了经纬度,海拔,获取坐标时间,时间,方位等。可以通过LocationManager.getLastKnowLocation(provider)获取位置信息,provider就是上文中提到的GPS_PROVIDER、NETWORK_PROVIDER、PASSIVE_PROVIDER、FUSED_PROVIDER;不过很多时候得到的Location对象为null;实时动态坐标可以在监听器locationListener的onLocationChanged(Location
location)方法中来获取。 -
4.LocationListener:位置监听接口 用于监听位置(包括GPS,网络,基站等所有提供位置的)变化,监听设备开关与状态。实时动态获取位置信息,首先要实现该接口,在相关方法中添加实现功能的代码,实现该接口可以使用内部类或者匿名实现。
-
5.Criteria:用于选择位置信息提供者的辅助类: 创建LocationProvider对象时会使用到此类。
使用定位
需要在AndroidManifest.xml 中申请权限:
<!-- 允许一个程序访问精良位置(如GPS) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 允许一个程序访问CellID或WiFi热点来获取粗略的位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
如果系统是6.0以上,需要申请动态权限:
if(ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION
,Manifest.permission.ACCESS_COARSE_LOCATION},1);
}else {
initLocation();
}
权限申请好以后,就可以初始化Location类:
try{
//获取一个地址管理者,获取的方法比较特殊,不是直接new出来的
LocationManager locationManager = (LocationManager)getSystemService(this.LOCATION_SERVICE);
//使用GPS获取上一次的地址,这样获取到的信息需要多次,才能够显示出来,所以后面有动态的判断
Location location = locationManager.getLastKnownLocation(locationManager.GPS_PROVIDER);
//判断用户是否打开了GPS开关,这个和获取权限没有关系
GPSisopen(locationManager);
//显示信息,根据自己传入对应的location
upLoadInfo(location);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 8, new LocationListener() {
@Override
//当地理位置发生变化时调用
public void onLocationChanged(Location location) {
upLoadInfo(location);
}
@Override
//当状态发生变化时调用
public void onStatusChanged(String s, int i, Bundle bundle) {
}
@Override
//当定位者启用的时候调用
public void onProviderEnabled(String s) {
}
@Override
public void onProviderDisabled(String s) {
}
});
}catch (SecurityException e){
e.printStackTrace();
}
其中GPSisOpen 方法的实现:
public void GPSisopen(LocationManager locationManager) {
if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this,"请打开GPS开关~",Toast.LENGTH_SHORT).show();
final AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("请打开GPS链接");
dialog.setMessage("为了获取定位服务,请先打开GPS开关");
dialog.setPositiveButton("设置",new android.content.DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//跳转到设置GPS的界面
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, 0);
}
});
dialog.setNegativeButton("取消",new android.content.DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
//调用dialog显示方法
dialog.show();
}
}
upLoadInfo方法的实现:
public void upLoadInfo(Location location){
Float accuracy1 = location.getAccuracy(); //精度
Double altitude1 = location.getAltitude(); //海拔,高度
Double latitude1 = location.getLatitude(); //纬度
Double longitude1 = location.getLongitude(); //精度
Float bearing1 = location.getBearing(); //轴承,方位
Float speed1 = location.getSpeed(); //速度
Long time1 = location.getTime(); //时间
time.setText(time1.toString());
accuracy.setText(accuracy1.toString());
bearing.setText(bearing1.toString());
speed.setText(speed1.toString());
altitude.setText(altitude1.toString());
latitude.setText(latitude1.toString());
longitude.setText(longitude1.toString());
others.setText("暂无额外信息");
}
动态权限申请后的回调:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 1:
if(grantResults.length>0 && grantResults[0] == getPackageManager().PERMISSION_GRANTED) {
initLocation();
}else {
Toast.makeText(this,"你拒绝开启权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
网络定位
是否支持网络定位:
if (manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {//是否支持Network定位
//获取最后的network定位信息
location = manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
找到最合适的定位方式:
public static Location getBestLocation(Context context, Criteria criteria) {
Location location;
LocationManager manager = getLocationManager(context);
if (criteria == null) {
criteria = new Criteria();
}
String provider = manager.getBestProvider(criteria, true);
if (TextUtils.isEmpty(provider)) {
//如果找不到最适合的定位,使用network定位
location = getNetWorkLocation(context);
} else {
//高版本的权限检查
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null;
}
//获取最适合的定位方式的最后的定位权限
location = manager.getLastKnownLocation(provider);
}
return location;
}
private void getBestLocation() {
Criteria c = new Criteria();//Criteria类是设置定位的标准信息(系统会根据你的要求,匹配最适合你的定位供应商),一个定位的辅助信息的类
c.setPowerRequirement(Criteria.POWER_LOW);//设置低耗电
c.setAltitudeRequired(true);//设置需要海拔
c.setBearingAccuracy(Criteria.ACCURACY_COARSE);//设置COARSE精度标准
c.setAccuracy(Criteria.ACCURACY_LOW);//设置低精度
//... Criteria 还有其他属性,就不一一介绍了
Location best = LocationUtils.getBestLocation(this, c); //调用上述的方法
if (best == null) {
Toast.makeText(this, " best location is null", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "best location: lat==" + best.getLatitude() + " lng==" + best.getLongitude(), Toast.LENGTH_SHORT).show();
}
}