一、开启定位功能。
手机定位类别。
手机定位类别 | 原理 |
卫星定位服务 | 根据多个卫星与芯片的通讯结果得到手机与卫星的距离 |
手机定位服务 | 基站定位:根据铁塔对应的信息进行定位 |
WIFI定位:查询WIFI路由器信息定位 |
获取定位权限。
首先需要先申请手机的定位权限,并且查询网络状态以及手机状态,于是在AndroidMainfest.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_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--查询手机状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
获取定位、数据连接、WLAN功能开关状态的判定以及系统界面的跳转。
首先我们获取定位功能的开关状态,构造getLocationstatus方法,用于获取定位开关状态:
- 从系统服务中获取定位管理器 LocationManager
LocationManager locationManager = (LocationManager)
context.getSystemService(Context.LOCATION_MANAGER);
- 用isProviderEnable返回开关开启结果
public static boolean getLocationStatus(Context context){
LocationManager locationmanager = (LocationManager)
context.getSystemService(Context.LOCATION_SERVICE);
return locationmanager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
获取数据连接的开关状态,构造 getMobileDataStatus 方法
- 从系统服务器中获取电话管理器 TelephonyManager
- 通过反射引用隐藏方法 getDataEnable
public static boolean getMobileDataStatus(Context context){
TelephonyManager telephonyManager = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
boolean isOpen = false;
try {
String methodName = "getDataEnable";
Method method = telephonyManager.getClass().getMethod(methodName);
isOpen = (boolean) method.invoke(telephonyManager);
} catch (Exception e){
e.printStackTrace();
}
return isOpen;
}
获取无线网络的开关状态,构造 getWifiStatus 方法
- 从服务器管理器中获取无线网络管理器 WifiManager
- 通过 isWiFiEnable 方法返回结果
public static boolean getWifiStatus(Context context){
WifiManager wifiManager = (WifiManager)
context.getSystemService(Context.WIFI_SERVICE);
return wifiManager.isWifiEnabled();
}
最后我们再通过检验开关状态来向用户申请权限,跳入到相关界面要求用户打开权限
定位设置界面 | Settings.ACTION_LOCATION_SOURCE_SETTINGS |
移动数据界面 | Settings.ACTION_WIFI_SETTINGS |
wifi界面 | Settings.ACTION_DATA_ROAMING_SETTINGS |
- 创建一个Activity以及相关布局来实现功能
public class LocationGPS extends AppCompatActivity {
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_location_gps);
findViewById(R.id.location_one).setOnClickListener(v -> {
if(!LocationUnit.getLocationStatus(this)) startActivity(new
Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
if(!LocationUnit.getWifiStatus(this)) startActivity(new
Intent(Settings.ACTION_WIFI_SETTINGS));
if(!LocationUnit.getMobileDataStatus(this)) startActivity(new
Intent(Settings.ACTION_DATA_ROAMING_SETTINGS));
});
}
}
二、获取定位信息
实现获取定位信息需要用到三类工具,定位条件器 Criteria、定位管理器 LoactionManager、定位监听器 LocationListener。
定位条件器 Criteria
定位条件器 Criteria 设定关于定位的前提条件
- setAccuracy: 设置定位精度
Criteria.ACCURACY_FINE | 高精度 |
Critrtia.ACCURACY_COARSE | 低精度 |
- setSpeedAccuracy: 设置速度精确度
ACCURACY_HIGH | 高精度,误差小于100米 |
ACCURACY_MEDIUM | 中等精度,误差100~500米 |
ACCURACY_LOW | 低精度,误差大于500米 |
- setAltitudeRequired: 设置是否需要海拔信息
- setBearingRequired: 设置是否需要方位信息
- setCostAllowed:设置是否允许运营商收费
- setPowerRequirement: 设置对电源的需求
Criteria.POWER_LOW | 高耗电 |
Criteria.POWER_MIEDIUM | 中耗电 |
Criteria.POWER_LOW | 低耗电 |
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE); //定位精确度
criteria.setAltitudeRequired(true); //海拔信息
criteria.setBearingRequired(true); //方位信息
criteria.setPowerRequirement(Criteria.POWER_LOW); // 对电源需求
criteria.setCostAllowed(false); // 是否允许运营商收费
定位管理器 LocationManager
LocationManager 主要用于获取定位信息的提供者、设置监听器、获取最后一次的位置信息等,从系统服务LOCATION_SERVICE获取。
- getBestProvider:获取最佳的定位提供者
String bestProvider = locationManager.getBestProvider(criteria, true);
第一个参数时是定位条件器,第二个参数是只选取可用的,其中bestProvider有三个对应返回值gps、network、passive
gps | 卫星定位 |
network | 网络定位 |
passive | 无法定位 |
- isProviderEnable:判断指定的定位提供者是否可用
- getLastKnownLocation:获取最后一次定位的位置信息
- requestLocationUpdates:设置定位监听器,其中参数,一是提供商,二是位置更新的最小更新时间,三是位置更新的最小更新距离,四是定义好的定位监听器
locationManager.requestLocationUpdates(provider, 300, 0, locationListener);
- removeUpdates: 移除位置监听器
- addGpsStatusListener:添加定位状态的监听器
- removeGpsStatusListener:删除定位状态的监听器
- registerGnssStatusCallback:注册全球导航卫星系统的状态监听器
- unregisterGnssStatusCallback:注销全球导航卫星系统的状态监听器
定位监听器 LocationLisenter
监听定位信息的变化事件,可重写以下方法
- onLocationChange:位置发生改变时调用,可以用来更新位置信息
- onProviderDisabled:在定位提供者被用户禁止时调用
- onProviderDisabled:在定位提供者被用户开启时调用
- onStatusChanged:定位提供者的状态变化时调用
OUT_OF_SERVICE | 在服务器范围外 |
TEMPORARILY_UNAVAILABLE | 暂时不可用 |
AVAILABLE | 可用状态 |
开始定位
初始化定位服务
即获取定位器设置相关规则,并且获取到最佳的定位提供者
private void initLocation(){
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE); //定位精确度
criteria.setAltitudeRequired(true); //海拔信息
criteria.setBearingRequired(true); //方位信息
criteria.setPowerRequirement(Criteria.POWER_LOW); // 对电源需求
criteria.setCostAllowed(false); // 是否允许运营商收费
String bestProvider = locationManager.getBestProvider(criteria, true);
}
开始定位
开始定位前,我们需要检测是否开启了定位服务,如果没有开启,我们需要提示用户开启。
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
permissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 458);
}
然后我们需要定义一个位置变更监听器 LocationLisenter,但是在此之前我们需要先创造一个locationShow方法来表示出监听器所收集到的定位信息
private void locationShow(Location location){
String desc = String.format("获取位置时间为%是%s\n 精度为%f,纬度为%f \n高度为%d,精度
为%d米",location.getTime(),location.getLongitude(),
location.getLatitude(),Math.round(location.getAltitude()),
Math.round(location.getAccuracy()));
display = (TextView) findViewById(R.id.location_display);
display.setText(desc);
}
其中Location即表示包含的位置信息以及常用的获取方法如下
location.getTime | 获取时间信息 |
location.getLongitude | 获取经度信息 |
location.getLatitude | 获取纬度信息 |
location.getAltitude | 获取海拔信息 |
location.getAccuracy | 获取精度信息 |
定义位置监听器 LocationLisenter
private final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
locationShow(location);
}
};
将位置监听器设置到定位管理器中
locationManager.requestLocationUpdates(provider, 300, 0, locationListener);
我们再添加一个功能,用于得到上一次的位置信息
Location location = locationManager.getLastKnownLocation(provider);
于是我们便可以初步的获取位置信息,总览如下
private void startLocation(String provider){
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION};
ActivityCompat.requestPermissions(this, permissions, 458);
}
// 设置位置变更监听器
locationManager.requestLocationUpdates(provider, 300, 0, locationListener);
Location location = locationManager.getLastKnownLocation(provider);
}
// 定义一个位置变更监听器
private final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
locationShow(location);
}
};
@SuppressLint("DefaultLocale")
private void locationShow(Location location){
String desc = String.format("获取位置时间为%是%s\n 精度为%f,纬度为%f \n高度为%d,精度为%d米",
location.getTime(),location.getLongitude(), location.getLatitude(),
Math.round(location.getAltitude()), Math.round(location.getAccuracy())
);
display = (TextView) findViewById(R.id.location_display);
display.setText(desc);
}
于是我们得到了100毫秒刷新一次的位置信息
虽然这可以让我们较为精准地获得到位置信息,但对我们实际获得地理信息并不是很直观,需要我们另外将纬度信息到他处进行转换。这里我是决定用了高德地图地开发平台,流程和这里差不多,但可以更直接地获取到地理信息。