在原生 App 中调用 Uniapp 的“原生功能”,本质上是原生代码触发 Uniapp 中通过原生插件实现的功能(如 Uniapp 集成的相机、支付等原生能力),或调用 Uniapp 中定义的 JS 方法(间接触发其关联的原生逻辑)。核心是通过“原生与 Uniapp 的通信桥接”实现双向调用,以下分场景详细说明实现流程。
一、核心原理:原生与 Uniapp 的通信机制
Uniapp 与原生 App 的通信依赖桥接层(由 Uniapp SDK 提供),支持两种方向的调用:
- Uniapp 调用原生功能:通过
uni.requireNativePlugin调用原生插件(Uniapp 官方或自定义插件)。 - 原生调用 Uniapp 功能:原生通过 SDK 提供的 API 发送事件或调用 JS 方法,触发 Uniapp 中对应的逻辑(包括其集成的原生功能)。
本文聚焦第 2 种场景:原生 App 主动触发 Uniapp 中已实现的原生功能(如 Uniapp 中封装的蓝牙扫描、地图定位等)。
二、场景1:原生调用 Uniapp 中通过官方插件实现的原生功能
Uniapp 官方提供了大量原生插件(如 uni.chooseImage 调用相机、uni.getLocation 获取定位),这些功能本质上是 Uniapp 封装的原生能力。原生 App 可通过调用 Uniapp 的 JS 方法间接触发这些功能。
实现步骤:
Uniapp 端定义“触发原生功能的 JS 方法”
在 Uniapp 页面中封装一个方法,内部调用官方插件的原生功能(如调用相机),并通过回调返回结果:// Uniapp 页面(pages/index/index.vue) export default { methods: { // 定义供原生调用的方法:触发相机功能 triggerCamera(callback) { // 调用 Uniapp 官方插件的相机功能 uni.chooseImage({ count: 1, success: (res) => { callback({ code: 0, data: res.tempFilePaths[0] }); // 返回照片路径 }, fail: (err) => { callback({ code: 1, msg: err.errMsg }); } }); } }, onLoad() { // 将方法挂载到 window,供原生调用 window.triggerCamera = this.triggerCamera; } }原生端调用 Uniapp 的 JS 方法
通过 Uniapp SDK 提供的 API 执行 Uniapp 中挂载到window的方法,触发相机功能。Android 原生调用:
使用UniSDKEngine.evaluateJavascript执行 JS 代码,调用window.triggerCamera:// 在原生 Activity 中 String jsCode = "window.triggerCamera(function(res) { " + "window.NativeCallback.onCameraResult(JSON.stringify(res)); " + // 回调结果给原生 "});"; // 执行 JS 代码(需在主线程) runOnUiThread(() -> { UniSDKEngine.evaluateJavascript(jsCode, new UniSDKJSCallback() { @Override public void onReceiveValue(String value) { // 可选:接收 JS 同步返回值 } }); });同时,原生需定义回调接口(
NativeCallback)接收相机结果:// 注册全局回调对象,供 Uniapp JS 调用 public class NativeCallbackModule extends UniModule { @UniJSMethod(uiThread = true) public void onCameraResult(String result) { // 解析 Uniapp 返回的照片路径 JSONObject json = new JSONObject(result); String imagePath = json.getString("data"); // 处理照片(如显示到原生 ImageView) } }iOS 原生调用:
使用DCUniSDK的evaluateJavaScript执行 JS 代码:// 在原生 ViewController 中 NSString *jsCode = @"window.triggerCamera(function(res) { " @"window.webkit.messageHandlers.NativeCallback.postMessage(res); " // 回调给原生 "});"; [[DCUniSDK sharedInstance] evaluateJavaScript:jsCode completionHandler:^(id _Nullable result, NSError * _Nullable error) { // 可选:处理同步返回值 }];通过
WKScriptMessageHandler接收结果:// 实现 WKScriptMessageHandler 协议 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"NativeCallback"]) { NSDictionary *res = message.body; NSString *imagePath = res[@"data"]; // 处理照片路径 } }
三、场景2:原生调用 Uniapp 中通过自定义插件实现的原生功能
若 Uniapp 集成了自定义原生插件(如团队自研的硬件交互插件),原生 App 需通过触发 Uniapp 中调用自定义插件的方法,间接使用该插件功能。
实现步骤:
Uniapp 端封装自定义插件调用方法
假设 Uniapp 集成了一个自定义蓝牙插件BluetoothPlugin,封装方法供原生调用:// Uniapp 页面 export default { methods: { // 调用自定义蓝牙插件扫描设备 triggerBluetoothScan(callback) { const bluetoothPlugin = uni.requireNativePlugin('BluetoothPlugin'); bluetoothPlugin.startScan({ timeout: 5000 }, (res) => { callback(res); // 返回扫描结果 }); } }, onLoad() { window.triggerBluetoothScan = this.triggerBluetoothScan; // 挂载到 window } }原生端调用该方法
与场景1类似,原生通过执行 JS 代码调用window.triggerBluetoothScan:Android:
String jsCode = "window.triggerBluetoothScan(function(res) { " + "window.NativeCallback.onBluetoothResult(JSON.stringify(res)); " + "});"; UniSDKEngine.evaluateJavascript(jsCode, null);iOS:
NSString *jsCode = @"window.triggerBluetoothScan(function(res) { " @"window.webkit.messageHandlers.NativeCallback.postMessage(res); " "});"; [[DCUniSDK sharedInstance] evaluateJavaScript:jsCode completionHandler:nil];
四、场景3:原生直接调用 Uniapp 插件的底层原生代码(进阶)
若自定义插件的原生代码(Android 的 Java/Kotlin、iOS 的 Objective-C/Swift)在原生 App 中可访问,可直接调用插件的底层方法,无需通过 Uniapp JS 中转(适合性能要求高的场景)。
实现条件:
- 自定义插件的原生代码需以库(Library) 形式提供给原生 App(而非仅打包在 Uniapp 资源中)。
- 原生 App 需依赖该插件的原生库,并了解其方法签名。
示例(Android):
假设 Uniapp 自定义插件 BluetoothPlugin 的原生代码中有一个 startScan 方法:
// 自定义插件的原生代码(BluetoothPlugin.java)
public class BluetoothPlugin extends UniModule {
public void startScan(JSONObject params, UniJSCallback callback) {
// 蓝牙扫描逻辑
}
}
原生 App 若依赖了该插件的 AAR 包,可直接实例化并调用:
// 原生 App 中直接调用插件的原生方法
BluetoothPlugin bluetoothPlugin = new BluetoothPlugin();
JSONObject params = new JSONObject();
params.put("timeout", 5000);
bluetoothPlugin.startScan(params, new UniJSCallback() {
@Override
public void invoke(Object data) {
// 处理扫描结果
}
});
五、关键注意事项
- 通信时机:原生调用 Uniapp 功能前,需确保 Uniapp 页面已加载完成(可通过监听 Uniapp 的
onReady事件确认),否则可能导致方法未定义。 - 线程安全:Android 中调用
evaluateJavascript需在主线程,iOS 中需在主线程执行 UI 相关操作。 - 参数序列化:原生与 Uniapp 传递复杂参数(如对象、数组)时,需通过 JSON 序列化(
JSON.stringify/JSON.parse),避免格式错误。 - 权限依赖:Uniapp 插件的原生功能(如相机、蓝牙)需在原生 App 中声明对应权限(如 Android 的
AndroidManifest.xml、iOS 的Info.plist),否则调用会失败。 - 版本兼容性:Uniapp 插件版本需与原生 App 中集成的 SDK 版本匹配,避免因接口变更导致调用失败。
总结
原生 App 调用 Uniapp 的原生功能,本质是通过“原生 → Uniapp JS → Uniapp 插件”的调用链实现,核心依赖 Uniapp SDK 提供的 JS 桥接能力。具体方式需根据场景选择:
- 简单场景:通过
evaluateJavascript调用 Uniapp 中封装的 JS 方法,间接触发原生功能。 - 进阶场景:若自定义插件原生代码可访问,直接调用其底层方法,减少通信开销。
通过这种方式,可充分复用 Uniapp 已实现的原生功能,避免原生 App 重复开发,同时保持两者的功能一致性。