在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<String> perms, boolean isAllGranted) {
        if (isAllGranted) {
            startLocate();
        }
    }

    @Override
    public void onPermissionsDenied(int requestCode, List<String> 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}: 是<br>{@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}: 是<br>{@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);
    }

    /**
     * 注册
     * <p>使用完记得调用{@link #unregister()}</p>
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.INTERNET"/>}</p>
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>}</p>
     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>}</p>
     * <p>如果{@code minDistance}为0,则通过{@code minTime}来定时更新;</p>
     * <p>{@code minDistance}不为0,则以{@code minDistance}为准;</p>
     * <p>两者都为0,则随时刷新。</p>
     *
     * @param minTime     位置信息更新周期(单位:毫秒)
     * @param minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米)
     * @param listener    位置刷新的回调接口
     * @return {@code true}: 初始化成功<br>{@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<Address> 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<String> 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<String> granted = new ArrayList<>();

        //拒绝的权限
        List<String> 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<String> 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<String> perms, boolean isAllGranted);

        /**
         */
        void onPermissionsDenied(int requestCode, List<String> perms);

    }
}