一、概述
在项目中需要获取用户所在位置的经纬度和城市上送给风控系统。一般来说,定位有两种方式:
-
用第三方SDK定位,如百度地图、高德地图、谷歌地图;
用Android原生SDK中的api定位;
本文讲述定位的第二种方式--用Android原生的SDK中的api定位,如果项目定位要求较高还是建议使用第三方地图库。
二、Android原生SDK中的api定位
Android原生方式获取经纬度两种定位方式:GPS定位和Wifi定位
GPS定位相比Wifi定位更精准且可在无网络情况下使用,但在室内基本暴毙无法使用。 WiFi定位没有室内外限制,也不需要开启GPS但需要联网。但测试发现WiFi定位时onLocationChanged函数(用于监听经纬度变化)触发间隔无法小于30s。示例代码如下:
public class TestLocationActivity extends AppCompatActivity { public static final int LOCATION_CODE = 301; private LocationManager locationManager; private String locationProvider = null; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getLocation(); } private void getLocation(){ //1.获取位置管理器 locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //2.获取位置提供器,GPS或是NetWork List<String> providers = locationManager.getProviders(true); if (providers.contains(LocationManager.GPS_PROVIDER)) { //如果是GPS locationProvider = LocationManager.GPS_PROVIDER; Log.v("TAG", "定位方式GPS"); } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) { //如果是Network locationProvider = LocationManager.NETWORK_PROVIDER; Log.v("TAG", "定位方式Network"); }else { Toast.makeText(this, "没有可用的位置提供器", Toast.LENGTH_SHORT).show(); return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //获取权限(如果没有开启权限,会弹出对话框,询问是否开启权限) if (ContextCompat.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}, LOCATION_CODE); } else { //3.获取上次的位置,一般第一次运行,此值为null Location location = locationManager.getLastKnownLocation(locationProvider); if (location!=null){ Toast.makeText(this, location.getLongitude() + " " + location.getLatitude() + "",Toast.LENGTH_SHORT).show(); Log.v("TAG", "获取上次的位置-经纬度:"+location.getLongitude()+" "+location.getLatitude()); getAddress(location); }else{ //监视地理位置变化,第二个和第三个参数分别为更新的最短时间minTime和最短距离minDistace locationManager.requestLocationUpdates(locationProvider, 3000, 1,locationListener); } } } else { Location location = locationManager.getLastKnownLocation(locationProvider); if (location!=null){ Toast.makeText(this, location.getLongitude() + " " + location.getLatitude() + "", Toast.LENGTH_SHORT).show(); Log.v("TAG", "获取上次的位置-经纬度:"+location.getLongitude()+" "+location.getLatitude()); getAddress(location); }else{ //监视地理位置变化,第二个和第三个参数分别为更新的最短时间minTime和最短距离minDistace locationManager.requestLocationUpdates(locationProvider, 3000, 1,locationListener); } } } public LocationListener locationListener = new LocationListener() { // Provider的状态在可用、暂时不可用和无服务三个状态直接切换时触发此函数 @Override public void onStatusChanged(String provider, int status, Bundle extras) { } // Provider被enable时触发此函数,比如GPS被打开 @Override public void onProviderEnabled(String provider) { } // Provider被disable时触发此函数,比如GPS被关闭 @Override public void onProviderDisabled(String provider) { } //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发 @Override public void onLocationChanged(Location location) { if (location != null) { //如果位置发生变化,重新显示地理位置经纬度 Toast.makeText(TestLocationActivity.this, location.getLongitude() + " " + location.getLatitude() + "", Toast.LENGTH_SHORT).show(); Log.v("TAG", "监视地理位置变化-经纬度:"+location.getLongitude()+" "+location.getLatitude()); } } }; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case LOCATION_CODE: if(grantResults.length > 0 && grantResults[0] == getPackageManager().PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "申请权限", Toast.LENGTH_LONG).show(); try { List<String> providers = locationManager.getProviders(true); if (providers.contains(LocationManager.NETWORK_PROVIDER)) { //如果是Network locationProvider = LocationManager.NETWORK_PROVIDER; }else if (providers.contains(LocationManager.GPS_PROVIDER)) { //如果是GPS locationProvider = LocationManager.GPS_PROVIDER; } Location location = locationManager.getLastKnownLocation(locationProvider); if (location!=null){ Toast.makeText(this, location.getLongitude() + " " + location.getLatitude() + "", Toast.LENGTH_SHORT).show(); Log.v("TAG", "获取上次的位置-经纬度:"+location.getLongitude()+" "+location.getLatitude()); }else{ // 监视地理位置变化,第二个和第三个参数分别为更新的最短时间minTime和最短距离minDistace locationManager.requestLocationUpdates(locationProvider, 0, 0,locationListener); } }catch (SecurityException e){ e.printStackTrace(); } } else { Toast.makeText(this, "缺少权限", Toast.LENGTH_LONG).show(); finish(); } break; } } //获取地址信息:城市、街道等信息 private List<Address> getAddress(Location location) { List<Address> result = null; try { if (location != null) { Geocoder gc = new Geocoder(this, Locale.getDefault()); result = gc.getFromLocation(location.getLatitude(), location.getLongitude(), 1); Toast.makeText(this, "获取地址信息:"+result.toString(), Toast.LENGTH_LONG).show(); Log.v("TAG", "获取地址信息:"+result.toString()); } } catch (Exception e) { e.printStackTrace(); } return result; } @Override protected void onDestroy() { super.onDestroy(); locationManager.removeUpdates(locationListener); } }
在AndroidManifest.xml加权限
<!-- 粗略的位置权限 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <!-- 精确的位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.INTERNET"></uses-permission>
三、总结
首先将手机定位服务设置,调到下图所示:
这可能会获取不到经纬度。为什么在网络和GPS都可用的情况下只执行GPS而不是网络?也许是从精确度考虑的,但是走GPS进入监听的listener后,不会执行onLocationChanged()方法,因为我是在室内,没有移动,所以如果你获取不到经纬度,就要将定位服务调到仅使用网络定位或者关闭手机GPS这样就可以获取到了。