当你用uni-app开发跨平台应用时,90%的功能都能用H5实现。但总有些场景需要调用手机原生能力,比如:
这时候就需要开发原生插件。我去年做过一个运动类APP,需要实时获取步数数据。H5的计步API在iOS上能用,但在安卓机型上兼容性极差,最后就是通过开发原生插件解决的。
原生插件本质上是个桥梁,让JS能调用Java代码。uni-app支持两种插件模式:
提示:大部分场景用Module模式就够了,只有需要嵌入原生UI时才用Component模式
我第一次配置环境时踩过坑:Android Studio的Gradle版本和项目不匹配,导致编译失败。建议直接用uni-app提供的离线SDK里的示例工程,能省去很多配置麻烦。
下载的SDK包含:
code复制UniPlugin-Hello-AS/
├── app/ # 主模块
├── library/ # 插件模块示例
└── UniPlugin-Hello-AS.iml
重点看library模块,这是插件开发的核心。我建议直接复制这个模块进行改造,比从零新建更稳妥。
Android Library,命名如pedometer-pluginbuild.gradle关键配置:groovy复制dependencies {
compileOnly 'androidx.appcompat:appcompat:1.6.1'
compileOnly 'com.alibaba:fastjson:1.2.83'
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs')
}
用compileOnly避免依赖冲突,这是很多新手容易忽略的点。
创建StepCounterModule.java:
java复制public class StepCounterModule extends UniModule {
@UniJSMethod(uiThread = true)
public void startCounting(JSONObject params, UniJSCallback callback) {
// 获取系统计步服务
SensorManager manager = (SensorManager) mUniSDKInstance.getContext()
.getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = manager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
// 回调步数数据
JSONObject result = new JSONObject();
result.put("steps", 1024);
callback.invoke(result);
}
}
注意三个关键点:
UniModule@UniJSMethod注解UniJSCallback在assets目录新建dcloud_uniplugins.json:
json复制{
"nativePlugins": [{
"plugins": [{
"type": "module",
"name": "StepCounter",
"class": "com.your.package.StepCounterModule"
}]
}]
}
这里有个易错点:json文件必须用UTF-8编码,否则云打包时会解析失败。
在vue文件中:
javascript复制const stepCounter = uni.requireNativePlugin('StepCounter')
stepCounter.startCounting({}, res => {
console.log('今日步数:', res.steps)
})
发行 → 原生App-本地打包 → 生成资源assets文件夹复制到Android项目的main目录我习惯在插件方法里加Log输出:
java复制Log.d("StepCounter", "收到JS调用:" + options.toString());
这样在Android Studio的Logcat里能实时看到调用日志。
:your_module → Tasks → build → assembleRelease/build/outputs/aar/your_module-release.aarcode复制nativeplugins/
└── StepCounter/
├── android/
│ └── your_module-release.aar
└── package.json
package.json:json复制{
"name": "StepCounter",
"id": "StepCounter",
"version": "1.0.0",
"_dp_type": "nativeplugin",
"_dp_nativeplugin": {
"android": {
"integrateType": "aar",
"plugins": [{
"type": "module",
"name": "StepCounter",
"class": "com.your.package.StepCounterModule"
}]
}
}
}
问题1:Minimum SDK version too high
解决方案:在AndroidManifest.xml添加:
xml复制<uses-sdk tools:overrideLibrary="com.your.package" />
问题2:Duplicate class found
这是因为依赖冲突,把所有依赖改为compileOnly即可
我在第一次发布时因为没写清楚使用文档被退回三次,后来总结出文档要包含:
uiThread = false当需要传递复杂数据时,建议用JSON序列化:
java复制@UniJSMethod(uiThread = false)
public JSONObject getDeviceInfo() {
JSONObject info = new JSONObject();
info.put("model", Build.MODEL);
info.put("sdk", Build.VERSION.SDK_INT);
return info;
}
重写这些方法处理资源释放:
java复制@Override
public void onActivityDestroy() {
// 释放传感器等资源
sensorManager.unregisterListener(this);
}
去年开发蓝牙插件时遇到个典型问题:Android不同版本API差异大。最后通过版本判断解决:
java复制@UniJSMethod(uiThread = true)
public void connectBluetooth(JSONObject params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 用新的API
} else {
// 兼容旧版本
}
}
另一个经验是做好错误处理:
java复制try {
// 可能出错的操作
} catch (Exception e) {
callback.invokeAndKeepAlive(new JSONObject() {{
put("code", "error");
put("message", e.getMessage());
}});
}