Android 获取定位信息的工具类实现

下面是一个完整的 Android 定位工具类实现,支持 GPS 和网络定位,兼容 Android 10 (API 29) 及以上版本的权限管理,并提供简洁的 API 供调用。

图片[1]_Android 获取定位信息的工具类实现_知途无界

一、工具类代码实现

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

/**
 * 定位工具类 - 支持GPS和网络定位
 * 注意:需要在AndroidManifest.xml中声明权限和meta-data
 */
public class LocationUtils {
    private static final long MIN_TIME_MS = 1000 * 10; // 最小更新时间间隔(10秒)
    private static final float MIN_DISTANCE_M = 10;   // 最小更新距离(10米)

    private Context mContext;
    private LocationManager mLocationManager;
    private LocationListener mLocationListener;
    private LocationCallback mCallback;

    public interface LocationCallback {
        void onLocationSuccess(Location location);  // 定位成功回调
        void onLocationFailure(String error);       // 定位失败回调
    }

    public LocationUtils(Context context) {
        this.mContext = context.getApplicationContext();
        mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
    }

    /**
     * 开始定位(异步获取一次位置)
     * @param callback 定位结果回调
     */
    public void requestLocation(@NonNull LocationCallback callback) {
        this.mCallback = callback;

        if (!checkLocationPermission()) {
            if (mCallback != null) {
                mCallback.onLocationFailure("缺少定位权限(需要ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION)");
            }
            return;
        }

        // 优先使用GPS定位
        if (isGpsEnabled()) {
            requestGpsLocation();
        } 
        // 其次使用网络定位
        else if (isNetworkEnabled()) {
            requestNetworkLocation();
        } 
        // 无可用定位方式
        else {
            if (mCallback != null) {
                mCallback.onLocationFailure("无可用定位服务(GPS和网络均未开启)");
            }
        }
    }

    /**
     * 检查是否已授予定位权限
     */
    private boolean checkLocationPermission() {
        return ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * 判断GPS是否开启
     */
    private boolean isGpsEnabled() {
        return mLocationManager != null && mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }

    /**
     * 判断网络定位是否开启
     */
    private boolean isNetworkEnabled() {
        return mLocationManager != null && mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

    /**
     * 请求GPS定位
     */
    private void requestGpsLocation() {
        if (mLocationListener != null) {
            mLocationManager.removeUpdates(mLocationListener);
        }

        mLocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                stopLocationUpdates();
                if (mCallback != null && location != null) {
                    mCallback.onLocationSuccess(location);
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {}

            @Override
            public void onProviderEnabled(String provider) {}

            @Override
            public void onProviderDisabled(String provider) {
                if (mCallback != null) {
                    mCallback.onLocationFailure("GPS定位已关闭");
                }
            }
        };

        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER,
                    MIN_TIME_MS,
                    MIN_DISTANCE_M,
                    mLocationListener
            );
        } catch (SecurityException e) {
            if (mCallback != null) {
                mCallback.onLocationFailure("GPS定位权限异常:" + e.getMessage());
            }
        }
    }

    /**
     * 请求网络定位
     */
    private void requestNetworkLocation() {
        if (mLocationListener != null) {
            mLocationManager.removeUpdates(mLocationListener);
        }

        mLocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                stopLocationUpdates();
                if (mCallback != null && location != null) {
                    mCallback.onLocationSuccess(location);
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {}

            @Override
            public void onProviderEnabled(String provider) {}

            @Override
            public void onProviderDisabled(String provider) {
                if (mCallback != null) {
                    mCallback.onLocationFailure("网络定位已关闭");
                }
            }
        };

        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER,
                    MIN_TIME_MS,
                    MIN_DISTANCE_M,
                    mLocationListener
            );
        } catch (SecurityException e) {
            if (mCallback != null) {
                mCallback.onLocationFailure("网络定位权限异常:" + e.getMessage());
            }
        }
    }

    /**
     * 停止定位更新(防止内存泄漏)
     */
    private void stopLocationUpdates() {
        if (mLocationManager != null && mLocationListener != null) {
            mLocationManager.removeUpdates(mLocationListener);
            mLocationListener = null;
        }
    }

    /**
     * 释放资源(建议在Activity/Fragment销毁时调用)
     */
    public void release() {
        stopLocationUpdates();
        mCallback = null;
    }
}

二、AndroidManifest.xml 必要配置

AndroidManifest.xml 中添加以下内容:

<!-- 基础定位权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Android 10+ 需要声明前台服务类型(如果需要在后台定位) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- 如果使用Google Play服务的高精度定位,可添加(可选) -->
<!-- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> -->

⚠️ 注意:

  • ACCESS_FINE_LOCATION:获取GPS高精度定位(包括经纬度、海拔、速度等)。
  • ACCESS_COARSE_LOCATION:获取网络定位(精度较低,通常只能到城市级别)。
  • 从 Android 6.0 (API 23) 开始,定位权限属于危险权限,必须动态申请
  • ​**从 Android 10 (API 29) 开始,后台定位需要额外声明 ACCESS_BACKGROUND_LOCATION**​(本工具类默认仅实现前台定位)。

三、动态权限申请(Activity/Fragment中调用)

在使用定位工具类前,必须确保已获取定位权限。以下是动态申请权限的示例代码(Kotlin/Java 均可):

1. 权限请求代码(Java 示例)

// 在Activity或Fragment中
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001;

private void checkAndRequestLocationPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
            || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
        // 已有权限,直接调用定位
        startLocation();
    } else {
        // 请求权限(兼容Android 11+的单次授权弹窗)
        ActivityCompat.requestPermissions(
                this,
                new String[]{
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION
                },
                LOCATION_PERMISSION_REQUEST_CODE
        );
    }
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
        boolean hasPermission = false;
        for (int result : grantResults) {
            if (result == PackageManager.PERMISSION_GRANTED) {
                hasPermission = true;
                break;
            }
        }
        if (hasPermission) {
            startLocation(); // 用户同意,开始定位
        } else {
            Toast.makeText(this, "定位权限被拒绝,无法获取位置", Toast.LENGTH_SHORT).show();
        }
    }
}

// 实际调用定位工具类的方法
private void startLocation() {
    LocationUtils locationUtils = new LocationUtils(this);
    locationUtils.requestLocation(new LocationUtils.LocationCallback() {
        @Override
        public void onLocationSuccess(Location location) {
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();
            Log.d("LocationUtils", "定位成功: 纬度=" + latitude + ", 经度=" + longitude);
            // TODO: 处理定位成功逻辑(如更新UI、上传服务器等)
        }

        @Override
        public void onLocationFailure(String error) {
            Log.e("LocationUtils", "定位失败: " + error);
            Toast.makeText(MainActivity.this, "定位失败: " + error, Toast.LENGTH_SHORT).show();
        }
    });
}

2. 调用示例(在按钮点击或页面初始化时)

// 例如在Activity的onCreate中调用
checkAndRequestLocationPermission();

四、工具类特性说明

特性说明
定位方式优先使用GPS,失败后自动降级到网络定位
权限兼容自动检查 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
异步回调通过 LocationCallback 返回定位结果(成功/失败)
资源释放提供 release() 方法,防止内存泄漏
低功耗设置最小更新时间(10秒)和距离(10米),避免频繁请求
错误处理捕获权限异常、服务不可用等常见错误

五、注意事项

  1. Android 10+ 后台定位限制
    如果需要在后台持续定位(如运动轨迹记录),需额外申请 ACCESS_BACKGROUND_LOCATION 权限,并声明前台服务(本工具类未包含,需自行扩展)。
  2. 高精度定位推荐
    如需更高精度的定位(如导航、打车),建议集成 ​Google Play服务定位API​ 或 ​百度/高德地图SDK​(提供更强大的定位能力和室内定位支持)。
  3. 模拟器测试
    在Android Studio模拟器中测试时,可通过 ​Extended Controls → Location​ 手动设置经纬度模拟定位。
  4. 真机调试
    真机需确保 ​GPS和网络功能正常,并在系统设置中开启定位服务。

通过以上工具类,你可以快速在Android应用中实现稳定、安全的定位功能,适用于天气、地图、本地生活等常见场景。如需进一步扩展(如持续定位、地理围栏等),可在此基础上继续完善。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞13 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容