
在 uni-app 开发中,当内置 API 无法满足需求时(如调用特定硬件、集成第三方 SDK),就需要开发 自定义 Android 原生插件。这允许你使用 Java/Kotlin 编写原生代码,并通过 JavaScript 接口在 uni-app 中调用。
uni-app 项目根目录nativeplugins/ 目录 (手动创建):存放所有自定义原生插件。在 nativeplugins/ 下创建插件文件夹,结构如下:
nativeplugins/
└── my-awesome-plugin/ # 插件根目录 (命名规范: 小写字母、数字、下划线)
├── android/ # Android 原生代码
│ ├── src/
│ │ └── main/
│ │ ├── java/ # Java/Kotlin 源码
│ │ │ └── com/
│ │ │ └── dcloud/
│ │ │ └── plugin/
│ │ │ └── MyAwesomePlugin.java # 核心插件类
│ │ ├── AndroidManifest.xml # 插件清单文件
│ │ └── assets/ # 资源文件 (可选)
│ └── build.gradle # 插件构建脚本
├── package.json # 插件描述文件 (必须)
└── index.js # JS 调用接口 (可选,推荐)package.json这是插件的核心描述文件,uni-app 通过它识别插件。
{
"id": "my-awesome-plugin", // 插件唯一标识 (与目录名一致)
"displayName": "我的插件", // 插件名称 (HBuilderX 中显示)
"version": "1.0.0", // 版本号
"description": "一个演示自定义插件的示例", // 描述
"platforms": {
"Android": ">=3.0.0" // 支持的平台和最低 uni-app 版本
},
"engines": {
"HBuilderX": ">=3.0.0" // 最低 HBuilderX 版本
},
"permissions": [
"Camera", // 需要的权限 (会自动合并到主应用)
"Storage"
],
"hooks": [], // 钩子 (较少用)
"nativePlugins": [
{
"type": "module", // 类型: module (功能模块)
"name": "MyAwesomePlugin", // 模块名 (JS 中调用的名称)
"android": {
"plugins": [
{
"type": "service", // 插件类型: service (服务类)
"name": "MyAwesomePlugin", // Java 类名 (不含包名)
"class": "com.dcloud.plugin.MyAwesomePlugin" // 完整类名
}
]
}
}
]
}MyAwesomePlugin.java该类必须继承 io.dcloud.feature.uniapp.common.UniModule。
package com.dcloud.plugin;
import android.util.Log;
import android.widget.Toast;
import io.dcloud.feature.uniapp.annotation.UniJSMethod; // 注解: 标记 JS 可调用方法
import io.dcloud.feature.uniapp.common.UniModule;
import io.dcloud.feature.uniapp.ui.view.WebView;
public class MyAwesomePlugin extends UniModule {
private static final String TAG = "MyAwesomePlugin";
// JS 调用的方法必须用 @UniJSMethod 注解
// mode = UniJSMethod.THREAD_UI: 在 UI 线程执行 (用于更新 UI)
// mode = UniJSMethod.THREAD_MAIN: 在主线程执行 (默认)
@UniJSMethod(uiThread = true)
public void showToast(String message, int duration, UniCallback callback) {
Log.d(TAG, "收到 JS 消息: " + message);
// 在 UI 线程执行 Toast
Toast.makeText(mWXSDKInstance.getContext(),
message,
duration == 1 ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG)
.show();
// 调用 callback 通知 JS 任务完成 (可选)
if (callback != null) {
// 返回给 JS 的数据
UniMPJSResponse response = new UniMPJSResponse();
response.setData("Toast 显示成功: " + message);
response.setSuccess(true);
callback.invoke(response);
}
}
@UniJSMethod
public void getDeviceInfo(UniCallback callback) {
try {
JSONObject deviceInfo = new JSONObject();
deviceInfo.put("model", Build.MODEL);
deviceInfo.put("brand", Build.BRAND);
deviceInfo.put("version", Build.VERSION.RELEASE);
UniMPJSResponse response = new UniMPJSResponse();
response.setData(deviceInfo);
response.setSuccess(true);
callback.invoke(response);
} catch (Exception e) {
UniMPJSResponse response = new UniMPJSResponse();
response.setSuccess(false);
response.setMsg(e.getMessage());
callback.invoke(response);
}
}
// UniModule 提供的上下文
// mWXSDKInstance.getContext() 获取 Context
}AndroidManifest.xml (可选)如果插件需要声明 Activity、Service 或特殊权限,需在此文件中定义。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 插件所需的权限 (如果 package.json 中已声明,可省略) -->
<!-- <uses-permission android:name="android.permission.CAMERA"/> -->
<!-- 如果需要启动 Activity -->
<!-- <activity android:name=".MyPluginActivity" /> -->
</manifest>build.gradle (可选)如果插件依赖第三方库,则需要此文件。
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
// 添加其他依赖...
}index.js)提供一个更友好的 JavaScript 接口给开发者。
// nativeplugins/my-awesome-plugin/index.js
// 导出方法
export function showToast(message, duration = 0) {
// uni.requireNativePlugin 返回原生模块实例
const MyAwesomePlugin = uni.requireNativePlugin('MyAwesomePlugin');
return new Promise((resolve, reject) => {
MyAwesomePlugin.showToast(message, duration, (result) => {
if (result.success) {
resolve(result.data);
} else {
reject(new Error(result.msg || '调用失败'));
}
});
});
}
export function getDeviceInfo() {
const MyAwesomePlugin = uni.requireNativePlugin('MyAwesomePlugin');
return new Promise((resolve, reject) => {
MyAwesomePlugin.getDeviceInfo((result) => {
if (result.success) {
resolve(result.data);
} else {
reject(new Error(result.msg));
}
});
});
}<template>
<view>
<button @click="handleClick">调用原生插件</button>
</view>
</template>
<script>
// 引入封装好的 JS 接口
import { showToast, getDeviceInfo } from '@/nativeplugins/my-awesome-plugin/index.js'
export default {
methods: {
async handleClick() {
try {
// 调用 showToast
await showToast('Hello from Native!', 1);
// 调用 getDeviceInfo
const info = await getDeviceInfo();
console.log('设备信息:', info);
} catch (error) {
console.error('插件调用失败:', error);
}
}
}
}
</script>@UniJSMethod 注解:
uiThread = true: 方法在 UI 线程执行,用于更新 UI (如 Toast, Dialog)。callback: JS 调用时传入的回调函数,在原生方法执行完毕后调用它返回结果。uiThread = true 或使用 runOnUiThread)。package.json 的 permissions 中声明。mWXSDKInstance.getActivity() 获取 Activity 实例来申请。unpackage 目录下的原生工程进行调试。Log.d() 输出日志,在 Android Studio 的 Logcat 中查看。console.log。callback 返回错误信息给 JS。try-catch 或 .catch() 处理 Promise 错误。开发 uni-app 自定义 Android 插件的核心流程:
nativeplugins/your-plugin/ 目录。package.json: 描述插件元信息。UniModule,用 @UniJSMethod 标记方法。index.js: 提供友好的 JS API。uni.requireNativePlugin 或导入 index.js 调用。自定义插件是扩展 uni-app 能力的关键,掌握它能让你的跨平台应用实现几乎任何原生功能。