引言:为什么需要原生插件?

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 的一种扩展机制,允许开发者:

  1. 使用 Java/Kotlin (Android) 或 Objective-C/Swift (iOS) 编写原生代码。
  2. 通过 JS-Native 通信,在 JavaScript 中调用原生方法。
  3. 将复杂功能封装成“黑盒”,前端开发者无需关心原生细节。
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 提供了插件项目模板。

  1. 打开 HBuilderX
  2. 文件 -> 新建 -> 移动App
  3. 选择 uni-app native 插件项目
  4. 填写插件信息:
    • 插件IDDCloud-HelloPlugin (全局唯一)
    • 插件名称:Hello Plugin
    • 插件类型:Module
    • 支持平台:Android, iOS

生成的目录结构:

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 引入插件
  1. hello-plugin 目录复制到 uni-app 项目的 nativePlugins 目录下。
  2. 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 实战:从零搭建一个电商小程序》

我们将综合运用前面所有知识,手把手带你开发一个完整的电商应用,包含:

  • 项目初始化与架构设计
  • 商品列表与详情页(虚拟列表优化)
  • 购物车与订单流程
  • 支付功能集成(原生插件)
  • 性能监控与错误上报

敬请期待!

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐