使用 @nativescript/geolocation 为应用添加 GPS 功能
构建具备 GPS 定位感知能力的应用
在当今的应用开发领域,整合基于位置的服务变得越来越重要。无论是用于地图、导航还是提供特定于位置的内容,GPS 功能都能丰富用户体验,并为移动应用程序增添巨大价值。
尽管初次集成 GPS 可能看起来令人生畏,尤其是如果你正从网页开发转向移动平台,但 NativeScript 通过官方的 @nativescript/geolocation 插件提供了无缝的解决方案。在本文中,我们将探讨如何使用这个强大的工具,轻松地为你的 NativeScript-Vue 应用添加 GPS 功能。
可在 Github 上获得示例运行中的 Vue 3 项目。
1. 技术原理
对于 Android,该包利用了 Google Play Services -- Location API。这是目前以节能方式与 GPS 交互的标准方法。但这同时也意味着没有 Google Play Services 的手机(例如华为等中国手机制造商的设备)将无法使用此包。
另一方面,在 iOS 上,使用的是 Core Location 框架。
2. 设置包
可以通过以下方式轻松安装该包:
bash
npm i @nativescript/geolocation
安装包后,接下来你需要根据你打算如何与 GPS 功能交互,为各个平台设置适当的权限清单。
对于 Android,在你的 AndroidManifest.xml 中添加以下几行:
xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
如果你并不真正需要用户的精确位置,例如新闻应用、天气应用等,你只需要知道用户所在的国家或最近的城镇来推荐相关内容,那么可以移除 ACCESS_FINE_LOCATION。
如果你需要在后台持续使用 GPS 定位,比如用户运动追踪应用。你需要为 Android 添加以下额外权限:
xml
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
对于 iOS 后台定位访问,添加到 plist 文件:
xml
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
(注意:如果只在前台访问位置,则不需要在 iOS plist 中添加任何内容)
专业提示
经验法则,使用更少的权限更好,这有助于你在应用审核过程中避免头疼。如果你在 Android 应用中包含 ACCESS_BACKGROUND_LOCATION,你需要向 Google 审核团队和 AppStore 审核团队解释其用途。
傻傻的我,曾经有一次不小心把后台权限包含到了我的应用中,但实际上并没有使用它,因为我们只在前台获取位置。别学我。
包的描述本身已经很好地解释了它的功能和用法,所以阅读一下也是个好主意:www.npmjs.com/package/@na...。
3. 如何使用 @nativescript/geolocation
3.1. 确保你有权限
记得始终将你的 GPS 操作包装在:
typescript
import * as geolocation from '@nativescript/geolocation';
geolocation.enableLocationRequest().then(() => {
// ... 此处安全访问 Location API
})
这确保你始终有足够的权限与设备位置进行交互。否则,在没有权限的情况下访问设备位置会导致致命错误。
专业提示
永远不要在你的 onMounted 钩子(或 Vue 2 的 mounted)中访问 Location API。问题是这些钩子调用得太早,一些组件可能还没有完全启动!相反,尝试在你的 <Page> 上使用 @loaded 事件。
3.2. 读取位置 -- 基础方法
一次性获取设备的精确位置:
typescript
import { CoreTypes } from '@nativescript/core';
// 注意:将下方代码包装在 `enableLocationRequest` 内部
geolocation.getCurrentLocation({ desiredAccuracy: CoreTypes.Accuracy.high, maximumAge: 5000, timeout: 20000 })
.then((result) => {
// 在 `result` 变量上访问纬度和经度
// 使用:result.latitude, result.longitude, result.
})
对于 desiredAccuracy,你有两个选项:
CoreTypes.Accuracy.any: 使用较少电量,预计精度在 100 米以上CoreTypes.Accuracy.high: 更耗电量,设备能给出的最精细的纬度和经度
3.3. 读取位置 -- 更好的方法
getCurrentLocation 的一个缺点是,如果 GPS 信号弱,有时可能需要长达 10 秒甚至超时。我总是使用 watchLocation 来避免超时问题,而且它与 Vue 组件生命周期一起使用感觉也更直观。强烈推荐使用 watchLocation(),即使你只需要一次位置信息。
这是一个例子:
typescript
import { CoreTypes } from '@nativescript/core';
const location = ref<geolocation.Location | null>(null);
const watchId = ref<number | null>(null);
// 注意:将下方代码包装在 `enableLocationRequest` 内部
geolocation.watchLocation(
(result) => {
// 在 `result` 变量上访问纬度和经度
// 使用:result.latitude, result.longitude, result.
location.value = result;
},
(error) => {
// 发生了不好的事情
},
{
desiredAccuracy: CoreTypes.Accuracy.high,
maximumAge: 5000,
timeout: 20000
}
)
.then((id) => {
watchId.value = id;
});
然后,别忘了使用 clearWatch 清除监听器,如果你不确定到底在哪里应该停止监听,最好在 onUnmounted 钩子内部使用 clearWatch:
typescript
onUnmounted(() => {
if (watchId.value) {
geolocation.clearWatch(watchId.value);
}
});
这可以确保电源效率,并防止僵尸线程在后台运行。
专业提示
以我的做法,我会在 @loaded 事件中开始监听位置变化,在调用 watchLocation 之前,通过检查 watchId 确保没有活跃的监听器,并在 onUnmounted 钩子中使用 clearWatch,以确保每当用户离开页面时,一切都应被清理干净。
同时,记住合理地访问设备位置,以防止对用户宝贵的电池消耗造成影响。
结论
GPS 定位功能是移动世界中最有趣的方面之一。将此功能整合到你的项目中,将为用户提供更沉浸式和个性化的体验。然而,它也非常耗电,因此请明智地使用它,以防止不必要的电力消耗。
在后台服务中访问 GPS 定位,对于司机追踪、配送应用等场景也是一个有趣的用例。我正在撰写另一篇关于 NativeScript 后台服务的文章,很快就会发布,敬请期待!
如何解决 geolocation 错误:Cannot read property 'PRIORITY_HIGH_ACCURACY'
如果你使用的是 NativeScript 8.1+,并且使用的是 @nativescript/geolocation 插件 8.1.0 版本,并且在某个地方意外设置了 googlePlayServicesVersion。来自此插件的最近更新可能会导致你的 Android 构建失败。
来自 Discord 的解答
相关 GitHub 问题:github.com/NativeScrip...
症状
javascript
Error: Cannot enable the location service. TypeError: Cannot read property 'PRIORITY_HIGH_ACCURACY' of undefined
如何修复
尝试从所有 .gradle 脚本中移除 googlePlayServicesVersion 设置,或者将其设置为 21.+。你可能意外地在 app.gradle 或 before-plugins.gradle 中设置了它。 
修复上述行后,尝试执行:
bash
ns clean
ns run android
以清理并重新构建你的项目。