uts 插件示例:获取设备电量信息

上一篇给的示例确实太简略了,连依赖怎么配、类型怎么转换、回调怎么写都没交代清楚。这里用一个完整可跑通的示例重新来:获取 Android/iOS 设备的电量水平。

选这个场景的原因:它足够简单,但覆盖了 uts 插件开发的核心要点------原生 API 调用、数据类型转换、异步回调设计、config.json 配置。

插件目录结构

先在项目的 uni_modules 下新建插件,命名为 uts-getbatteryinfo。完整目录如下:

text

bash 复制代码
uni_modules/uts-getbatteryinfo/
├── utssdk/
│   ├── app-android/
│   │   ├── index.uts          # Android 实现
│   │   └── config.json        # Android 原生配置(可选)
│   ├── app-ios/
│   │   ├── index.uts          # iOS 实现
│   │   └── config.json        # iOS 原生配置(可选)
│   └── interface.uts          # 跨端接口类型定义
└── package.json               # 插件清单

关键点:interface.uts 定义的是跨端统一的类型契约 ,app-android 和 app-ios 各自实现。前端只 import 接口,不关心底层是 Kotlin 还是 Swift

第一步:定义接口类型

utssdk/interface.uts

uts

typescript 复制代码
// 成功回调的数据结构
export type GetBatteryInfoSuccess = {
  errCode: number
  errMsg: string
  level: number        // 电量百分比,0-100
  isCharging: boolean  // 是否正在充电
}

// 失败回调
export type GetBatteryInfoFail = {
  errCode: number
  errMsg: string
}

// 调用参数
export type GetBatteryInfoOptions = {
  success?: (res: GetBatteryInfoSuccess) => void
  fail?: (res: GetBatteryInfoFail) => void
  complete?: (res: any) => void
}

// 对外暴露的函数签名
export type GetBatteryInfo = (options: GetBatteryInfoOptions) => void

这里用的是回调风格(success/fail/complete) ,而不是 Promise。uni-app 的传统 API 都是这个模式,保持一致,调用方不用额外学习

第二步:Android 实现

utssdk/app-android/index.uts

uts

javascript 复制代码
import Context from "android.content.Context"
import BatteryManager from "android.os.BatteryManager"
import { UTSAndroid } from "io.dcloud.uts"
import {
  GetBatteryInfo,
  GetBatteryInfoOptions
} from "../interface.uts"

export const getBatteryInfo: GetBatteryInfo = function (options: GetBatteryInfoOptions) {
  // 获取 Android 应用上下文
  const context = UTSAndroid.getAppContext()

  // 上下文为空时直接走 fail 回调
  if (context == null) {
    const res = {
      errCode: 1001,
      errMsg: "getBatteryInfo:fail getAppContext is null"
    }
    options.fail?.(res)
    options.complete?.(res)
    return
  }

  // 获取电池管理服务
  const manager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager

  // 读取电量百分比(0-100)
  // getIntProperty 返回的是 Int,UTS 里 Number 可以覆盖 Int 场景
  const level = manager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)

  // 判断是否在充电
  const isCharging = manager.isCharging()

  const res = {
    errCode: 0,
    errMsg: "getBatteryInfo:ok",
    level: level,
    isCharging: isCharging
  }

  options.success?.(res)
  options.complete?.(res)
}

几个值得注意的地方:

UTSAndroid.getAppContext() 是 uts 内置的 Android 上下文获取方式,不需要自己传 Contextas BatteryManager 是类型断言,因为 getSystemService 返回的是 Object,编译器需要知道具体类型才能调 getIntProperty

关于 IntNumber:UTS 本身只有 Number,但 Android 原生 API 的 getIntProperty 返回 Int当原生 API 明确要求或返回原生类型时,UTS 允许直接使用 Int ,编译器会做适配。这里 level 拿到的是 Int,赋给 Number 类型的字段是安全的。

第三步:iOS 实现

utssdk/app-ios/index.uts

uts

javascript 复制代码
import { UIDevice } from "UIKit"
import { GetBatteryInfo, GetBatteryInfoOptions } from "../interface.uts"

export const getBatteryInfo: GetBatteryInfo = function (options: GetBatteryInfoOptions) {
  // 开启电池监控,否则 batteryLevel 返回 -1
  UIDevice.current.isBatteryMonitoringEnabled = true

  const level = UIDevice.current.batteryLevel  // 0.0 ~ 1.0
  const state = UIDevice.current.batteryState

  // batteryLevel 为 -1 表示监控未开启或未知
  if (level < 0) {
    const res = {
      errCode: 1002,
      errMsg: "getBatteryInfo:fail battery level unavailable"
    }
    options.fail?.(res)
    options.complete?.(res)
    return
  }

  // 转换为百分比,取整
  const levelPercent = Math.round(Number.from(level) * 100)

  // 充电状态判断
  const isCharging = state == UIDeviceBatteryState.charging ||
                     state == UIDeviceBatteryState.full

  const res = {
    errCode: 0,
    errMsg: "getBatteryInfo:ok",
    level: levelPercent,
    isCharging: isCharging
  }

  options.success?.(res)
  options.complete?.(res)
}

iOS 侧的关键差异:

batteryLevel 返回的是 Float(0.0 到 1.0),需要手动转成百分比。Number.from(level) 把原生 Float 转成 UTS 的 Number,这是跨类型转换的标准做法

isBatteryMonitoringEnabled 必须设为 true,否则 batteryLevel 返回 -1。这是个容易踩的坑。

充电状态用的是 UIDeviceBatteryState 枚举,UTS 可以直接引用。

第四步:前端调用

在页面里:

javascript

javascript 复制代码
import { getBatteryInfo } from '@/uni_modules/uts-getbatteryinfo'

getBatteryInfo({
  success(res) {
    console.log('电量:', res.level + '%')
    console.log('充电中:', res.isCharging)
    uni.showToast({
      title: `电量 ${res.level}%`,
      icon: 'none'
    })
  },
  fail(err) {
    console.error('获取失败:', err.errMsg)
  }
})

注意:直接 import 函数名调用 ,不是 uni.requireNativePlugin。这是 uts 插件和旧原生语言插件最直观的区别------调用方式就是普通的 ES module import,有类型提示,支持摇树优化

第五步:如果要引入三方库

假设你想用某个 Java 工具库(比如 Hutool 的 StrUtil)来做字符串处理,需要在 app-android/config.json 里声明依赖

json

json 复制代码
{
  "minSdkVersion": 21,
  "dependencies": [
    "cn.hutool:hutool-all:5.8.26"
  ]
}

然后在 index.uts 里直接 import:

uts

typescript 复制代码
import StrUtil from 'cn.hutool.core.util.StrUtil'

export const isNotBlank = function (str: string): boolean {
  return StrUtil.isNotBlank(str)
}

HBuilderX 会在真机运行时通过 Gradle 自动下载依赖

什么时候需要自定义基座

只要你的 uts 插件涉及原生依赖、config.json 配置、或隐私合规相关代码,就必须制作自定义调试基座 。标准基座只包含官方内置的原生能力,不包含你新写的插件

流程:HBuilderX → 运行 → 运行到手机或模拟器 → 制作自定义调试基座 → 云打包完成后,再用自定义基座运行。

这个示例覆盖了什么

回头看,这个电量插件虽然简单,但把 uts 插件开发的核心模式都走了一遍:interface 定义契约 → 各平台原生实现 → 类型转换处理 → 回调风格 API 设计 → config.json 依赖声明 → import 调用

换一个更复杂的场景------比如封装一个蓝牙扫描、或者接入一个音视频 SDK------骨架是完全一样的,只是原生 API 调用和类型处理更多而已。

相关推荐
码上成长1 小时前
Mapbox 上用 Turf 裁多边形:屏幕贴边了,接口却说越界
前端·前端框架
计算机魔术师1 小时前
纽约时报告了OpenAI和微软:820万条聊天记录背后,是AI时代最大的版权保卫战
前端
码事漫谈1 小时前
在 Kubernetes 上管好数据库:金仓 KES-Operator 正式落地
前端·后端
用户1880687493521 小时前
手上全是油,我怎么让rokid眼镜在厨房里教我炒菜
前端
gnip1 小时前
uni-app 原生插件
前端·javascript·uni-app
gnip1 小时前
跨域常用解决方案
前端·javascript
用户298698530141 小时前
前端如何把 TXT 文本文件转成 Word 文档:React 实践
前端·javascript·react.js
FEF前端团队1 小时前
03# Claude Code实战:终端里的逻辑引擎
前端·ai编程·claude
FEF前端团队2 小时前
04# Codex CLI实战:OpenAI的编程代理
前端·chatgpt·ai编程