# 如何获取 GPS 位置（纬度/经度）

> 用 Location + retrieveGPSData() 读取设备 GPS 坐标，或用 GPSThread 持续跟踪。

Source: https://crossfly.dev/zh/docs/cookbook/device/obter-gps — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何获取 GPS 位置（纬度/经度）

**目的。** 获取设备的 **纬度/经度**。若只需一次性读取，使用 `Location`
+ `retrieveGPSData()`；若需 **持续跟踪**，使用 `GPSThread`，它会自行管理线程。

## 使用的 API
- `new Location() throws IOException` — 打开 GPS（默认高精度）。也有 `new Location(int precision)`，可用 `Location.HIGH_GPS_PRECISION`（仅 GPS）或 `Location.LOW_GPS_PRECISION`（GPS + Google Play Services / 蜂窝 / wifi）。
- `boolean retrieveGPSData()` — 更新静态字段；获取到有效数据时返回 `true`，信号弱时返回 `false`（原因在 `Location.lowSignalReason` 中）。
- `Location.getLatitude()` / `getLongitude()`（静态）— 坐标；无信号时值为 `Location.INVALID`。
- `Location.velocity`（**单位为节/knots**）、`Location.pdop`（精度因子）、`Location.satellites`、`Location.direction` — 静态字段。
- `void stop()` — **必需**：关闭原生 API 并释放这个唯一实例。同一时刻只能有 **一个** `Location` 打开（若已存在，构造函数会抛出 `IOException`）。
- `GPSThread(boolean highPrecision, int intervalInMS, Runnable onLocation, RunnableWith<Exception> onError)` — 守护线程，在构造函数中 **自行启动**；`stopGps()` 停止并等待其结束。

> **注意事项。** 坐标字段是 **静态的** —— 同一时刻只有一个活跃读取。始终
> 针对 `Location.INVALID` 校验，并在信任位置前检查 `pdop`（LOW 可能返回
> 30m–1500m+）。结束时调用 `stop()`（`GPSThread` 会在每个周期为你完成这件事）。

## 完整示例（复制、粘贴并运行）

```java
package com.exemplo;

import dev.crossfly.ui.*;                   // Window, Component, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.device.*;               // Location, Device

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button acao = new Button("Obter GPS");
      acao.onAction(c ->
      {
         // 单次读取（在 paint thread 之外）— 始终用 try/finally 确保 stop()
         new Thread(() ->
         {
            Location loc = null;
            try
            {
               loc = new Location();                              // 打开 GPS（一次仅 1 个）
               for (int i = 0; i < 120 && !loc.retrieveGPSData(); i++)
                  Device.sleep(500);                              // 最多等待约 60s 以获取定位
               double lat = Location.getLatitude();
               double lon = Location.getLongitude();
               if (lat != Location.INVALID && lon != Location.INVALID)
                  Component.onUI(() -> resultado.setText("Lat " + lat + " / Lon " + lon));
               else
                  Component.onUI(() -> resultado.setText("Sem sinal de GPS"));
            }
            catch (java.io.IOException e)
            {
               // 已有一个 Location 打开，或打开 GPS 失败
               Component.onUI(() -> resultado.setText("Erro: " + e.getMessage()));
            }
            finally
            {
               if (loc != null) loc.stop();                       // 必须
            }
         }).start();
      });
      add(LAY_AUTO, acao);
   }
}
```

> 下面的片段只展示 **核心部分**。要运行它，请把它放进上面的 `MainWindow` 骨架里。

## 最小片段

```java
// 已在源码验证：Location.java (Location(), retrieveGPSData, getLatitude/Longitude, INVALID, stop); GPSThread.java (constructor, stopGps)
import dev.crossfly.device.*;              // Location
import dev.crossfly.core.*;                // GPSThread
import dev.crossfly.ui.*;                  // Component (onUI)

// 1) 单次读取（在 paint thread 之外）— 始终用 try/finally 确保 stop()
new Thread(() -> {
   Location loc = null;
   try {
      loc = new Location();                              // 打开 GPS（一次仅 1 个）
      for (int i = 0; i < 120 && !loc.retrieveGPSData(); i++)
         Device.sleep(500);                              // 最多等待约 60s 以获取定位
      double lat = Location.getLatitude();
      double lon = Location.getLongitude();
      if (lat != Location.INVALID && lon != Location.INVALID)
         Component.onUI(() -> mostrarPosicao(lat, lon)); // 返回在 paint thread 之外
   } catch (java.io.IOException e) {
      // 已有一个 Location 打开，或打开 GPS 失败
   } finally {
      if (loc != null) loc.stop();                       // 必须
   }
}).start();

// 2) 持续追踪 — GPSThread 自行启动；回调在 paint thread 之外
GPSThread gps = new GPSThread(true, 5000,                // 高精度，每 5s 一次
   () -> {
      double lat = Location.getLatitude(), lon = Location.getLongitude();
      Component.onUI(() -> atualizarMapa(lat, lon));
   },
   err -> Component.onUI(() -> mostrarErro(err.getMessage())));
// ... 结束时：
gps.stopGps();
```

## 深入了解
- 定位指南：[Location & GPS](/docs/data-and-services/device-location-gps)
- 姊妹篇：[escolher-local-no-mapa.md](/docs/cookbook/device/escolher-local-no-mapa)
