一、引言:uts插件为何成为Uniapp开发的新利器
在Uniapp开发中,当遇到需要调用系统级API或高性能原生功能时,传统的做法是通过编写原生插件(使用Java或Kotlin)并借助JSBridge进行通信。这种模式不仅需要维护两套代码,通信过程中的序列化开销也常常成为性能瓶颈。Uniapp在2023年底推出了uts(Uni Type Script)插件机制,它基于TypeScript语法但能直接编译为各平台原生代码——在Android端编译为Kotlin字节码,在iOS端编译为Swift。这意味着开发者可以用一套类TypeScript代码直接调用Android或iOS的原生API,无需桥接,性能接近纯原生开发。
本文将以一个完整的Android设备信息获取与传感器监听插件为案例,从环境搭建到代码实现,再到打包使用,全面演示uts插件的开发流程。通过本教程,你将能够掌握如何用uts高效地扩展Uniapp的原生能力。
二、环境搭建与项目初始化
开发uts插件需要HBuilderX 3.99及以上版本,并安装uts开发扩展插件。Android端还需安装Android Studio及对应的SDK。
2.1 创建uts插件项目
在HBuilderX中,选择文件 -> 新建 -> uts插件。填写插件名称(例如device-utils),选择平台为Android(之后可添加iOS),然后点击创建。项目结构如下:
device-utils/
├── uts.config.json # 插件配置
├── src/
│ └── index.uts # 插件入口(对外暴露方法)
├── android/
│ └── src/
│ └── index.uts # Android平台实现
├── index.d.ts # 类型声明(供HBuilderX提示)
└── package.json
2.2 配置uts.config.json
该文件定义插件的基本信息及所依赖的原生库:
{
"id": "device-utils",
"version": "1.0.0",
"name": "设备工具插件",
"description": "获取Android设备信息与传感器数据",
"platforms": ["android"],
"android": {
"minSdk": 21,
"permissions": [
"android.permission.BODY_SENSORS",
"android.permission.ACCESS_FINE_LOCATION"
]
}
}
三、编写Android端uts代码
uts在Android端可以直接调用Kotlin/Java API,语法上与TypeScript相似,类型系统则映射到Kotlin类型。我们将在android/src/index.uts中实现获取设备信息和监听传感器的功能。
3.1 获取设备基本信息
以下代码展示了如何调用Android的Build类和DisplayMetrics获取设备硬件信息:
// android/src/index.uts
import { Context } from "android.content.Context";
import { Build } from "android.os.Build";
import { DisplayMetrics } from "android.util.DisplayMetrics";
import { WindowManager } from "android.view.WindowManager";
export function getDeviceInfo(): UTSJSONObject {
const result: UTSJSONObject = {
brand: Build.BRAND,
model: Build.MODEL,
sdkVersion: Build.VERSION.SDK_INT,
releaseVersion: Build.VERSION.RELEASE,
};
// 获取屏幕信息需要Activity上下文
const activity = UTSAndroid.getCurrentActivity();
if (activity != null) {
const metrics = new DisplayMetrics();
const wm = activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager;
wm.getDefaultDisplay().getRealMetrics(metrics);
result["screenWidth"] = metrics.widthPixels;
result["screenHeight"] = metrics.heightPixels;
result["density"] = metrics.density;
}
return result;
}
关键点:
- UTSAndroid.getCurrentActivity()获取当前Activity,这在Android开发中是获取系统服务的基础。
- UTSJSONObject是uts提供的跨平台JSON对象类型,可以无缝传递到前端。
- Android类直接通过import导入,调用方式与Kotlin几乎一致。
3.2 传感器数据监听
接下来实现光线传感器的实时监听,并支持通过回调向前端推送数据。这里需要实现SensorEventListener接口:
import { SensorManager, Sensor, SensorEvent, SensorEventListener } from "android.hardware";
import { Context } from "android.content.Context";
// 保存全局引用防止被GC回收
let sensorManager: SensorManager | null = null;
let lightSensor: Sensor | null = null;
let sensorListener: SensorEventListener | null = null;
let isListening: boolean = false;
let onSensorChange: ((lux: number) => void) | null = null;
export function startLightSensor(callback: (lux: number) => void): void {
const activity = UTSAndroid.getCurrentActivity();
if (activity == null) {
throw new Error("无法获取Activity");
}
// 获取传感器管理器
sensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager;
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if (lightSensor == null) {
throw new Error("该设备没有光线传感器");
}
onSensorChange = callback;
// 创建传感器事件监听器
sensorListener = new SensorEventListener({
onSensorChanged(event: SensorEvent): void {
const lightValue = event.values[0];
if (onSensorChange != null) {
onSensorChange(lightValue);
}
},
onAccuracyChanged(sensor: Sensor, accuracy: number): void {
// 精度变化时的处理
}
});
// 注册监听,第三个参数为采样频率
sensorManager.registerListener(
sensorListener,
lightSensor,
SensorManager.SENSOR_DELAY_NORMAL
);
isListening = true;
}
export function stopLightSensor(): void {
if (sensorManager != null && sensorListener != null) {
sensorManager.unregisterListener(sensorListener);
}
onSensorChange = null;
isListening = false;
}
export function getSensorStatus(): UTSJSONObject {
return {
isSupported: lightSensor != null,
isListening: isListening,
};
}
在uts中实现接口非常简洁,直接传入一个包含对应方法的匿名对象即可。这里我们将光线传感器的数值通过回调函数实时传递出去。
四、编写类型声明文件
为了让Uniapp前端能够获得完整的TypeScript类型提示和代码补全,我们需要在插件根目录的index.d.ts中声明导出函数的类型:
// index.d.ts
export interface DeviceInfo {
brand: string;
model: string;
sdkVersion: number;
releaseVersion: string;
screenWidth?: number;
screenHeight?: number;
density?: number;
}
export interface SensorStatus {
isSupported: boolean;
isListening: boolean;
}
export declare function getDeviceInfo(): DeviceInfo;
export declare function startLightSensor(callback: (lux: number) => void): void;
export declare function stopLightSensor(): void;
export declare function getSensorStatus(): SensorStatus;
五、在前端使用uts插件
插件开发完成后,需要在HBuilderX中打包为uni_modules或本地插件。在Uniapp项目中,将uts插件放入nativeplugins目录或直接引入uni_modules中即可使用。
5.1 调用设备信息
<template>
<view class="page">
<view class="section">
<text class="title">设备信息</text>
<button @click="fetchDeviceInfo">获取设备信息</button>
<text v-if="deviceInfo">品牌:{{ deviceInfo.brand }}</text>
<text v-if="deviceInfo">型号:{{ deviceInfo.model }}</text>
<text v-if="deviceInfo">安卓版本:{{ deviceInfo.releaseVersion }}</text>
<text v-if="deviceInfo.screenWidth">
屏幕分辨率:{{ deviceInfo.screenWidth }}x{{ deviceInfo.screenHeight }}
</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { getDeviceInfo } from '@/nativeplugins/device-utils';
const deviceInfo = ref(null);
const fetchDeviceInfo = () => {
try {
deviceInfo.value = getDeviceInfo();
} catch (e) {
uni.showToast({ title: '获取失败: ' + e.message, icon: 'none' });
}
};
</script>
5.2 监听光线传感器
<template>
<view>
<text>当前光照强度:{{ lightValue }} lux</text>
<button @click="startSensor">开始监听</button>
<button @click="stopSensor">停止监听</button>
</view>
</template>
<script setup>
import { ref, onUnmounted } from 'vue';
import { startLightSensor, stopLightSensor, getSensorStatus } from '@/nativeplugins/device-utils';
const lightValue = ref(0);
const startSensor = () => {
try {
const status = getSensorStatus();
if (!status.isSupported) {
uni.showToast({ title: '设备不支持光线传感器', icon: 'none' });
return;
}
startLightSensor((lux) => {
lightValue.value = lux;
});
} catch (e) {
uni.showToast({ title: e.message, icon: 'none' });
}
};
const stopSensor = () => {
stopLightSensor();
};
// 页面卸载时确保停止监听
onUnmounted(() => {
stopLightSensor();
});
</script>
调用原生功能就像调用普通的JavaScript函数一样简单,而背后运行的是编译后的Kotlin代码,性能与纯原生无异。
六、权限处理与运行时请求
Android 6.0以上系统要求危险权限必须在运行时动态申请。uts可以调用Activity的requestPermissions方法,但更推荐使用Uniapp提供的uni.requestAndroidPermission API进行统一管理。
// 在调用传感器前检查并请求权限
async function requestSensorPermission(): Promise<boolean> {
// 传感器权限在Android中为BODY_SENSORS
const result = await uni.requestAndroidPermission(
'android.permission.BODY_SENSORS'
);
return result === 0; // 0表示授权成功
}
// 在startSensor中增加权限检查
const startSensor = async () => {
const hasPermission = await requestSensorPermission();
if (!hasPermission) {
uni.showToast({ title: '需要传感器权限', icon: 'none' });
return;
}
// 后续启动传感器代码
};
七、性能对比与注意事项
- 通信开销:传统原生插件通过JSBridge序列化数据进行通信,频繁调用会产生明显的延迟。uts插件直接运行在原生线程,调用几乎无额外开销。
- 开发效率:uts使用统一的TypeScript风格编写,前后端开发者均可快速上手,不用学习两套语言体系。
- 内存管理:在uts中创建的Android对象(如SensorManager、SensorEventListener)需要手动管理生命周期,避免内存泄漏。上例中我们在页面卸载时调用了stopLightSensor以释放资源。
- 调试支持:HBuilderX支持uts插件的断点调试和日志输出,可使用console.log在Android Logcat中查看。
- 类型映射:熟悉Kotlin类型与uts类型的对应关系有助于避免编译错误。例如Kotlin的Int对应uts的number,String对应string。
八、进阶扩展:调用系统相册与文件选择器
基于上述模式,可以快速扩展更多原生功能。以下展示如何使用Intent打开系统图片选择器并将结果返回给前端:
import { Intent, Activity } from "android.content";
import { Uri } from "android.net.Uri";
import { Environment } from "android.os.Environment";
export function pickImage(callback: (path: string) => void): void {
const activity = UTSAndroid.getCurrentActivity();
if (activity == null) return;
const intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
// 启动Activity并等待结果(uts支持通过ActivityResultLauncher)
UTSAndroid.startActivityForResult(intent, (resultCode: number, data: Intent) => {
if (resultCode == Activity.RESULT_OK && data != null) {
const uri = data.getData() as Uri;
if (uri != null) {
const path = uri.getPath();
callback(path ?? "");
}
}
});
}
这样前端就能方便地调用系统相册选择图片,并获得文件路径进行后续处理。
九、总结与展望
uts插件为Uniapp开发者打开了原生能力的大门。它消除了JSBridge的性能瓶颈,统一了跨平台开发的语法体验,使得前端工程师也能轻松编写高性能的原生模块。通过本文的设备信息获取与传感器监听案例,你已经掌握了uts插件从创建到部署的完整流程。随着Uniapp官方的持续投入,uts未来将在iOS、鸿蒙等更多平台得到支持,成为跨平台开发的核心利器。现在,不妨尝试将你项目中依赖传统原生插件的部分用uts重写,体验效率与性能的双重提升。

