移動終端設備已經深入人們日常生活的方方面面,如查看所在城市的天氣、新聞軼事、出行打車、旅行導航、運動記錄。這些習以為常的活動,都離不開定位用戶終端設備的位置。
Location Kit
使用多種定位技術提供服務,可以準確地確定設備在室外/室內的位置:
- 坐標
系統以 1984 年世界大地坐標系統為參考,使用經度、緯度、海拔高度數據描述地球上的一個位置。 - GNSS 定位
全球導航衛星系統,包含:GPS、GLONASS、北斗、Galileo 等,通過導航衛星、設備芯片提供的定位算法,來確定設備準確位置。定位過程具體使用哪些定位系統,取決于用戶設備的硬件能力。 - 基站定位
根據設備當前駐網基站和相鄰基站的位置,估算設備當前位置。此定位方式的定位結果精度相對較低,并且需要設備可以訪問蜂窩網絡。 - WLAN、藍牙定位
根據設備可搜索到的周圍 WLAN、藍牙設備位置,估算設備當前位置。此定位方式的定位結果精度依賴設備周圍可見的固定 WLAN、藍牙設備的分布,密度較高時,精度也相較于基站定位方式更高,同時也需要設備可以訪問網絡
申請定位權限
應用在使用 Location Kit 系統能力前,需要檢查是否已經獲取用戶授權訪問設備位置信息。如未獲得授權,可以向用戶申請需要的位置權限。系統提供的定位相關權限有:
ohos.permission.LOCATION
:用于獲取精準位置,精準度在米級別ohos.permission.APPROXIMATELY_LOCATION
:用于獲取模糊位置,精確度為 5 公里ohos.permission.LOCATION_IN_BACKGROUND
:用于應用切換到后臺仍然需要獲取定位信息的場景
當 APP 運行在前臺,訪問設備位置信息時,申請位置權限的方式有兩種:
獲取模糊位置:ohos.permission.APPROXIMATELY_LOCATION
獲取精確位置:ohos.permission.APPROXIMATELY_LOCATION + ohos.permission.LOCATION
當 APP 運行在后臺時,除了上述兩組權限外,還需要申請如下權限:
后臺定位權限:ohos.permission.LOCATION_IN_BACKGROUND
或者申請定位類型的長時任務:backgroundModes: "location"
申請用戶權限
import {abilityAccessCtrl,bundleManager,common,Permissions,
} from "@kit.AbilityKit";let permissions: Permissions[] = ["ohos.permission.APPROXIMATELY_LOCATION","ohos.permission.LOCATION",
];let accessMgr = abilityAccessCtrl.createAtManager();
const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION;
const bundleInfo = await bundleManager.getBundleInfoForSelf(flags);
const grantStatus0 = await accessMgr.checkAccessToken(bundleInfo.appInfo.accessTokenId,permissions[0]
);
const grantStatus1 = await accessMgr.checkAccessToken(bundleInfo.appInfo.accessTokenId,permissions[1]
);if (grantStatus0 === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED &&grantStatus1 === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED
) {let results = await accessMgr.requestPermissionsFromUser(getContext(),permissions);if (results.authResults[0] == 0 && results.authResults[1] == 0) {// 通過授權} else {// 拒絕授權}
}
相關接口
接口名 | 功能描述 |
---|---|
on(type: 'locationChange', request: LocationRequest | ContinuousLocationRequest, callback: Callback<Location>): void | 開啟位置變化訂閱,并發起定位請求,持續性定位 |
off(type: 'locationChange', callback?: Callback<Location>): void | 關閉位置變化訂閱,并刪除對應的定位請求,如果不關閉就會產生內存泄漏 |
getCurrentLocation(request: CurrentLocationRequest | SingleLocationRequest, callback: AsyncCallback<Location>): void | 獲取當前位置,使用 callback 回調異步返回結果,一次性定位 |
getCurrentLocation(request?: CurrentLocationRequest | SingleLocationRequest): Promise<Location> | 獲取當前位置,使用 Promise 方式異步返回結果 |
getLastLocation(): Location | 獲取最近一次定位結果 |
isLocationEnabled(): boolean | 判斷位置服務是否已經開啟 |
示例
{"module":{"extensionAbilities":["requestPermissions": [//當前應用申請數據和功能的訪問權限//系統授予級權限 —— 只需要聲明name即可{"name": "ohos.permission.INTERNET" //互聯網訪問權限},//用戶授予級權限 —— 必須聲明name/reason/usedScene三個屬性{"name": "ohos.permission.APPROXIMATELY_LOCATION", //模糊定位"reason": "$string:Location_Reason","usedScene": {"abilities": ["EntryAbility"],"when": "always"}},{"name": "ohos.permission.LOCATION", //精確定位"reason": "$string:Location_Reason", //當前應用需要向用戶解釋使用該權限的原因"usedScene": { //權限在何種場景下被使用"abilities": ["EntryAbility"], //哪些Ability/窗口中需要使用該權限"when": "always" //何時使用該權限 inuse:當前應用在前臺運行時需要使用 always:總是需要該權限,即使應用沒在運行}}]]}
}
import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit'
import { geoLocationManager } from '@kit.LocationKit'
import { JSON } from '@kit.ArkTS'
import { router } from '@kit.ArkUI'@Entry
@Component
struct Index {//頁面顯式時,先彈出“申請定位權限”授權窗口async onPageShow() {//① 聲明需要用戶授權的權限列表let list: Permissions[] = ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']//② 獲得當前應用的“訪問令牌”let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION //需要獲得整個應用的信息,而不是模塊的/應用組件的let bundleInfo = await bundleManager.getBundleInfoForSelf(flags) //得到當前資源包信息let tokenId = bundleInfo.appInfo.accessTokenId //當前應用的當前分身在當前用戶使用場景下,系統分配的令牌編號let atManager = abilityAccessCtrl.createAtManager() //At: Access Token,訪問令牌,即當前應用的授權列表let grantStatus0 = await atManager.checkAccessToken(tokenId, list[0])let grantStatus1 = await atManager.checkAccessToken(tokenId, list[1])//③ 從訪問令牌中查詢,用戶是否授予過定位權限if (grantStatus0 == -1 && grantStatus1 == -1) { //0表示已經通過授權了 -1表尚未尚未授權/之前拒絕授權了//④ 如果尚未授權過,則彈出申請授權對話框let result = await atManager.requestPermissionsFromUser(getContext(), list)if (result.authResults[0] == 0) {console.log('1.模糊定位權限已經從用戶處申請到')} else {console.log('2.用戶拒絕授予模糊定位權限')}if (result.authResults[1] == 0) {console.log('3.精確定位權限已經從用戶處申請到')} else {console.log('4.用戶拒絕授予精確定位權限')}}}//當頁面隱藏時,取消“持續性定位改變監聽”onPageHide() {try {geoLocationManager.off('locationChange')console.log('--持續性定位改變監聽已經關閉')} catch (err) {console.log('--關閉持續性定位改變監聽失敗:', JSON.stringify(err))}}build() {Column({ space: 10 }) {Text('首頁').fontSize(30)Button('1.獲取用戶當前的定位信息——一次性定位').onClick(async _ => {if (geoLocationManager.isLocationEnabled()) {console.log('--當前系統已打開定位開關,正在獲取位置信息....')let loc = await geoLocationManager.getCurrentLocation()console.log('--成功獲取到當前定位信息:', JSON.stringify(loc))if (geoLocationManager.isGeocoderAvailable()) {let address = await geoLocationManager.getAddressesFromLocation(loc)console.log('--當前系統可以進行地理<=>坐標轉化',address[0].placeName)} else {console.log('--當前系統無法進行地理<=>坐標轉化')}} else {console.log('--當前系統未啟用定位服務,請用戶打開定位開關!')}})Button('2.獲取用戶當前的定位信息——持續性定位').onClick(_ => {if (!geoLocationManager.isLocationEnabled()) {console.log('--當前系統沒有打開定位開關!')return}let count = 0geoLocationManager.on('locationChange', {}, (loc) => {count++console.log('--當前設備位置改變了:', count, JSON.stringify(loc))})})Button('3.跳轉到下一個頁面,測試是否仍然監聽定位改變').onClick(_ => {router.pushUrl({url: 'pages/Page1'})})}.height('100%').width('100%').padding(10)}
}
地理編碼
使用坐標描述一個位置,非常準確,但是并不直觀,面向用戶表達并不友好。系統向開發者提供了以下兩種轉化能力:
- 地理編碼轉化:將地理描述轉化為具體坐標。
- 逆地理編碼轉化:將坐標轉化為地理描述。
其中地理編碼包含多個屬性來描述位置,包括國家、行政區劃、街道、門牌號、地址描述等等,這樣的信息更便于用戶理解
import { geoLocationManager } from '@kit.LocationKit'try {if( geoLocationManager.isGeocoderAvailable() ){ //查詢地理編碼與逆地理編碼服務是否可用let loc = {"latitude": 31.12, "longitude": 121.11, ... }let addr = await geoLocationManager.getAddressesFromLocation( loc ) //逆地理編碼轉化}
} catch (err) {console.error("逆地理編碼轉化失敗:" + JSON.stringify(err));
}