在Android开发其中。常常须要用到定位功能,尤其是依赖于地理位置功能的应用。非常多人喜欢使用百度地图,高德地图提供的sdk。开放API,可是在只须要经纬度,或者城市,街道地址等信息。并不须要提供预览地图,地图界面的应用中。这时,不须要使用百度地图。高德地图。这样做只会添加apk的体积。怎么办呢?
事实上LocationManager,Geocoder这些Android API给我们提供的这些类就能够满足了。
以下笔者就来讲讲怎样利用LocationManager获取经纬度,并利用Geocoder将经纬度转换为城市街道等信息。
添加权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
LocationManager定位管理者实例通过getSystemService()方式获得:
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
定位大致分为三大类:GPS定位;Network定位;AGPS定位。
而Network又细分为WIFI定位和基站定位。下面详细讲解每种定位:
GPS定位:需要GPS硬件支持,直接和卫星交互来获取当前经纬度。
优点:速度快、精度高、可在无网络情况下使用。
缺点:首次连接时间长、只能在户外已经开阔地使用,设备上方有遮挡物就不行了、比较耗电。
代码:
/**
* GPS定位
*/
public class GpsLocationActivity extends AppCompatActivity implements PermissionUtils.PermissionCallbacks {
private LocationManager mLocationManager;
private String[] permissions = {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
private static final int REQUEST_PERMISSION_CODE = 12;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gps_location);
}
public void doClick(View view) {
if (!PermissionUtils.hasPermissions(this, permissions)) {
PermissionUtils.requestPermissions(this, REQUEST_PERMISSION_CODE, permissions);
} else {
startLocate();
}
}
private void startLocate() {
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean providerEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (providerEnabled) { //GPS已开启
/**
* 绑定监听
* 参数1,设备:有GPS_PROVIDER和NETWORK_PROVIDER两种,前者是GPS,后者是GPRS以及WIFI定位
* 参数2,位置信息更新周期.单位是毫秒
* 参数3,位置变化最小距离:当位置距离变化超过此值时,将更新位置信息
* 参数4,监听
* 备注:参数2和3,如果参数3不为0,则以参数3为准;参数3为0,则通过时间来定时更新;两者为0,则随时刷新
*/
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
} else {
Toast.makeText(this, "请打开GPS", Toast.LENGTH_SHORT).show();
}
}
private LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
//位置信息变化时触发
Log.e("xyh", "定位方式:" + location.getProvider());
Log.e("xyh", "纬度:" + location.getLatitude());
Log.e("xyh", "经度:" + location.getLongitude());
Log.e("xyh", "海拔:" + location.getAltitude());
Log.e("xyh", "时间:" + location.getTime());
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
//GPS状态变化时触发
switch (status) {
case LocationProvider.AVAILABLE:
Log.e("onStatusChanged", "当前GPS状态为可见状态");
break;
case LocationProvider.OUT_OF_SERVICE:
Log.e("onStatusChanged", "当前GPS状态为服务区外状态");
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
Log.e("onStatusChanged", "当前GPS状态为暂停服务状态");
break;
}
}
@Override
public void onProviderEnabled(String provider) {
//GPS开启时触发
Log.e("xyh", "onProviderEnabled: ");
}
@Override
public void onProviderDisabled(String provider) {
//GPS禁用时触发
Log.e("xyh", "onProviderDisabled: ");
}
};
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionUtils.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsAllGranted(int requestCode, List perms, boolean isAllGranted) {
if (isAllGranted) {
startLocate();
}
}
@Override
public void onPermissionsDenied(int requestCode, List perms) {
if (PermissionUtils.somePermissionPermanentlyDenied(this, perms)) {
PermissionUtils.showDialogGoToAppSettting(this);
} else {
PermissionUtils.showPermissionReason(requestCode, this, permissions, "需要定位权限");
}
}
}
定位结果:
08-22 15:34:48.002 10039-10039/com.xiaoyehai.locationmanager E/xyh: 定位方式:gps
08-22 15:34:48.002 10039-10039/com.xiaoyehai.locationmanager E/xyh: 纬度:23.16859603
08-22 15:34:48.002 10039-10039/com.xiaoyehai.locationmanager E/xyh: 经度:113.34331354
08-22 15:34:48.002 10039-10039/com.xiaoyehai.locationmanager E/xyh: 海拔:77.0
08-22 15:34:48.002 10039-10039/com.xiaoyehai.locationmanager E/xyh: 时间:1534923287000
网络定位:
优点:它的优势在于收环境影响较小
缺点: 首先需要消耗流量、其实精度没有GPS那么准确,大概在十几米到几十米之间。
定位工具类:LocationUtils
/**
* 定位工具类
* Created by xiaoyehai on 2018/8/22 0022.
*/
public class LocationUtils {
private static OnLocationChangeListener mListener;
private static MyLocationListener myLocationListener;
private static LocationManager mLocationManager;
private LocationUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 判断Gps是否可用
*
* @return {@code true}: 是
{@code false}: 否
*/
public static boolean isGpsEnabled(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
/**
* 判断定位是否可用
*
* @return {@code true}: 是
{@code false}: 否
*/
public static boolean isLocationEnabled(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER) || lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
/**
* 打开Gps设置界面
*/
public static void openGpsSettings(Context context) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 注册
* 使用完记得调用{@link #unregister()}
* 需添加权限 {@code }
* 需添加权限 {@code }
* 需添加权限 {@code }
* 如果{@code minDistance}为0,则通过{@code minTime}来定时更新;
* {@code minDistance}不为0,则以{@code minDistance}为准;
* 两者都为0,则随时刷新。
*
* @param minTime 位置信息更新周期(单位:毫秒)
* @param minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米)
* @param listener 位置刷新的回调接口
* @return {@code true}: 初始化成功
{@code false}: 初始化失败
*/
public static boolean register(Context context, long minTime, long minDistance, OnLocationChangeListener listener) {
if (listener == null)
return false;
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
mListener = listener;
if (!isLocationEnabled(context)) {
Toast.makeText(context, "无法定位,请打开定位服务", Toast.LENGTH_SHORT).show();
return false;
}
String provider = mLocationManager.getBestProvider(getCriteria(), true);
Location location = mLocationManager.getLastKnownLocation(provider);
if (location != null)
listener.getLastKnownLocation(location);
if (myLocationListener == null)
myLocationListener = new MyLocationListener();
mLocationManager.requestLocationUpdates(provider, minTime, minDistance, myLocationListener);
return true;
}
/**
* 注销
*/
public static void unregister() {
if (mLocationManager != null) {
if (myLocationListener != null) {
mLocationManager.removeUpdates(myLocationListener);
myLocationListener = null;
}
mLocationManager = null;
}
}
/**
* 设置定位参数
*
* @return {@link Criteria}
*/
private static Criteria getCriteria() {
Criteria criteria = new Criteria();
//设置定位精确度 Criteria.ACCURACY_COARSE比较粗略,Criteria.ACCURACY_FINE则比较精细
criteria.setAccuracy(Criteria.ACCURACY_FINE);
//设置是否要求速度
criteria.setSpeedRequired(false);
// 设置是否允许运营商收费
criteria.setCostAllowed(false);
//设置是否需要方位信息
criteria.setBearingRequired(false);
//设置是否需要海拔信息
criteria.setAltitudeRequired(false);
// 设置对电源的需求
criteria.setPowerRequirement(Criteria.POWER_LOW);
return criteria;
}
/**
* 根据经纬度获取地理位置
*
* @param context 上下文
* @param latitude 纬度
* @param longitude 经度
* @return {@link Address}
*/
public static Address getAddress(Context context, double latitude, double longitude) {
Geocoder geocoder = new Geocoder(context, Locale.getDefault());
try {
List addresses = geocoder.getFromLocation(latitude, longitude, 1);
if (addresses.size() > 0)
return addresses.get(0);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据经纬度获取所在国家
*
* @param context 上下文
* @param latitude 纬度
* @param longitude 经度
* @return 所在国家
*/
public static String getCountryName(Context context, double latitude, double longitude) {
Address address = getAddress(context, latitude, longitude);
return address == null ? "unknown" : address.getCountryName();
}
/**
* 根据经纬度获取所在地
*
* @param context 上下文
* @param latitude 纬度
* @param longitude 经度
* @return 所在地
*/
public static String getLocality(Context context, double latitude, double longitude) {
Address address = getAddress(context, latitude, longitude);
return address == null ? "unknown" : address.getLocality();
}
/**
* 根据经纬度获取所在街道
*
* @param context 上下文
* @param latitude 纬度
* @param longitude 经度
* @return 所在街道
*/
public static String getStreet(Context context, double latitude, double longitude) {
Address address = getAddress(context, latitude, longitude);
return address == null ? "unknown" : address.getAddressLine(0);
}
private static class MyLocationListener implements LocationListener {
/**
* 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
*
* @param location 坐标
*/
@Override
public void onLocationChanged(Location location) {
if (mListener != null) {
mListener.onLocationChanged(location);
}
}
/**
* provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数
*
* @param provider 提供者
* @param status 状态
* @param extras provider可选包
*/
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (mListener != null) {
mListener.onStatusChanged(provider, status, extras);
}
switch (status) {
case LocationProvider.AVAILABLE:
Log.e("onStatusChanged", "当前GPS状态为可见状态");
break;
case LocationProvider.OUT_OF_SERVICE:
Log.e("onStatusChanged", "当前GPS状态为服务区外状态");
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
Log.e("onStatusChanged", "当前GPS状态为暂停服务状态");
break;
}
}
/**
* provider被enable时触发此函数,比如GPS被打开
*/
@Override
public void onProviderEnabled(String provider) {
}
/**
* provider被disable时触发此函数,比如GPS被关闭
*/
@Override
public void onProviderDisabled(String provider) {
}
}
public interface OnLocationChangeListener {
/**
* 获取最后一次保留的坐标
*
* @param location 坐标
*/
void getLastKnownLocation(Location location);
/**
* 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
*
* @param location 坐标
*/
void onLocationChanged(Location location);
/**
* provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数
*
* @param provider 提供者
* @param status 状态
* @param extras provider可选包
*/
void onStatusChanged(String provider, int status, Bundle extras);//位置状态发生改变
}
}
使用:
public void doClick(View view) {
LocationUtils.register(this, 0, 0, new LocationUtils.OnLocationChangeListener() {
@Override
public void getLastKnownLocation(Location location) {
Log.e("xyh", "onLocationChanged: " + location.getLatitude());
}
@Override
public void onLocationChanged(Location location) {
//位置信息变化时触发
Log.e("xyh", "定位方式:" + location.getProvider());
Log.e("xyh", "纬度:" + location.getLatitude());
Log.e("xyh", "经度:" + location.getLongitude());
Log.e("xyh", "海拔:" + location.getAltitude());
Log.e("xyh", "时间:" + location.getTime());
Log.e("xyh", "国家:" + LocationUtils.getCountryName(NetWorKLocationActivity.this, location.getLatitude(), location.getLongitude()));
Log.e("xyh", "获取地理位置:" + LocationUtils.getAddress(NetWorKLocationActivity.this, location.getLatitude(), location.getLongitude()));
Log.e("xyh", "所在地:" + LocationUtils.getLocality(NetWorKLocationActivity.this, location.getLatitude(), location.getLongitude()));
Log.e("xyh", "所在街道:" + LocationUtils.getStreet(NetWorKLocationActivity.this, location.getLatitude(), location.getLongitude()));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
LocationUtils.unregister();
}
}
定位结果:
08-22 15:43:47.445 11529-11529/com.xiaoyehai.locationmanager E/xyh: 定位方式:network
08-22 15:43:47.445 11529-11529/com.xiaoyehai.locationmanager E/xyh: 纬度:23.168848
08-22 15:43:47.445 11529-11529/com.xiaoyehai.locationmanager E/xyh: 经度:113.343307
08-22 15:43:47.445 11529-11529/com.xiaoyehai.locationmanager E/xyh: 海拔:0.0
08-22 15:43:47.446 11529-11529/com.xiaoyehai.locationmanager E/xyh: 时间:1534923825508
08-22 15:43:47.847 11529-11529/com.xiaoyehai.locationmanager E/xyh: 国家:中国
08-22 15:43:48.182 11529-11529/com.xiaoyehai.locationmanager E/xyh: 获取地理位置:Address[addressLines=[0:"广东省广州市天河区xxx219",1:"广州市轻工职业学校",2:"长兴智汇商务中心-G座",3:"xx大厦",4:"广州天河软件园(智汇分园)",5:"长福路小区",6:"广州华南商贸职业学院",7:"长兴·智汇商务中心",8:"永佳连锁超市(广州)",9:"广州市创艺艺术职业学校",10:"广州嘉福利晶酒店(天河店)"],feature=null,admin=广东省,sub-admin=null,locality=广州市,thoroughfare=长福路,postalCode=null,countryCode=0,countryName=中国,hasLatitude=true,latitude=23.172007566919543,hasLongitude=true,longitude=113.35534387635434,phone=null,url=null,extras=null]
08-22 15:43:48.580 11529-11529/com.xiaoyehai.locationmanager E/xyh: 所在地:广州市
08-22 15:43:48.844 11529-11529/com.xiaoyehai.locationmanager E/xyh: 所在街道:广东省广州市天河区XX路258741
PermissionUtils:
/**
* 动态申请权限工具类
* Created by xiaoyehai on 2018/4/25 0025.
*/
public class PermissionUtils {
public static final int GOTO_SEETING_CODE = 152;
/**
* 判断是否有权限
*
* @param context
* @param perms
* @return
*/
public static boolean hasPermissions(@NonNull Context context, @Size(min = 1) @NonNull String... perms) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
if (context == null) {
throw new IllegalArgumentException("Can't check permissions for null context");
}
for (String perm : perms) {
if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* 申请权限
*/
public static void requestPermissions(@NonNull Activity activity, int requestCode, String[] permissions) {
List permissionList = new ArrayList<>();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(permission);
}
}
String[] permissionsArray = permissionList.toArray(new String[permissionList.size()]);//将List转为数组
if (permissionList.isEmpty()) {
//不可能为空
} else {
ActivityCompat.requestPermissions(activity, permissionsArray, requestCode);
//返回结果onRequestPermissionsResult
}
}
/**
* 申请权限的回调
*
* @param requestCode 请求权限时传入的请求码,用于区别是哪一次请求的
* @param permissions 所请求的所有权限的数组
* @param grantResults 权限授予结果,和 permissions 数组参数中的权限一一对应,元素值为两种情况,如下:
* 授予: PackageManager.PERMISSION_GRANTED
* 拒绝: PackageManager.PERMISSION_DENIED
*/
public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults, @NonNull PermissionCallbacks callBack) {
//授予的权限。
List granted = new ArrayList<>();
//拒绝的权限
List denied = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
String perm = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
granted.add(perm);
} else {
denied.add(perm);
}
}
if (null != callBack) {
if (denied.isEmpty()) {
callBack.onPermissionsAllGranted(requestCode, granted, denied.isEmpty());
}
if (!denied.isEmpty()) {
callBack.onPermissionsDenied(requestCode, denied);
}
}
}
/**
* 用户是否拒绝权限,并检查“不要提醒”。
*
* @param activity
* @param perms
* @return
*/
public static boolean somePermissionPermanentlyDenied(Activity activity, @NonNull List perms) {
for (String deniedPermission : perms) {
if (permissionPermanentlyDenied(activity, deniedPermission)) {
return true;
}
}
return false;
}
public static boolean permissionPermanentlyDenied(Activity activity, @NonNull String perms) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, perms)) {
return true;
}
return false;
}
public static void showDialogGoToAppSettting(final Activity activity) {
AlertDialog dialog = new AlertDialog.Builder(activity)
.setMessage("去设置界面开启权限")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 跳转到应用设置界面
goToAppSetting(activity);
}
}).setCancelable(false).show();
}
/**
* 跳转到应用设置界面
*/
public static void goToAppSetting(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent, GOTO_SEETING_CODE);
}
public static void showPermissionReason(final int requestCode, final Activity activity, final String[] permission, String s) {
AlertDialog dialog = new AlertDialog.Builder(activity)
.setMessage(s)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
requestPermissions(activity, requestCode, permission);
}
})
.setCancelable(false).show();
}
public interface PermissionCallbacks {
/**
* @param isAllGranted 是否全部同意
*/
void onPermissionsAllGranted(int requestCode, List perms, boolean isAllGranted);
/**
*/
void onPermissionsDenied(int requestCode, List perms);
}
}