第十二篇:uni-app 与原生插件开发:打通 JS 与原生的桥梁
·
引言:为什么需要原生插件?
uni-app 的“跨平台”能力让我们用一套代码覆盖多端,但总有“够不着”的地方:
- 系统级功能:蓝牙、NFC、指纹识别、系统通知权限
- 高性能需求:图像处理、音视频编解码、复杂计算
- 特定硬件:扫码枪、打印机、POS 机
- 已有原生 SDK:支付、地图、推送、广告
这些功能,H5 和小程序的 API 无法覆盖,必须通过原生代码实现。
原生插件(Native Plugin)就是 uni-app 提供的“桥梁”,让你可以用 JavaScript 调用 Android (Java/Kotlin) 和 iOS (Objective-C/Swift) 的原生能力。
本文将带你从零开始,手把手开发一个 Android/iOS 原生插件,并集成到 uni-app 项目中。
一、原生插件是什么?
原生插件是 uni-app 的一种扩展机制,允许开发者:
- 使用 Java/Kotlin (Android) 或 Objective-C/Swift (iOS) 编写原生代码。
- 通过 JS-Native 通信,在 JavaScript 中调用原生方法。
- 将复杂功能封装成“黑盒”,前端开发者无需关心原生细节。
1.1 工作原理
JavaScript (uni-app)
↓ (调用)
JS Bridge (HBuilderX / App 运行时)
↓ (分发)
Native Plugin (Android: Java/Kotlin, iOS: Objective-C/Swift)
↓ (执行)
原生系统 API
1.2 插件类型
- Module 插件:提供一个或多个方法,供 JS 调用(最常用)。
- Component 插件:创建自定义 UI 组件。
- Adapter 插件:修改底层行为(如网络、文件系统)。
二、开发环境准备
2.1 必备工具
| 工具 | 版本 | 说明 |
|---|---|---|
| HBuilderX | 3.0+ | 官方 IDE,必须使用 |
| Android Studio | 最新 | 开发 Android 插件 |
| Xcode | 12.0+ | 开发 iOS 插件 |
| JDK | 1.8+ | Android 开发环境 |
| Node.js | 14.0+ | 命令行工具 |
2.2 创建插件项目
HBuilderX 提供了插件项目模板。
- 打开 HBuilderX
文件->新建->移动App- 选择
uni-app native 插件项目 - 填写插件信息:
- 插件ID:
DCloud-HelloPlugin(全局唯一) - 插件名称:Hello Plugin
- 插件类型:Module
- 支持平台:Android, iOS
- 插件ID:
生成的目录结构:
hello-plugin/
├── android/ # Android 原生代码
│ ├── src/main/
│ │ ├── java/
│ │ │ └── DCloud/HelloPlugin/HelloPluginModule.java
│ │ └── AndroidManifest.xml
│ └── build.gradle
├── ios/ # iOS 原生代码
│ ├── HelloPlugin/
│ │ └── HelloPluginModule.m
│ └── HelloPlugin.podspec
├── package.json # 插件描述
├── nativeplugins.dcloud.json # 插件配置
└── README.md
三、Android 插件开发 (Java)
3.1 创建 Module 类
// android/src/main/java/DCloud/HelloPlugin/HelloPluginModule.java
package DCloud.HelloPlugin;
import io.dcloud.feature.uniapp.common.UniModule;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import android.util.Log;
import android.widget.Toast;
public class HelloPluginModule extends UniModule {
private static final String TAG = "HelloPlugin";
/**
* 同步方法:返回字符串
*/
@UniJSMethod(uiThread = false)
public String echo(String message) {
Log.d(TAG, "echo called: " + message);
return "Hello from Android: " + message;
}
/**
* 异步方法:通过 callback 返回结果
*/
@UniJSMethod(uiThread = true)
public void showToast(String text, final JSCallback callback) {
Toast.makeText(mWXSDKInstance.getContext(), text, Toast.LENGTH_SHORT).show();
// 异步操作完成后,调用 callback 返回结果
callback.invoke("Toast shown");
}
/**
* 异步方法:支持成功和失败回调
*/
@UniJSMethod(uiThread = false)
public void getData(final JSCallback callback) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 模拟耗时操作
Thread.sleep(1000);
String data = "Data from native";
// 成功回调
callback.invoke(data);
} catch (Exception e) {
// 失败回调
callback.invokeFail(e.getMessage());
}
}
}).start();
}
}
3.2 配置 AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="DCloud.HelloPlugin">
<application>
<!-- 注册 Module -->
<meta-data
android:name="DCloud-HelloPlugin.HelloPluginModule"
android:value="HelloPluginModule" />
</application>
</manifest>
四、iOS 插件开发 (Objective-C)
4.1 创建 Module 类
// ios/HelloPlugin/HelloPluginModule.m
#import "HelloPluginModule.h"
#import "UMModuleFactory.h"
@implementation HelloPluginModule
// 注册 Module
+ (void)registerModule {
[UMModuleFactory registerModule:@"HelloPluginModule" class:[HelloPluginModule class]];
}
/**
* 同步方法
*/
- (NSString*)echo:(NSString*)message {
NSLog(@"echo called: %@", message);
return [NSString stringWithFormat:@"Hello from iOS: %@", message];
}
/**
* 异步方法:通过 callback 返回
*/
- (void)showToast:(NSString*)text callback:(UMPluginCallback*)callback {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:text message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController:alert animated:YES completion:nil];
// 调用 callback
[callback invoke:@"Toast shown"];
});
}
/**
* 异步方法:支持成功/失败
*/
- (void)getData:(UMPluginCallback*)callback {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 模拟耗时
sleep(1);
NSString *data = @"Data from native";
// 成功
[callback invoke:data];
});
}
@end
4.2 配置 .podspec 文件
# ios/HelloPlugin.podspec
Pod::Spec.new do |s|
s.name = 'HelloPlugin'
s.version = '1.0.0'
s.summary = 'A demo native plugin for uni-app'
s.description = 'This is a demo plugin that shows how to develop native plugins for uni-app.'
s.homepage = 'https://github.com/yourname/hello-plugin'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Your Name' => 'your.email@example.com' }
s.source = { :git => 'https://github.com/yourname/hello-plugin.git', :tag => s.version.to_s }
s.ios.deployment_target = '9.0'
s.source_files = 'HelloPlugin/**/*.{h,m}'
s.static_framework = true
s.dependency 'UMCCommon'
end
五、在 uni-app 项目中使用插件
5.1 引入插件
- 将
hello-plugin目录复制到 uni-app 项目的nativePlugins目录下。 - 在
manifest.json中配置:
{
"app-plus": {
"nativePlugins": [
{
"plugins": [
{
"type": "module",
"name": "HelloPlugin",
"class": "HelloPluginModule",
"path": "./nativePlugins/hello-plugin"
}
]
}
]
}
}
5.2 调用原生方法
<!-- pages/index/index.vue -->
<template>
<view class="content">
<button @click="callEcho">调用 echo</button>
<button @click="callShowToast">显示 Toast</button>
<button @click="callGetData">获取数据</button>
<text>{{ result }}</text>
</view>
</template>
<script>
export default {
data() {
return {
result: ''
}
},
methods: {
// 调用同步方法
callEcho() {
// 注意:uni.requireNativePlugin 是 HBuilderX 3.0+ 的新 API
const plugin = uni.requireNativePlugin('HelloPluginModule')
const res = plugin.echo('Hello from JS')
this.result = res
},
// 调用异步方法
callShowToast() {
const plugin = uni.requireNativePlugin('HelloPluginModule')
plugin.showToast('Hello from JS!', (res) => {
console.log('Toast result:', res)
})
},
callGetData() {
const plugin = uni.requireNativePlugin('HelloPluginModule')
plugin.getData((res) => {
console.log('Success:', res)
this.result = res
}, (err) => {
console.error('Error:', err)
this.result = 'Error: ' + err
})
}
}
}
</script>
六、高级技巧与最佳实践
6.1 参数校验
在原生端对 JS 传入的参数进行校验。
// Android
@UniJSMethod(uiThread = false)
public void showToast(Object options, final JSCallback callback) {
if (options == null || !(options instanceof Map)) {
callback.invokeFail("Invalid options");
return;
}
Map<String, Object> map = (Map<String, Object>) options;
String text = (String) map.get("text");
if (text == null || text.isEmpty()) {
callback.invokeFail("Text is required");
return;
}
// ...
}
6.2 错误处理
使用 callback.invokeFail() 返回错误信息。
// iOS
- (void)getData:(UMPluginCallback*)callback {
@try {
// ...
} @catch (NSException *exception) {
[callback invokeFail:[NSString stringWithFormat:@"Exception: %@", exception.reason]];
}
}
6.3 线程控制
@UniJSMethod(uiThread = false):在子线程执行(推荐耗时操作)。@UniJSMethod(uiThread = true):在主线程执行(如 UI 操作)。
6.4 发布插件
可以将插件发布到 DCloud 插件市场,供其他开发者使用。
七、常见问题与调试
- 插件不生效:检查
manifest.json配置、插件 ID 是否唯一。 - 方法找不到:确保方法有
@UniJSMethod注解(Android)或正确注册(iOS)。 - 调试:使用 Android Studio / Xcode 的调试工具,查看原生日志。
八、总结
我们完成了一个完整的原生插件开发流程:
✅ 环境搭建:HBuilderX + Android Studio + Xcode
✅ Android 开发:Java/Kotlin + @UniJSMethod
✅ iOS 开发:Objective-C/Swift + UMPluginCallback
✅ JS 调用:uni.requireNativePlugin
✅ 最佳实践:参数校验、错误处理、线程控制
原生插件是 uni-app 的“核武器”,它打破了跨平台的限制,让你可以触及设备的每一寸能力。
下一篇预告
《uni-app 实战:从零搭建一个电商小程序》
我们将综合运用前面所有知识,手把手带你开发一个完整的电商应用,包含:
- 项目初始化与架构设计
- 商品列表与详情页(虚拟列表优化)
- 购物车与订单流程
- 支付功能集成(原生插件)
- 性能监控与错误上报
敬请期待!
更多推荐

所有评论(0)