news 2026/4/16 16:03:44

用代码当“尺子”:HarmonyOS 定位 API 测距入门小记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用代码当“尺子”:HarmonyOS 定位 API 测距入门小记

前言

用手机测量距离这件事,很多人第一个想到的是打开地图 App,长按起点再长按终点,等系统算出一条蓝线。这当然可以,但如果只想简单测一下“我离某个地方大概多远”,地图的操作链条显得有点长。

那能不能自己动手做一个小工具?不依赖地图 SDK,不用加载复杂的瓦片图层,就用系统自带的定位能力,拿到当前位置的经纬度,再和预设的几个“虚拟地标”算一算球面距离,完事。

正好 HarmonyOS 的 Location Kit 提供了完整的定位服务,从权限申请到坐标获取再到状态监听,接口设计得相当清晰。我们用它来实现一个“测量助手”:获取当前位置,计算到几个虚拟地标的直线距离,顺便看看在这个过程中能学到哪些实用的东西。

一、项目概览:要做什么、怎么测

这个小工具的交互逻辑非常简单,一句话就能讲清楚:打开应用,点一下按钮,拿到当前位置,算出离“地标”有多远,展示出来。不需要地图可视化,不需要复杂的 UI,核心功能就三个环节:

  • 检查并申请位置权限,确保能拿到定位数据
  • 调用定位接口获取当前经纬度
  • 用 Haversine 公式计算到预设地标的球面距离

在界面设计上,因为用模拟器测试,我们可以把几个预设地标的坐标也做成可配置的,方便调试。最终的效果是:打开应用,系统弹窗请求定位权限,同意后点击“开始测量”,界面上显示当前位置坐标和到各地标的距离。

二、位置服务与权限机制

HarmonyOS 将位置信息归类为“半开放隐私信息”,这意味着开发者不能像读取设备型号那样直接拿到坐标,必须先经过用户的明确授权。

2.1 三种定位权限

系统提供了三个与定位相关的权限,层级不同,用途也不同:

  • ohos.permission.APPROXIMATELY_LOCATION:模糊定位,精确度约 5 公里,适合只需要城市级位置的场景
  • ohos.permission.LOCATION:精准定位,精确度在米级,需要同时申请模糊定位权限
  • ohos.permission.LOCATION_IN_BACKGROUND:后台定位,应用切到后台时仍需位置信息时才需要

我们的测距工具显然需要精准位置,因此在module.json5里需要声明前两个权限。

2.2 权限申请流程

HarmonyOS 的权限申请分为两步:第一步是在配置文件中声明,第二步是在运行时动态请求用户授权。这两步缺一不可——只声明不请求,用户不会收到弹窗;只请求不声明,系统直接返回失败。

这里有一个容易踩的坑:位置开关。即使权限拿到了,如果系统设置里的“位置信息”开关是关闭的,定位接口也会返回错误码。所以代码里要先调用isLocationEnabled()检查一下,提示用户去开开关。

2.3 动态申请的实现

EntryAbilityonCreateonWindowStageCreate阶段,拿到上下文后就可以发起权限申请了。核心流程:创建AtManager实例,检查权限状态,如果未授权则调用requestPermissionsFromUser拉起系统弹窗。

这部分逻辑放在 Ability 层比较合适,因为权限状态会影响到后续所有定位操作,早申请早安心。

三、获取当前位置

权限搞定之后,获取坐标这件事就变得非常简单。HarmonyOS 从 API 9 开始废弃了老的@system.geolocation,统一推荐使用geoLocationManager模块,导入路径是@kit.LocationKit

3.1 核心接口

geoLocationManager提供了多个定位相关的 API,最常用的是这几个:

  • getCurrentLocation(request, callback):单次定位,返回当前位置
  • getLastLocation():获取系统缓存的最近一次位置,省电但可能不够“新鲜”
  • on('locationChange', ...):持续监听位置变化,适合导航类应用
  • isLocationEnabled():检查系统定位开关状态

我们用的是第一种,单次定位。虽然getLastLocation更省资源,但对于测距这种场景,用户期望看到的是“此刻”的距离,而不是几分钟前缓存的位置。

3.2 配置定位请求参数

调用getCurrentLocation时需要传入一个SingleLocationRequest对象,它决定了系统用什么样的策略来获取位置。主要参数有两个值得关注:

  • locatingPriority:定位优先级。选PRIORITY_ACCURACY表示宁可慢一点也要精度高,选PRIORITY_LOCATING_SPEED表示优先返回最快的定位结果。
  • locatingTimeoutMs:超时时间,单位毫秒。超过这个时间还没拿到位置就返回超时错误。

对于测距工具,精度优先显然更重要,所以选用PRIORITY_ACCURACY,超时设 10 秒差不多。

3.3 坐标系的那些事

这里有一个值得注意的技术点:geoLocationManager返回的坐标是WGS-84 坐标系——也就是 GPS 原生使用的国际标准坐标系。但如果你后续想把位置标记在 HarmonyOS 的地图组件上,会发现点和地图对不上。这是因为华为地图在国内使用的是GCJ-02 坐标系(俗称“火星坐标系”),两者之间需要做一次转换。

我们的测距工具因为不涉及地图可视化,直接使用 WGS-84 坐标计算距离完全没有问题。但如果你未来想做“在地图上显示位置”之类的扩展,记得用map.convertCoordinate做一次转换,否则标记点会偏移几百米。

四、Haversine 公式:从经纬度到公里数

拿到当前位置的经纬度后,下一步是计算到目标地标的距离。地球是一个近似的球体,不能用平面几何的勾股定理直接算,得用球面距离公式。最常用的就是Haversine 公式(也叫半正矢公式)。

4.1 为什么用 Haversine?

Haversine 公式专门用于计算球面上两点之间的大圆距离——也就是穿过球心的平面与球面相交形成的那段圆弧的长度。它的优点很突出:

  • 计算简单:只需要几次三角函数运算,不需要复杂的迭代
  • 精度够用:对于绝大多数应用场景(比如测两地距离、配送范围估算),偏差在可接受范围内
  • 不依赖外部库:纯数学运算,不需要网络,不需要 SDK

当然,如果对精度要求极高(比如测绘级应用),可以用 Vincenty 公式,它考虑了地球椭球体的扁率,偏差可以控制在厘米级。但对于一个测距小工具,Haversine 绰绰有余。

4.2 公式拆解

Haversine 公式的完整形式是:

d = 2R · arcsin(√a) 其中: a = sin²(Δlat/2) + cos(lat1) · cos(lat2) · sin²(Δlon/2)

参数说明:

  • lat1lon1:第一个点的纬度和经度(弧度)
  • lat2lon2:第二个点的纬度和经度(弧度)
  • Δlat = lat2 - lat1,纬度差
  • Δlon = lon2 - lon1,经度差
  • R = 6371,地球平均半径,单位公里
4.3 代码实现

把公式翻译成 TypeScript,核心代码大约十几行:

function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371; // 地球平均半径(公里) // 角度转弧度 const toRad = (deg: number) => deg * Math.PI / 180; const φ1 = toRad(lat1); const φ2 = toRad(lat2); const Δφ = toRad(lat2 - lat1); const Δλ = toRad(lon2 - lon1); // Haversine 核心计算 const a = Math.sin(Δφ / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) ** 2; const c = 2 * Math.asin(Math.sqrt(a)); return R * c; }

这里有几个小细节值得留意。Math.asin的输入范围是[-1, 1],由于浮点数精度问题,a可能略微超出这个范围(比如 1.0000000001),导致返回NaN。加一个Math.min(a, 1.0)的保护更稳健。另外,当两点非常接近时,a会很小,计算结果在米级也能保持较好的精度。

五、代码实现

理论部分说完了,下面直接上完整可运行的代码。你可以在 DevEco Studio 里新建一个 Empty Ability 项目,然后把以下文件内容替换进去。

5.1 项目配置

首先需要在module.json5中声明权限:

{ "module": { "name": "entry", "type": "entry", "description": "$string:module_desc", "mainElement": "EntryAbility", "deviceTypes": ["phone", "tablet"], "deliveryWithInstall": true, "installationFree": false, "pages": "$profile:main_pages", "requestPermissions": [ { "name": "ohos.permission.APPROXIMATELY_LOCATION", "reason": "$string:location_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }, { "name": "ohos.permission.LOCATION", "reason": "$string:location_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } ], "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", "icon": "$media:layered_image", "label": "$string:EntryAbility_label", "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ { "entities": ["entity.system.home"], "actions": ["action.system.home"] } ] } ] } }

resources/base/element/string.json中添加权限说明文案:

{ "string": [ { "name": "module_desc", "value": "HarmonyOS 测量助手模块" }, { "name": "EntryAbility_desc", "value": "测量助手" }, { "name": "EntryAbility_label", "value": "测量助手" }, { "name": "location_permission_reason", "value": "需要使用您的位置信息来计算与预设地标之间的距离" } ] }
5.2 EntryAbility 中的权限申请

/entry/src/main/ets/entryability/EntryAbility.ets的内容如下。这里在 Ability 启动时主动检查并申请权限,这样页面加载时权限状态就已经确定了:

import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(0x0000, 'MeasureApp', 'Ability onCreate'); // 在 Ability 创建时申请权限 this.requestLocationPermissions(); } onDestroy(): void { hilog.info(0x0000, 'MeasureApp', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage): void { hilog.info(0x0000, 'MeasureApp', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(0x0000, 'MeasureApp', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } hilog.info(0x0000, 'MeasureApp', 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { hilog.info(0x0000, 'MeasureApp', 'Ability onWindowStageDestroy'); } onForeground(): void { hilog.info(0x0000, 'MeasureApp', 'Ability onForeground'); } onBackground(): void { hilog.info(0x0000, 'MeasureApp', 'Ability onBackground'); } private async requestLocationPermissions(): Promise<void> { const permissions: Array<Permissions> = [ 'ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION' ]; const atManager = abilityAccessCtrl.createAtManager(); try { const bundleInfo = await bundleManager.getBundleInfoForSelf( bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION ); const tokenId = bundleInfo.appInfo.accessTokenId; for (const permission of permissions) { const grantStatus = await atManager.checkAccessToken(tokenId, permission); if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { hilog.info(0x0000, 'MeasureApp', `请求权限: ${permission}`); } } await atManager.requestPermissionsFromUser(this.context, permissions); hilog.info(0x0000, 'MeasureApp', '权限申请完成'); } catch (err) { const error = err as BusinessError; hilog.error(0x0000, 'MeasureApp', `权限申请失败: ${error.code}, ${error.message}`); } } }
5.3 主页面的完整实现

/entry/src/main/ets/pages/Index.ets是核心页面,包含定位逻辑、距离计算和 UI 展示:

import { geoLocationManager } from '@kit.LocationKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; // 地标数据接口 interface Landmark { name: string; latitude: number; longitude: number; description: string; iconColor: string; } @Entry @Component struct Index { // 预设地标(请根据实际测试需求修改经纬度) @State landmarks: Landmark[] = [ { name: '北京天安门', latitude: 39.9042, longitude: 116.4074, description: '首都心脏', iconColor: '#E74C3C' }, { name: '上海外滩', latitude: 31.2304, longitude: 121.4737, description: '东方明珠', iconColor: '#3498DB' }, { name: '广州塔', latitude: 23.1059, longitude: 113.3245, description: '小蛮腰', iconColor: '#2ECC71' }, { name: '深圳湾', latitude: 22.5136, longitude: 113.9544, description: '科技之窗', iconColor: '#F39C12' } ]; @State currentLat: string = '--'; @State currentLon: string = '--'; @State distances: Map<string, number> = new Map(); @State isLoading: boolean = false; @State hasLocation: boolean = false; @State locationTime: string = ''; // Haversine 距离计算(单位:公里) private calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371; // 地球平均半径(公里) const toRad = (deg: number): number => deg * Math.PI / 180; const φ1 = toRad(lat1); const φ2 = toRad(lat2); const Δφ = toRad(lat2 - lat1); const Δλ = toRad(lon2 - lon1); const a = Math.sin(Δφ / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) ** 2; // 防止浮点误差导致 a 略大于 1 const c = 2 * Math.asin(Math.sqrt(Math.min(a, 1.0))); return R * c; } // 格式化距离显示 private formatDistance(distanceKm: number): string { if (distanceKm < 1) { return `${(distanceKm * 1000).toFixed(0)} 米`; } return `${distanceKm.toFixed(2)} 公里`; } // 获取当前位置 private getCurrentLocation(): void { this.isLoading = true; // 1. 检查定位开关 try { const isEnabled = geoLocationManager.isLocationEnabled(); if (!isEnabled) { promptAction.showToast({ message: '请先在设置中开启位置服务', duration: 2000 }); this.isLoading = false; return; } } catch (err) { console.error('检查定位开关失败: ' + JSON.stringify(err)); } // 2. 配置定位请求 const request: geoLocationManager.SingleLocationRequest = { locatingPriority: geoLocationManager.LocatingPriority.PRIORITY_ACCURACY, locatingTimeoutMs: 10000 }; // 3. 发起定位请求 geoLocationManager.getCurrentLocation(request) .then((location) => { this.currentLat = location.latitude.toFixed(6); this.currentLon = location.longitude.toFixed(6); this.hasLocation = true; // 记录定位时间 const now = new Date(); this.locationTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`; // 计算到各地标的距离 const newDistances = new Map<string, number>(); this.landmarks.forEach(landmark => { const dist = this.calculateDistance( location.latitude, location.longitude, landmark.latitude, landmark.longitude ); newDistances.set(landmark.name, dist); }); this.distances = newDistances; this.isLoading = false; console.info(`定位成功: ${location.latitude}, ${location.longitude}`); }) .catch((error: BusinessError) => { console.error(`定位失败: ${error.code}, ${error.message}`); this.isLoading = false; let errorMsg = '定位失败'; if (error.code === 601) { errorMsg = '定位超时,请检查网络或移至开阔区域'; } else if (error.code === 602) { errorMsg = '定位服务不可用'; } else if (error.code === 3301000) { errorMsg = '位置服务未开启'; } promptAction.showToast({ message: errorMsg, duration: 2000 }); }); } // 地标卡片构建器 @Builder LandmarkCard(landmark: Landmark, index: number) { Column() { Row() { // 左侧图标 Text((index + 1).toString()) .fontSize(18) .fontColor(Color.White) .width(36) .height(36) .backgroundColor(landmark.iconColor) .borderRadius(18) .textAlign(TextAlign.Center) // 地标信息 Column() { Row() { Text(landmark.name) .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#1A1A1A') Text(`· ${landmark.description}`) .fontSize(12) .fontColor('#999999') .margin({ left: 6 }) } Text(`经纬度: ${landmark.latitude.toFixed(4)}°, ${landmark.longitude.toFixed(4)}°`) .fontSize(11) .fontColor('#AAAAAA') .margin({ top: 2 }) } .alignItems(HorizontalAlign.Start) .margin({ left: 12 }) .layoutWeight(1) // 距离显示 Column() { Text(this.distances.has(landmark.name) ? this.formatDistance(this.distances.get(landmark.name)!) : '等待测量') .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor(this.distances.has(landmark.name) ? '#007DFF' : '#CCCCCC') Text('直线距离') .fontSize(10) .fontColor('#AAAAAA') } .alignItems(HorizontalAlign.End) } .width('100%') .padding({ left: 16, right: 16, top: 14, bottom: 14 }) } .width('100%') .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 6, color: '#10000000', offsetY: 3 }) } build() { Column() { // 标题区 Column() { Text('测量助手') .fontSize(32) .fontWeight(FontWeight.Bold) .fontColor('#1A1A1A') Text('利用位置 API,测测你离「虚拟地标」有多远') .fontSize(14) .fontColor('#999999') .margin({ top: 4 }) } .width('100%') .alignItems(HorizontalAlign.Start) .padding({ left: 20, top: 20, bottom: 8 }) // 当前位置卡片 Column() { Row() { Text('📍 当前位置') .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor('#666666') if (this.hasLocation) { Text(` · ${this.locationTime}`) .fontSize(12) .fontColor('#AAAAAA') .margin({ left: 6 }) } } .width('100%') Row() { Column() { Text('纬度') .fontSize(11) .fontColor('#AAAAAA') Text(this.currentLat) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(this.hasLocation ? '#2ECC71' : '#CCCCCC') .margin({ top: 4 }) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) Column() { Text('经度') .fontSize(11) .fontColor('#AAAAAA') Text(this.currentLon) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(this.hasLocation ? '#3498DB' : '#CCCCCC') .margin({ top: 4 }) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) } .width('100%') .margin({ top: 12 }) Button(this.isLoading ? '测量中...' : (this.hasLocation ? '重新测量' : '开始测量')) .fontSize(16) .fontWeight(FontWeight.Medium) .backgroundColor(this.isLoading ? '#CCCCCC' : '#007DFF') .borderRadius(24) .height(48) .width('100%') .margin({ top: 16 }) .enabled(!this.isLoading) .onClick(() => { this.getCurrentLocation(); }) } .width('100%') .padding(18) .backgroundColor(Color.White) .borderRadius(16) .shadow({ radius: 8, color: '#15000000', offsetY: 4 }) .margin({ left: 16, right: 16, bottom: 16 }) // 地标列表标题 Row() { Text('📍 虚拟地标') .fontSize(16) .fontWeight(FontWeight.Medium) .fontColor('#1A1A1A') Text('(预设坐标,可自行修改)') .fontSize(11) .fontColor('#AAAAAA') .margin({ left: 6 }) } .width('100%') .padding({ left: 20, top: 8, bottom: 8 }) // 地标列表 List({ space: 12 }) { ForEach(this.landmarks, (landmark: Landmark, index: number) => { ListItem() { this.LandmarkCard(landmark, index) } }, (landmark: Landmark) => landmark.name) } .width('100%') .padding({ left: 16, right: 16 }) .layoutWeight(1) // 底部说明 Text('Haversine 球面距离 · WGS-84 坐标系') .fontSize(11) .fontColor('#CCCCCC') .padding({ top: 12, bottom: 16 }) } .width('100%') .height('100%') .backgroundColor('#F8F9FA') } }
5.4 其他配置文件

/entry/src/main/resources/base/profile/main_pages.json保持不变即可(默认内容)。如果想调整应用名称,可以修改string.json中的EntryAbility_label

完整项目结构如下:

entry/src/main/ ├── ets/ │ ├── entryability/ │ │ └── EntryAbility.ets # 权限申请 │ └── pages/ │ └── Index.ets # 主页面(定位 + 测距) ├── resources/base/ │ ├── element/ │ │ └── string.json # 文案配置 │ └── profile/ │ └── main_pages.json # 页面路由 └── module.json5 # 模块配置(含权限声明)

代码部分就到这里。接下来我们看看在模拟器上怎么跑起来。

六、运行展示

代码写完后,点击 DevEco Studio 右上角的绿色三角按钮,编译并安装到模拟器。

应用启动后,系统会弹出一个权限申请对话框,请求获取位置信息。点击“允许”后进入主界面。此时屏幕上方显示“当前位置”,纬度和经度都还是“— —”,下方是四个预设地标的卡片,距离一栏显示“等待测量”。

在开始测量之前,需要先给模拟器设定一个虚拟位置,否则定位接口会因为拿不到 GPS 信号而超时。点击模拟器右侧工具栏的设置按钮(三个点),在下拉菜单中选择“GPS”,在弹出的窗口中切换到“手动设置”页签,输入经纬度(比如北京:纬度 39.9042,经度 116.4074),点击确定。

回到应用,点击“开始测量”按钮。大约一两秒后,界面上的纬度和经度更新为刚才设置的值,下方四个地标卡片右侧的距离数据也同步刷新。因为模拟器定位在北京,所以“北京天安门”的距离显示为 0 公里或几十米,“上海外滩”大约 1067 公里,“广州塔”大约 1889 公里,“深圳湾”大约 1941 公里。距离小于 1 公里时单位自动切换为米,超过 1 公里显示为公里并保留两位小数。

如果想测其他位置,可以随时修改模拟器的 GPS 坐标,然后点击“重新测量”按钮,距离数据会实时更新。整个过程不需要重启应用,定位接口每次都会返回最新的模拟位置。

七、回顾与收获

这个小工具写下来,核心代码其实不到 200 行,但背后涉及的几个知识点挺有嚼头。

第一,位置权限的设计哲学。HarmonyOS 把位置信息归为隐私数据,通过“配置文件声明 + 运行时动态请求”两道关卡来保护用户。这种设计思路在整个系统里是一致的——相机、麦克风、通讯录等敏感权限都走这个流程。理解了这个模式,以后接入其他敏感能力就能举一反三。

第二,Location Kit 的能力边界。geoLocationManager不只是能拿一次位置,它还支持持续监听、获取历史缓存、逆地理编码、地理围栏等功能。这次只用了冰山一角,但整个模块的接口风格是一致的,上手一个就能快速掌握其他的。

第三,球面距离的计算原理。Haversine 公式在数学上不算复杂,但它解决了一个实际工程问题——如何在球面上用经纬度算距离。理解了这个公式之后,类似的需求(比如判断用户是否在某个圆形区域内、计算两个 GPS 点之间的方位角)都能迎刃而解。

第四,模拟器的虚拟定位功能。对于位置类应用的开发和测试,模拟器的 GPS 模拟功能非常实用。不需要抱着手机出门跑,坐在电脑前就能切换北京、上海、纽约的坐标,验证各种地理位置下的逻辑表现。手动设置、轨迹导入、场景模拟三种模式覆盖了从单点测试到连续运动测试的各种需求。

总结

从权限到定位,从坐标到距离,整个过程比想象中顺畅。HarmonyOS 的 Location Kit 把位置服务的复杂度封装得相当干净,开发者只需要关注业务逻辑,不用操心底层定位技术的差异——无论是 GNSS 还是网络定位,接口层都是统一的。

当然,这个小工具还有很多可以扩展的方向。比如把地标列表做成可编辑的,让用户自己添加常去的地点;加上地理围栏功能,当用户接近某个地标时弹个通知;或者结合地图组件,把当前位置和地标直观地显示在地图上。

如果你正好在学 HarmonyOS 开发,不妨把这个小工具当作一个练习起点。从权限申请到定位获取,再到数据计算和界面展示,完整的链路走一遍,比看十篇教程都管用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 16:03:32

GetQzonehistory:如何安全备份你的QQ空间数字记忆

GetQzonehistory&#xff1a;如何安全备份你的QQ空间数字记忆 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 在社交媒体数据管理中&#xff0c;我们常常面临一个现实问题&#xff1a;平…

作者头像 李华
网站建设 2026/4/16 16:02:39

3大核心功能解析:CREST如何帮你彻底解决分子构象搜索难题

3大核心功能解析&#xff1a;CREST如何帮你彻底解决分子构象搜索难题 【免费下载链接】crest CREST - A program for the automated exploration of low-energy molecular chemical space. 项目地址: https://gitcode.com/gh_mirrors/crest/crest 你是否曾为寻找分子的稳…

作者头像 李华
网站建设 2026/4/16 16:02:31

AI专著生成魔法揭秘:高效工具推荐,极大提升专著撰写效率

学术专著写作难题与AI工具解决方案 对大多数研究人员来说&#xff0c;撰写学术专著的最大挑战无疑是“有限的时间和精力”与“无限的写作需求”之间的矛盾。专著的创作过程通常需要耗费3到5年&#xff0c;甚至更久的时间&#xff0c;而研究者们还要面对教学、科研项目、学术交…

作者头像 李华
网站建设 2026/4/16 16:00:12

2025_NIPS_Solver-Informed RL: Grounding Large Language Models for Authentic Optimization Modeling

文章主要内容总结 该研究聚焦于解决大语言模型(LLMs)在生成优化模型时存在的错误和幻觉问题,提出了Solver-Informed Reinforcement Learning(SIRL)框架——一种基于可验证奖励的强化学习方法,旨在提升LLMs生成准确、可执行优化模型的能力。 核心流程包括: 数据合成:通…

作者头像 李华
网站建设 2026/4/16 15:58:10

软件测试|app测试相关面试题(2)

一、App 稳定性怎么做的?Monkey 怎么用? 稳定性这块,我们当时用的是SDK 自动的一个Monkey工具进行测试的,其实Monkey工具主要通过模拟用户发送伪随机时间去操作软件,通过执行Monkey命令,它会自动出报告,执行测试大概在10 万次,每个动作的间隔时间250ms,主要就是看软件…

作者头像 李华