鸿蒙技术干货9:deviceInfo 设备信息获取与位置提醒 APP 整合

前两篇咱们分别搞定了通知服务(发送提醒)和地理位置服务(获取位置),这篇咱们先学习设备信息(deviceInfo)的核心用法,适配不同设备的硬件和系统特性,再将三大服务整合,完成 "位置提醒 APP" 的完整开发。让咱们从设备适配到功能闭环,一步步实现可直接复用的实战项目!

一、设备信息服务核心认知

1. 设备信息的应用价值

  • 设备适配:根据屏幕尺寸调整 UI 布局、根据系统版本适配 API;
  • 功能优化:根据设备型号调整定位策略(如高端机支持高精度 GNSS);
  • 问题排查:记录设备信息用于日志分析,快速定位兼容性问题;
  • 个性化服务:根据设备特性提供定制化功能(如平板端显示更多操作入口)。

2. 核心 API 与可获取信息

  • 核心模块:@ohos.deviceInfo(设备信息)、@ohos.screen(屏幕信息)

  • 关键信息:

    • 设备基础信息:型号、厂商、设备 ID;
    • 系统信息:系统版本、API 版本、设备类型;
    • 屏幕信息:屏幕尺寸、分辨率、像素密度。

二、设备信息获取实战

1. 获取设备基础信息

typescript 复制代码
import deviceInfo from '@ohos.deviceInfo';

/**
 * 获取设备基础信息
 */
export function getDeviceBaseInfo(): {
  deviceModel: string; // 设备型号
  manufacturer: string; // 设备厂商
  osVersion: string; // 系统版本
  apiVersion: number; // API版本
  deviceType: string; // 设备类型(phone/tablet/watch等)
} {
  return {
    deviceModel: deviceInfo.deviceModel, // 如:Mate 60 Pro
    manufacturer: deviceInfo.manufacturer, // 如:Huawei
    osVersion: deviceInfo.osVersion, // 如:4.0.0.188
    apiVersion: deviceInfo.apiVersion, // 如:10
    deviceType: deviceInfo.deviceType // 如:phone
  };
}

2. 获取屏幕信息(适配 UI 布局)

ini 复制代码
import screen from '@ohos.screen';

/**
 * 获取屏幕信息
 */
export function getScreenInfo(): {
  screenWidth: number; // 屏幕宽度(像素)
  screenHeight: number; // 屏幕高度(像素)
  density: number; // 像素密度
  dpi: number; // 屏幕DPI
} {
  const mainScreen = screen.getDefaultScreen();
  const screenRect = mainScreen.getRect();
  const density = mainScreen.getDensity();
  const dpi = mainScreen.getDpi();
  
  return {
    screenWidth: screenRect.width,
    screenHeight: screenRect.height,
    density: density,
    dpi: dpi
  };
}

3. 设备适配实战(根据设备类型调整功能)

ini 复制代码
/**
 * 根据设备类型调整定位配置
 * 手机:高精度模式,平板:平衡模式,手表:低功耗模式
 */
export function getLocationConfigByDeviceType(): geoLocationManager.LocationRequest {
  const deviceInfo = getDeviceBaseInfo();
  let priority: geoLocationManager.LocationRequestPriority;
  
  switch (deviceInfo.deviceType) {
    case 'phone':
      priority = geoLocationManager.LocationRequestPriority.HIGH_ACCURACY;
      break;
    case 'tablet':
      priority = geoLocationManager.LocationRequestPriority.BALANCED;
      break;
    case 'watch':
      priority = geoLocationManager.LocationRequestPriority.LOW_POWER;
      break;
    default:
      priority = geoLocationManager.LocationRequestPriority.BALANCED;
  }
  
  return {
    priority: priority,
    interval: deviceInfo.deviceType === 'watch' ? 10000 : 5000, // 手表定位间隔 longer
    distance: 10,
    scenario: geoLocationManager.LocationScenario.NAVIGATION
  };
}

三、整合三大服务:位置提醒 APP 完整实现

1. 项目核心流程

  1. 应用启动:获取设备信息,适配 UI 布局和定位策略;
  2. 权限申请:批量申请通知权限和定位权限;
  3. 设置目标:用户输入或选择目标位置(经纬度);
  4. 定位监听:启动持续定位,实时计算与目标位置的距离;
  5. 提醒触发:到达目标区域,发送通知并停止定位;
  6. 点击跳转:用户点击通知,进入应用查看详情。

2. 完整代码实现

(1)全局工具类整合(utils/SystemServiceUtil.ets)

javascript 复制代码
// 整合前两篇的通知、定位工具方法,加上设备信息方法
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import notification from '@ohos.notification';
import wantAgent from '@ohos.wantAgent';
import geoLocationManager from '@ohos.geolocation';
import deviceInfo from '@ohos.deviceInfo';
import screen from '@ohos.screen';
import common from '@ohos.app.ability.common';

// 权限申请:批量申请通知和定位权限
export async function requestAllPermissions(context: common.UIAbilityContext): Promise<boolean> {
  const permissions = [
    'ohos.permission.NOTIFICATION_CONTROLLER',
    'ohos.permission.APPROXIMATELY_LOCATION',
    'ohos.permission.LOCATION'
  ];
  
  const atManager = abilityAccessCtrl.createAtManager();
  try {
    const authResults = await atManager.checkAccessToken(
      abilityAccessCtrl.createTokenID(),
      permissions
    );
    
    const needReqPerms = permissions.filter(
      (perm, index) => authResults[index] !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
    );
    
    if (needReqPerms.length === 0) return true;
    
    const reqResult = await atManager.requestPermissionsFromUser(context, needReqPerms);
    return reqResult.authResults.every(status => status === 0);
  } catch (err) {
    console.error(`批量权限申请失败:${JSON.stringify(err)}`);
    return false;
  }
}

// 设备信息方法(同上篇)
export function getDeviceBaseInfo() { /* 省略,复用前文代码 */ }
export function getScreenInfo() { /* 省略,复用前文代码 */ }
export function getLocationConfigByDeviceType() { /* 省略,复用前文代码 */ }

// 通知方法(同上篇)
export async function createLocationReminderNotification() { /* 省略,复用前文代码 */ }
export async function sendNotification() { /* 省略,复用前文代码 */ }

// 定位方法(同上篇)
export function startContinuousLocation() { /* 省略,复用前文代码 */ }
export function stopContinuousLocation() { /* 省略,复用前文代码 */ }
export function isReachTargetArea() { /* 省略,复用前文代码 */ }

(2)主页面实现(pages/LocationReminderPage.ets)

typescript 复制代码
import common from '@ohos.app.ability.common';
import {
  requestAllPermissions,
  getDeviceBaseInfo,
  getScreenInfo,
  getLocationConfigByDeviceType,
  createLocationReminderNotification,
  sendNotification,
  startContinuousLocation,
  stopContinuousLocation,
  isReachTargetArea
} from '../utils/SystemServiceUtil';

@Entry
@Component
struct LocationReminderPage {
  private context = getContext(this) as common.UIAbilityContext;
  @State deviceInfoStr: string = '';
  @State currentLocation: string = '未获取位置';
  @State targetLat: string = '39.9042'; // 目标纬度默认值
  @State targetLng: string = '116.4074'; // 目标经度默认值
  @State reminderRadius: number = 100; // 提醒半径(米)
  @State isTracking: boolean = false;

  // 页面加载时获取设备信息
  aboutToAppear() {
    this.loadDeviceInfo();
  }

  // 加载设备信息
  loadDeviceInfo() {
    const deviceInfo = getDeviceBaseInfo();
    const screenInfo = getScreenInfo();
    this.deviceInfoStr = `设备:${deviceInfo.manufacturer} ${deviceInfo.deviceModel} | 系统:${deviceInfo.osVersion} | 屏幕:${screenInfo.screenWidth}x${screenInfo.screenHeight}`;
  }

  // 启动位置提醒
  async startReminder() {
    // 1. 检查权限
    const allGranted = await requestAllPermissions(this.context);
    if (!allGranted) {
      Toast.show({ message: '部分权限未授予,无法启动提醒' });
      return;
    }

    // 2. 验证目标位置
    const targetLoc = {
      latitude: parseFloat(this.targetLat),
      longitude: parseFloat(this.targetLng)
    };
    if (isNaN(targetLoc.latitude) || isNaN(targetLoc.longitude)) {
      Toast.show({ message: '目标位置格式错误' });
      return;
    }

    // 3. 启动持续定位(根据设备类型适配配置)
    this.isTracking = true;
    Toast.show({ message: '位置提醒已启动,正在跟踪您的位置' });
    
    startContinuousLocation((currentLoc) => {
      this.currentLocation = `当前:纬度${currentLoc.latitude.toFixed(6)},经度${currentLoc.longitude.toFixed(6)}`;
      
      // 4. 判断是否到达目标区域
      if (isReachTargetArea(currentLoc, targetLoc, this.reminderRadius)) {
        this.sendReminderNotification();
        this.stopReminder();
      }
    }, getLocationConfigByDeviceType());
  }

  // 发送提醒通知
  async sendReminderNotification() {
    const notification = await createLocationReminderNotification(
      this.context,
      '位置提醒',
      `您已到达目标区域(半径${this.reminderRadius}米),点击查看详情`
    );
    await sendNotification(notification);
  }

  // 停止位置提醒
  stopReminder() {
    stopContinuousLocation();
    this.isTracking = false;
    Toast.show({ message: '位置提醒已停止' });
  }

  build() {
    Column({ space: 25 })
      .width('100%')
      .height('100%')
      .padding(30)
      .backgroundColor('#f5f5f5') {
      
      // 标题区域
      Text('位置提醒APP')
        .fontSize(36)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)
        .width('100%')
      
      // 设备信息区域
      Text(this.deviceInfoStr)
        .fontSize(16)
        .color('#666')
        .textAlign(TextAlign.Center)
        .width('100%')
        .lineHeight(24)
      
      // 目标位置输入区域
      Column({ space: 15 })
        .width('100%')
        .padding(20)
        .backgroundColor('#ffffff')
        .borderRadius(12) {
        Text('目标位置设置')
          .fontSize(20)
          .fontWeight(FontWeight.Medium)
        
        Row({ space: 10 })
          .width('100%') {
          Text('纬度:')
            .fontSize(18)
            .width('20%')
          TextInput({ text: this.targetLat })
            .width('80%')
            .height(45)
            .padding(10)
            .border({ width: 1, color: '#eee' })
            .borderRadius(8)
            .fontSize(18)
            .onChange((value) => this.targetLat = value)
        }
        
        Row({ space: 10 })
          .width('100%') {
          Text('经度:')
            .fontSize(18)
            .width('20%')
          TextInput({ text: this.targetLng })
            .width('80%')
            .height(45)
            .padding(10)
            .border({ width: 1, color: '#eee' })
            .borderRadius(8)
            .fontSize(18)
            .onChange((value) => this.targetLng = value)
        }
        
        Row({ space: 10 })
          .width('100%') {
          Text('提醒半径:')
            .fontSize(18)
            .width('30%')
          TextInput({ text: this.reminderRadius.toString() })
            .width('70%')
            .height(45)
            .padding(10)
            .border({ width: 1, color: '#eee' })
            .borderRadius(8)
            .fontSize(18)
            .onChange((value) => this.reminderRadius = parseInt(value) || 100)
          Text('米')
            .fontSize(18)
            .marginLeft(10)
        }
      }
      
      // 当前位置显示
      Text(this.currentLocation)
        .fontSize(18)
        .width('100%')
        .textAlign(TextAlign.Center)
        .padding(15)
        .backgroundColor('#ffffff')
        .borderRadius(12)
      
      // 操作按钮区域
      Row({ space: 30 })
        .width('100%')
        .justifyContent(FlexAlign.Center) {
        Button(this.isTracking ? '停止提醒' : '启动提醒')
          .type(ButtonType.Capsule)
          .width(200)
          .height(60)
          .fontSize(20)
          .backgroundColor(this.isTracking ? '#ff4d4f' : '#2f54eb')
          .onClick(() => {
            if (this.isTracking) {
              this.stopReminder();
            } else {
              this.startReminder();
            }
          })
      }
    }
  }
}

(3)配置文件完善(module.json5)

json 复制代码
{
  "module": {
    "package": "com.example.locationreminder",
    "name": "LocationReminder",
    "mainAbility": "MainAbility",
    "requestPermissions": [
      {
        "name": "ohos.permission.NOTIFICATION_CONTROLLER",
        "reason": "用于发送位置到达提醒通知",
        "usedScene": { "abilities": ["MainAbility"], "when": "always" }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "用于获取大致位置,提供位置提醒服务",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "用于获取精准位置,提升提醒准确性",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      }
    ],
    "abilities": [
      {
        "name": ".MainAbility",
        "type": "page",
        "visible": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      }
    ]
  }
}

四、APP 核心功能说明

1. 设备适配能力

  • 自动识别设备类型(手机 / 平板 / 手表),调整定位策略;
  • 根据屏幕尺寸自适应 UI 布局,在不同设备上显示正常;
  • 适配不同 API 版本,避免因系统版本差异导致功能异常。

2. 合规性保障

  • 批量申请权限,明确告知用户权限用途;
  • 定位和通知功能可手动启停,用户可控;
  • 到达目标后自动停止定位,降低功耗,符合系统规范。

3. 用户体验优化

  • 实时显示当前位置和设备信息,状态透明;
  • 支持自定义目标位置和提醒半径,灵活适配不同场景;
  • 通知点击跳转应用,形成功能闭环。

加入班级,学习鸿蒙开发

相关推荐
nashane4 小时前
HarmonyOS 6学习:CapsLock键失效诊断与长截图完整实现指南
学习·华为·harmonyos
richard_yuu6 小时前
鸿蒙心理测评模块实战|PHQ-9/GAD7双量表答题、实时计分与结果本地化存储
华为·harmonyos
不爱吃糖的程序媛9 小时前
2026年Electron 鸿蒙PC环境搭建指南
人工智能·华为·harmonyos
nashane9 小时前
HarmonyOS 6学习:长截图功能开发中的滚动拼接与权限处理实战
人工智能·华为·harmonyos
大师兄666811 小时前
从零开发一个 HarmonyOS 输入法——KikaInputMethod 完整拆解
harmonyos·服务卡片·harmonyos6·formkit
Python私教16 小时前
鸿蒙 NEXT 也能接 MCP?用 ArkTS 跑通 AI Agent 工具链
人工智能·华为·harmonyos
Swift社区18 小时前
分布式能力在鸿蒙 PC 上到底怎么用?
分布式·华为·harmonyos
nashane1 天前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
aqi001 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony