睿诚科技协会

Android GPS与网络定位,哪种更准?

  1. 核心概念:GPS vs. 网络定位
  2. Android 定位系统架构:Fused Location Provider
  3. 实现定位的步骤(以现代 API 为准)
  4. 关键代码示例
  5. 最佳实践与注意事项
  6. 如何选择定位方式?

核心概念:GPS vs. 网络定位

要明白 GPS 和网络定位是两种完全不同的技术,它们各有优缺点。

Android GPS与网络定位,哪种更准?-图1
(图片来源网络,侵删)
特性 GPS (Global Positioning System) 网络定位
技术原理 通过手机接收多颗 GPS 卫星发射的信号,通过三角计算法精确确定设备的经纬度、海拔等信息。 通过连接的 Wi-Fi 或移动网络基站来估算位置。
精度 ,室外开阔地带可达 5 - 10 米,甚至更高。 较低,Wi-Fi 定位精度在几十米到几百米;基站定位精度在几百米到几公里。
首次定位时间 慢 (TTFF - Time to First Fix),冷启动可能需要几十秒甚至几分钟,因为需要下载卫星星历数据。 ,几乎是即时的,因为它只需要查询网络信息。
耗电量 ,持续开启 GPS 模块会显著消耗电量。 ,查询网络信息耗电量相对较小。
使用环境 依赖天空,在室内、隧道、高楼林立的“城市峡谷”中,信号会变弱或完全丢失。 依赖网络,只要有手机信号(2G/3G/4G/5G)或 Wi-Fi,就可以定位。
数据费用 免费,接收卫星信号不产生费用。 可能产生费用,查询基站信息需要与运营商通信,但通常流量很小。

Android 定位系统架构:Fused Location Provider

在 Android 6.0 (API 23) 及更高版本中,Google 推荐使用 Fused Location Provider,它不是一个简单的 GPS 或网络定位,而是一个智能的、统一的定位服务。

Fused Location Provider (FLP) 的核心思想是:

  • 智能融合:它会根据你的应用请求的精度、设备当前的状态(如是否插电、网络状况、已知的辅助数据等),自动在 GPS、Wi-Fi、蓝牙、基站等多种定位方式之间做出最优选择
  • 降低功耗:通过智能调度,它可以在保证精度的前提下,最大限度地降低定位对电量的消耗。
  • 简化开发:开发者不再需要手动切换或组合不同的定位提供者(GPS_PROVIDER, NETWORK_PROVIDER),只需告诉系统你想要的精度级别,系统会搞定一切。

在现代 Android 开发中,你应该始终优先使用 Fused Location Provider


实现定位的步骤(以现代 API 为准)

下面是实现定位功能的完整流程,我们将使用 Fused Location Provider。

Android GPS与网络定位,哪种更准?-图2
(图片来源网络,侵删)

步骤 1:添加权限

AndroidManifest.xml 中必须声明以下权限:

<!-- 用于访问网络,网络定位需要此权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 用于访问 GPS,GPS 定位需要此权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 用于访问网络(Wi-Fi 或基站)进行定位,精度较低 -->
<!-- 如果只申请 ACCESS_FINE_LOCATION,则此权限会被隐式包含,但最好显式声明 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 从 Android 10 (API 29) 开始,在后台获取位置需要此权限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

步骤 2:动态请求权限(Android 6.0+)

在运行时,特别是针对 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION,必须向用户请求授权。

// 在 Activity 或 Fragment 中
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) 
    != PackageManager.PERMISSION_GRANTED) {
    // 权限尚未授予,请求权限
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            MY_PERMISSIONS_REQUEST_LOCATION);
} else {
    // 权限已授予,可以开始定位
    startLocationUpdates();
}

步骤 3:创建 LocationCallbackLocationRequest

LocationCallback 是一个抽象类,用于接收位置更新,你需要实现它的 onLocationResult 方法。

LocationRequest 用于告诉 FLP 你对定位更新的要求,

Android GPS与网络定位,哪种更准?-图3
(图片来源网络,侵删)
  • setPriority: 定位精度优先级。
    • PRIORITY_HIGH_ACCURACY: 最高精度,优先使用 GPS。
    • PRIORITY_BALANCED_POWER_ACCURACY: 平衡精度和功耗,优先使用网络定位。
    • PRIORITY_LOW_POWER: 低功耗,只使用网络定位。
    • PRIORITY_NO_POWER: 不耗电,只在其他应用请求位置时顺便获取。
  • setInterval: 请求位置更新的最小间隔时间(毫秒),注意,这只是一个请求,系统可能会根据优化策略返回更频繁或更少的数据。
  • setFastestInterval: 应用能处理位置更新的最快间隔时间,这有助于防止 UI 线程阻塞。
private LocationCallback locationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {
        if (locationResult == null) {
            return;
        }
        // 获取最新的位置信息
        Location location = locationResult.getLastLocation();
        // 在这里更新 UI 或处理位置数据
        updateUI(location);
    }
};
private LocationRequest locationRequest;
private void createLocationRequest() {
    locationRequest = LocationRequest.create();
    locationRequest.setInterval(10000); // 10秒
    locationRequest.setFastestInterval(5000); // 5秒
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度
}

步骤 4:获取 FusedLocationProviderClient

这是与 FLP 交互的入口点。

private FusedLocationProviderClient fusedLocationClient;
// 在 onCreate 或类似的生命周期方法中
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

步骤 5:开始和停止位置更新

开始更新:

private void startLocationUpdates() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) 
        != PackageManager.PERMISSION_GRANTED) {
        // 权限检查
        return;
    }
    createLocationRequest();
    fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
}

停止更新:

当你的应用不再需要位置信息时(Activity 暂停或销毁时),必须停止位置更新,否则会持续消耗电量。

private void stopLocationUpdates() {
    fusedLocationClient.removeLocationUpdates(locationCallback);
}

步骤 6:在生命周期中管理定位

最佳实践是在 onStart() 中开始定位,在 onStop() 中停止定位。

@Override
protected void onStart() {
    super.onStart();
    if (checkPermissions()) {
        startLocationUpdates();
    }
}
@Override
protected void onStop() {
    super.onStop();
    stopLocationUpdates();
}

关键代码示例(一个完整 Activity)

public class MainActivity extends AppCompatActivity {
    private FusedLocationProviderClient fusedLocationClient;
    private LocationCallback locationCallback;
    private TextView locationTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        locationTextView = findViewById(R.id.locationTextView);
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        // 创建 LocationCallback
        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null) {
                    return;
                }
                Location location = locationResult.getLastLocation();
                String text = "纬度: " + location.getLatitude() + "\n" +
                              "经度: " + location.getLongitude() + "\n" +
                              "精度: " + location.getAccuracy() + " 米";
                locationTextView.setText(text);
            }
        };
        // 检查并请求权限
        if (!checkPermissions()) {
            requestPermissions();
        }
    }
    private boolean checkPermissions() {
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) 
               == PackageManager.PERMISSION_GRANTED;
    }
    private void requestPermissions() {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                1);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1) {
            if (grant
分享:
扫描分享到社交APP
上一篇
下一篇