1. 从“能用”到“好用”为什么原生插件开发是UniApp进阶的必经之路如果你已经用UniApp做过几个项目从H5到小程序再到App一路下来可能会觉得挺顺。直到有一天产品经理拿着一个需求过来“咱们这个App需要调用一下手机的NFC功能来读卡”、“或者得跟这个特定的蓝牙打印机对接一下”、“再或者得在后台保活一个长连接服务实时接收推送”。你打开UniApp的API文档一查发现官方并没有提供这些能力。这时候你面前的路大概有两条一条是寻找现成的第三方原生插件另一条就是自己动手丰衣足食——开发原生插件。我经历过不少这样的时刻也见过很多团队在这里卡住。找现成插件要么功能不完全匹配需要各种魔改要么年久失修兼容性是个大问题最头疼的是调试云打包一次等几分钟出了问题只能靠猜日志效率极低。所以当项目对性能、稳定性或者特定硬件功能有要求时掌握原生插件的离线打包和调试能力就从“锦上添花”变成了“雪中送炭”。这不仅仅是多会一项技能而是让你对整个UniApp应用的掌控力从云端落到了本地从黑盒变成了白盒。今天我就以一个踩过无数坑的过来人身份带你走通从零开发一个Android原生插件到集成、离线打包、真机调试的完整闭环。你会发现一旦打通这个流程很多之前觉得棘手的问题都会迎刃而解。2. 环境搭建别在起跑线摔跤开发原生插件第一步就是把环境配好。这一步看似基础但很多诡异的问题都源于这里。我们需要两个核心环境UniApp的开发环境HBuilderX和Android的原生开发环境Android Studio。很多人觉得只要装了就行其实里面的门道不少。2.1 HBuilderX与Android Studio的版本“联姻”首先版本兼容性是个隐形杀手。你用的HBuilderX版本决定了它生成的离线打包工程对Android编译环境compileSdkVersion,buildToolsVersion等的要求。我强烈建议你使用HBuilderX官方下载页面推荐的稳定版本而不是一味追求最新。比如在写这篇文章的时候HBuilderX 3.6.18 就是一个经过大量项目验证的稳定版本。与之配套的Android Studio我推荐使用Arctic Fox (2020.3.1)或Bumblebee (2021.1.1)这些LTS长期支持版本而不是最新的预览版。新版本IDE固然有酷炫的功能但可能引入未知的Gradle插件兼容性问题导致离线打包工程导入失败。安装Android Studio时记得通过其内置的SDK Manager把必要的SDK和构建工具装全。关键的有Android SDK Platform对应你项目manifest.json中设置的targetSdkVersion的版本。Android SDK Build-Tools选择一个较新且稳定的版本例如30.0.3或31.0.0。NDK (Side by side)UniApp的原生插件尤其是涉及C/C代码的需要NDK。建议安装一个稳定的版本如r21e或r23c并在项目的local.properties文件中指定路径。注意千万不要在系统环境变量里设置ANDROID_HOME指向一个旧版本的SDK路径这会导致Android Studio的SDK Manager管理的SDK和实际编译使用的SDK不一致引发各种找不到资源的错误。一切以Android Studio内SDK Manager的设置为准。2.2 获取离线打包资源解锁本地编译的钥匙UniApp项目默认是云打包代码和资源会上传到DCloud的服务器进行编译。我们要离线打包首先需要把“原料”下载到本地。在HBuilderX中打开你的UniApp项目点击顶部菜单发行-原生App-本地打包-生成本地打包App资源。这个操作会在你的项目根目录下生成一个unpackage/resources文件夹里面包含一个以本次打包参数如__UNI__XXXXXX命名的目录。这个目录里的内容就是我们App的“灵魂”——所有编译后的前端代码和资源。请妥善保存这个目录我们后续在Android Studio中创建的离线打包工程将直接引用这里的资源。这里有个关键细节每次你修改了前端代码Vue/JS/CSS等都需要重新执行“生成本地打包App资源”否则Android Studio打包出来的APK还是旧的界面和逻辑。很多新手会忘记这一步在原生侧调试半天结果发现是前端资源没更新。3. 创建你的第一个Android原生插件模块环境就绪资源在手现在可以开始动手造轮子了。我们从一个最简单的插件开始实现一个获取手机系统当前时间的插件。这个例子不涉及复杂硬件操作但能完整走通插件创建、方法定义、数据返回的整个流程。3.1 在Android Studio中创建Module打开Android Studio新建或打开一个已有的Android项目作为容器。然后我们新建一个Android Library模块来承载我们的插件代码。File-New-New Module...选择Android Library给模块起个名比如uniplugin_demo_time。注意命名风格建议加上uniplugin_前缀便于识别。Minimum SDK选择API 21 (Android 5.0)或与你主App一致的版本点击完成。现在你的项目结构里应该多了一个uniplugin_demo_time模块。这个模块将独立编译成一个aarAndroid Archive文件最终被主App依赖。3.2 引入UniApp插件核心依赖插件需要与UniApp框架通信因此必须引入官方提供的SDK。DCloud将这些依赖发布在了JitPack仓库。你需要打开uniplugin_demo_time模块下的build.gradle文件通常是Module级别的那个在dependencies块中添加依赖。dependencies { // 核心依赖必须引入 implementation com.github.dcloudio.uni-app:uniplugin_android_plugin:3.6.18 // 版本号与你的HBuilderX版本对应 // 如果需要使用uni-app的基座能力如启动参数等可以引入这个 implementation com.github.dcloudio.uni-app:uniplugin_android_uni:3.6.18 // ... 其他你的模块需要的依赖 }添加后Sync一下项目。如果Sync失败检查项目根目录的build.gradle中是否已经配置了JitPack仓库allprojects { repositories { ... maven { url https://jitpack.io } // 确保有这一行 } }3.3 编写插件核心类实现UniModuleUniApp的Android插件核心是一个继承了UniModule的类。我们在uniplugin_demo_time模块的java目录下新建一个类例如TimeModule。package com.yourcompany.uniplugin_demo_time; import com.alibaba.fastjson.JSONObject; import io.dcloud.feature.uniapp.annotation.UniJSMethod; import io.dcloud.feature.uniapp.bridge.UniJSCallback; import io.dcloud.feature.uniapp.common.UniModule; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class TimeModule extends UniModule { // 定义一个同步方法直接返回数据给JS UniJSMethod(uiThread false) // uiThread false 表示在非UI线程执行适合简单计算 public String getSystemTimeSync() { SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss, Locale.getDefault()); return sdf.format(new Date()); } // 定义一个异步方法通过回调返回数据这是更推荐的方式 UniJSMethod(uiThread true) // uiThread true 表示在UI线程执行如果需要操作UI组件必须为true public void getSystemTimeAsync(JSONObject options, UniJSCallback callback) { // options 是从JS端传过来的参数这里我们没用上 SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss.SSS, Locale.getDefault()); String currentTime sdf.format(new Date()); // 构造返回给JS的数据 JSONObject result new JSONObject(); result.put(time, currentTime); result.put(success, true); // 通过callback.invoke返回结果 // 第一个参数通常为null或一个JSONObject错误信息第二个参数为成功的数据 callback.invoke(null, result); } }代码解读与避坑点UniJSMethod注解这是关键它告诉UniApp框架这是一个可以被JS调用的方法。务必加上。uiThread参数这是最容易出错的地方之一。uiThread true方法将在Android的主线程UI线程执行。如果你在方法里需要更新UI、显示Toast、操作View或者调用某些必须在主线程执行的Android API必须设为true。uiThread false方法将在非UI线程一个后台线程执行。适合执行耗时操作如网络请求、大量计算、文件IO避免阻塞UI导致App卡顿。但要注意在非UI线程里绝对不能直接操作Android的UI组件。同步 vs 异步同步方法如getSystemTimeSync直接返回一个值给JS。它简单但只能返回基本类型或String。复杂对象或需要回调的场景不适用。异步方法如getSystemTimeAsync通过UniJSCallback callback参数来回调。这是主流方式可以返回任意JSON数据也更符合前端异步编程的习惯。callback.invoke的第一个参数传错误对象成功时为null第二个参数传成功的数据。包名建议使用有意义的、唯一的包名避免与其他插件冲突。3.4 注册插件让框架找到你的Module编写完Module类还需要告诉UniApp框架它的存在。我们需要在插件模块的src/main目录下创建assets文件夹如果没有的话然后在其中创建dcloud_uniplugins.json文件。dcloud_uniplugins.json内容如下{ nativePlugins: [ { hooksClass: , // 生命周期钩子类非必需 plugins: [ { type: module, name: TimeModule-Demo, // 插件名将在JS中引用 class: com.yourcompany.uniplugin_demo_time.TimeModule // 完整类路径 } ] } ] }这个文件是插件的“身份证”。name字段非常重要它是前端JS调用时uni.requireNativePlugin传入的标识符。class字段必须是你编写的UniModule类的全限定名一点都不能错。至此一个最简单的Android原生插件模块就开发完成了。你可以先编译一下这个模块Build-Make Module ‘uniplugin_demo_time’确保没有语法错误。4. 集成插件到离线打包工程并编译APK插件模块做好了但它还是一个独立的aar。我们需要把它“安装”到一个完整的UniApp Android项目中并打包成APK。这就是离线打包的核心。4.1 下载并配置官方离线打包工程DCloud为每个版本的HBuilderX提供了对应的Android离线打包SDK和示例工程。你需要去DCloud官方插件市场或开源仓库如https://nativesupport.dcloud.net.cn/AppDocs/download/android下载对应版本的“离线打包SDK”。解压后你会看到一个标准的Android工程目录比如HBuilder-Integrate-AS。用Android Studio打开这个工程。这个工程已经配置好了UniApp运行所需的所有基础依赖和Activity。关键步骤来了把我们刚才生成的本地App资源复制到这个工程中。找到你之前用HBuilderX生成的资源目录unpackage/resources/__UNI__XXXXXX。将该目录下的所有文件主要是apps、data、www等文件夹复制并覆盖到离线打包工程的app/src/main/assets/apps/__UNI__XXXXXX/目录下如果__UNI__XXXXXX目录不存在就新建一个。注意目录层级www文件夹应该直接在__UNI__XXXXXX下面。修改app/src/main/assets/data/dcloud_control.xml文件将其中的appid修改为你的应用ID即__UNI__XXXXXX。4.2 将自定义插件模块引入主App现在要把我们开发的uniplugin_demo_time模块集成进来。模块依赖在离线打包工程的主模块通常是app模块的build.gradle文件中添加对我们插件模块的依赖。dependencies { implementation project(:uniplugin_demo_time) // 如果插件模块在同一项目内 // 或者如果你将插件模块打包成了aar可以这样 // implementation files(libs/uniplugin_demo_time-release.aar) }注册插件光依赖还不够需要在主App的assets/dcloud_uniplugins.json中也注册我们的插件。你可以把插件模块assets/dcloud_uniplugins.json的内容合并到主App的这个文件中。合并后的结果类似{ nativePlugins: [ { hooksClass: , plugins: [ // ... 可能已有其他官方插件 { type: module, name: TimeModule-Demo, class: com.yourcompany.uniplugin_demo_time.TimeModule } ] } ] }注意主App的dcloud_uniplugins.json是最终生效的配置。如果这里不注册即使模块被依赖了JS也调用不到。4.3 处理可能的多余权限声明重要一个常见的坑是权限冲突。你的自定义插件模块的AndroidManifest.xml里可能声明了一些权限比如网络、定位等。主App模块的AndroidManifest.xml也可能声明了相同权限。在Android Gradle构建过程中如果同一个权限被声明多次可能会导致Manifest merger failed错误。解决方案在自定义插件模块的build.gradle中添加以下配置告诉构建工具在合并Manifest时如果遇到重复的权限声明优先使用主App的并忽略本模块的。android { // ... 其他配置 defaultConfig { // ... 其他配置 // 解决Manifest merger重复权限问题 manifestPlaceholders [ // 可以在这里放置一些占位符 ] } } // 或者更直接地在插件模块的AndroidManifest.xml中给权限声明加上 tools:noderemove (不推荐不灵活)更稳妥的做法是自定义插件模块的AndroidManifest.xml只声明该插件独有的、必须的权限。通用的权限如网络访问INTERNET最好在主App的Manifest中统一声明。如果插件需要某个权限应该在插件文档中说明由集成者也就是你自己在主App中手动添加。完成这些配置后尝试点击Android Studio的Run ‘app’按钮将应用安装到连接的手机或模拟器上。如果一切顺利你应该能看到你的UniApp前端界面成功运行在手机上了。不过我们的插件功能还没在前端调用所以暂时看不到效果。5. 前端JS调用与真机调试实战APK能跑起来只是第一步更重要的是能方便地调试。我们将从前端调用插件方法开始并搭建高效的调试环境。5.1 在前端Vue/JS中调用原生插件在你的UniApp前端项目的页面例如index.vue中添加调用代码。template view classcontent text当前系统时间同步{{syncTime}}/text button clickgetSyncTime获取同步时间/button text当前系统时间异步{{asyncTime}}/text button clickgetAsyncTime获取异步时间/button /view /template script export default { data() { return { syncTime: , asyncTime: } }, onLoad() { // 在页面加载时require原生插件 // 这个TimeModule-Demo必须与dcloud_uniplugins.json中注册的name完全一致 this.timeModule uni.requireNativePlugin(TimeModule-Demo); }, methods: { getSyncTime() { // 调用同步方法 try { const result this.timeModule.getSystemTimeSync(); this.syncTime result; uni.showToast({ title: 同步调用成功, icon: none }); } catch (e) { uni.showToast({ title: 调用失败 e.message, icon: none }); } }, getAsyncTime() { // 调用异步方法 this.timeModule.getSystemTimeAsync({ // 这里可以传递参数给原生端对应Java方法中的JSONObject options参数 someKey: someValue }, (res) { // 回调函数res即原生端callback.invoke的第二个参数 console.log(收到原生端回调, JSON.stringify(res)); if (res res.success) { this.asyncTime res.time; } else { uni.showToast({ title: 异步调用失败, icon: none }); } }); } } } /script关键点解析uni.requireNativePlugin这是加载原生插件的唯一方式。参数是字符串必须与dcloud_uniplugins.json中注册的name严格一致包括大小写。这是前后端联调中最常见的错误来源之一。方法调用加载后的模块对象其方法名就是你在Java类中用UniJSMethod注解的方法名。同步方法直接返回值异步方法传入回调函数。参数传递JS对象会通过FastJSON自动转换为Java的JSONObject。你可以传递复杂结构。同样Java端返回的JSONObject也会自动转换为JS对象。错误处理同步调用用try-catch包裹。异步调用的错误通常通过回调的第一个参数返回我们例子中成功时传了null但有些插件设计可能不同需要看具体实现。5.2 重新打包与安装测试修改完前端代码后切记要回到HBuilderX重新执行“生成本地打包App资源”。然后将新生成的资源文件再次覆盖到Android Studio离线打包工程的对应assets目录下。在Android Studio中直接再次点击Run ‘app’。因为只是资源文件更新Android Studio的增量编译通常很快。安装到手机后打开App点击按钮你应该能看到从原生插件返回的系统时间显示在界面上了。恭喜你的第一个原生插件调用成功了5.3 高效的日志调试告别“盲人摸象”能运行只是开始高效的调试才能让你快速定位问题。原生插件开发中日志是你的眼睛。我推荐组合使用以下几种方式1. Android Studio Logcat这是最直接、信息最全的工具。在Android Studio底部找到Logcat标签页。你需要学会过滤日志按进程过滤在Logcat顶部的筛选框中选择你的App包名对应的进程。按标签(Tag)过滤UniApp框架和你的插件日志都有特定Tag。你可以在插件Java代码中使用UniLog或Android原生的Log类打印日志。import io.dcloud.common.util.UniLog; // UniApp提供的日志类会统一输出 import android.util.Log; // Android原生日志类 public class TimeModule extends UniModule { UniJSMethod(uiThread true) public void getSystemTimeAsync(JSONObject options, UniJSCallback callback) { UniLog.d(TimeModule-Demo, getSystemTimeAsync被调用参数: options); // 或者使用 Log.d(TimeModule-Demo, getSystemTimeAsync被调用参数: options); // ... 业务逻辑 } }在Logcat中过滤TimeModule-Demo就能只看你插件的日志。UniLog.d的输出在Logcat中Tag为uniPlugin你也可以过滤这个。2. 前端console.log与uni.showModal不要忽视前端的调试能力。在JS调用插件前后用console.log打印信息在回调函数里详细打印res。对于关键错误可以用uni.showModal弹窗显示在真机上尤其方便。3. 真机调试与代码热重载受限遗憾的是一旦涉及原生插件UniApp的“真机运行”和“云打包”模式下的代码热重载保存即刷新就失效了。因为你的代码运行在原生基座里而基座不包含你的自定义插件。你必须使用离线打包生成的APK进行调试。提升效率的技巧配置调试构建变体在Android Studio的Build Variants工具窗口为app模块选择debug变体。这样打包的APK是可调试的并且会包含调试符号。使用adb logcat命令行如果你习惯命令行adb logcat -s TimeModule-Demo可以持续过滤并输出你的插件日志非常清晰。断点调试在Android Studio中在你插件的Java代码行号左侧点击设置断点。然后以Debug模式运行App点击绿色虫子图标。当JS代码调用到对应原生方法时执行就会暂停在断点处你可以查看所有变量、调用栈进行单步调试。这是解决复杂逻辑问题的终极武器。6. 进阶处理复杂场景与常见“巨坑”指南当你掌握了基础流程后必然会遇到更复杂的需求。下面分享几个进阶场景和对应的避坑经验。6.1 插件间通信与依赖管理你的插件可能需要调用另一个插件的能力或者依赖某个第三方Android库。例如你的插件需要网络请求想复用另一个封装好的“网络请求插件”。方案依赖注入通过UniApp上下文在UniModule子类中你可以通过mUniSDKInstance或mWXSDKEngine获取到App的上下文Context和UniSDKInstance对象。但插件间的直接Java调用并不被UniApp框架直接支持。更优雅的方式是将公共能力下沉为Android Library将网络请求、图片处理等公共逻辑封装成一个纯Android库aar。多个插件模块依赖该公共库你的TimeModule和另一个NetworkModule都implementation这个公共库。通过JS桥接如果两个插件必须通信可以通过前端JS作为中介。插件A将数据通过回调传给JSJS再调用插件B的方法。虽然绕了一点但符合框架设计解耦更彻底。依赖冲突解决 当你的插件引入的第三方库如okhttp 4.9.0与UniApp基础库或主App引入的库如okhttp 3.12.0版本不一致时Gradle会报依赖冲突。使用exclude在插件的build.gradle中排除冲突的模块。implementation (com.squareup.okhttp3:okhttp:4.9.0) { exclude group: com.squareup.okio, module: okio // 根据冲突情况排除 }强制指定版本在项目根目录的build.gradle中使用configurations.all强制所有模块使用同一版本。subprojects { configurations.all { resolutionStrategy { force com.squareup.okhttp3:okhttp:4.9.0 } } }使用强制指定要谨慎可能会引起其他库的不兼容。6.2 插件生命周期与上下文安全UniModule的生命周期与所在的WebView页面绑定。当页面销毁时模块实例可能被回收。因此切忌在模块中持有Activity或Context的长期强引用这会导致内存泄漏。正确获取Contextpublic class MyModule extends UniModule { UniJSMethod(uiThread true) public void doSomething(JSONObject options, UniJSCallback callback) { // 通过 mUniSDKInstance 获取当前Activity上下文 if (mUniSDKInstance ! null mUniSDKInstance.getContext() instanceof Activity) { Activity activity (Activity) mUniSDKInstance.getContext(); // 使用activity例如显示Dialog // 注意确保在UI线程执行 } else { // 上下文不可用可能是页面已销毁 callback.invoke(Context is not available, null); return; } // ... 业务逻辑 } }异步操作的回调安全 如果你在插件中启动了异步任务如线程池、AsyncTask、RxJava等务必确保在回调给JS时检查mUniSDKInstance是否还存活。// 伪代码示例 executor.submit(() - { // 后台执行耗时操作 String result doHeavyWork(); // 回到UI线程回调 mUniSDKInstance.runOnUiThread(() - { if (mUniSDKInstance ! null !mUniSDKInstance.isDestroyed()) { callback.invoke(null, result); } else { UniLog.e(MyModule, 页面已销毁丢弃回调); } }); });6.3 资源、图片与文件路径处理前端通过uni.requireNativePlugin获取的模块无法直接访问前端的static资源或通过uni.xxxAPI选择的文件路径。因为原生端运行在完全不同的沙盒中。前端资源/文件传递给原生端Base64对于小图片可以转成Base64字符串传给原生端解码。临时文件路径使用uni.downloadFile、uni.getImageInfo等API获取文件的临时路径tempFilePath。这个路径是原生FileAPI可以访问的。将这个临时路径字符串传给原生插件。uni.chooseImage({ success: (res) { const tempFilePaths res.tempFilePaths; myNativePlugin.processImage(tempFilePaths[0], (result) {...}); } });_www和_doc等虚拟目录UniApp在原生端映射了一些虚拟目录。例如static目录下的资源在原生端可以通过“assets://www/static/...”或“file:///android_asset/www/static/...”的方式访问具体取决于你使用的API如BitmapFactory.decodeStream。但这种方式比较底层需要原生端代码做特殊处理。原生端文件传递给前端 原生端产生的文件如处理后的图片、生成的报表可以保存到App的内部存储或外部存储的私有目录。然后将文件的绝对路径如/data/data/your.package.name/files/output.jpg或**file://协议URI**返回给JS。前端JS可以通过uni.previewImage预览图片或者使用plus.io接口进行进一步操作。关键陷阱Android 10 (Q) 及以上版本的沙盒限制Scoped Storage从Android 10开始对外部存储的访问受到了严格限制。你的App不能直接通过file://路径访问其他App的文件或公共目录下的某些文件如DCIM,Download除非使用MediaStoreAPI或系统的文件选择器Intent.ACTION_OPEN_DOCUMENT。建议对于需要持久化保存、或需要与其他App共享的文件统一使用MediaStoreAPI。对于App私有文件使用Context.getFilesDir(),getCacheDir()或getExternalFilesDir()。在插件开发中如果涉及文件读写务必在文档中注明所需权限如READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE和Android版本的适配说明。7. 性能优化与发布准备当插件功能稳定后就需要考虑性能和正式发布的问题了。7.1 插件性能优化点线程管理严格遵守UniJSMethod(uiThread )的设定。耗时操作超过16ms一定要放在uiThread false的方法中或者自行创建子线程/线程池。避免阻塞UI线程导致App卡顿、ANRApplication Not Responding。内存管理原生插件是内存泄漏的重灾区。特别注意匿名内部类/Handler持有外部类通常是UniModule的引用容易造成循环引用。使用静态内部类弱引用WeakReference。BitmapAndroid的Bitmap对象非常消耗内存。及时调用recycle()使用BitmapFactory.Options.inSampleSize进行采样压缩。监听器/广播在模块生命周期结束时虽然UniModule没有明确的onDestroy但可以参考onActivityDestroy记得反注册。序列化开销JS和Java之间通过JSON序列化传递数据。频繁调用或传递超大对象如巨大的数组、Base64大图会带来显著的性能开销。尽量设计简洁的接口将大数据拆分成小块或考虑使用ArrayBuffer等更高效的格式这需要更底层的UniComponent支持。7.2 生成Release版AAR并集成调试完成后需要为正式发布生成独立的插件包AAR文件方便集成到最终的发布版APK或提供给其他项目使用。在Android Studio右侧的Gradle工具窗口中找到你的插件模块如:uniplugin_demo_time -Tasks-build-assembleRelease。双击运行。构建完成后在模块的build/outputs/aar/目录下会生成uniplugin_demo_time-release.aar文件。将这个aar文件复制到你的主App离线打包工程的app/libs/目录下没有就新建。修改主App模块的build.gradle依赖将implementation project(‘:uniplugin_demo_time’)改为implementation files(‘libs/uniplugin_demo_time-release.aar’)。执行一次Clean Project然后Rebuild Project。这样主App就依赖了你发布的正式版插件库。7.3 编写插件使用文档一个好的插件离不开清晰的文档。至少应该包含插件名称与IDdcloud_uniplugins.json中配置的name。集成方式如何引入aar或如何依赖模块。权限要求需要在主App的AndroidManifest.xml中添加哪些权限。API说明## getSystemTimeAsync 异步获取系统时间。 **参数** | 参数名 | 类型 | 必填 | 说明 | | :--- | :--- | :--- | :--- | | options | Object | 否 | 预留参数对象当前版本未使用。 | | callback | Function | 是 | 回调函数。 | **回调参数 (res)** | 参数名 | 类型 | 说明 | | :--- | :--- | :--- | | time | String | 格式化的当前系统时间格式为”yyyy-MM-dd HH:mm:ss.SSS”。 | | success | Boolean | 调用是否成功。 | **示例代码** javascript const timeModule uni.requireNativePlugin(TimeModule-Demo); timeModule.getSystemTimeAsync({}, (res) { console.log(系统时间, res.time); });兼容性说明最低支持的Android版本是否有特殊的系统要求。常见问题把调试过程中遇到的典型错误和解决方案列出来。走到这一步你已经独立完成了一个UniApp Android原生插件的开发、集成、调试、优化和发布准备的全流程。这套流程不仅能用于实现特定硬件功能任何需要更高性能、更底层系统交互、或复用现有Android库的场景你都可以通过它来搞定。从被云打包的等待和不确定性所束缚到在本地自由地构建、调试和优化这种掌控感的提升对于解决复杂问题至关重要。下次当遇到UniApp官方API无法满足的需求时你可以自信地说“这个功能我们可以用原生插件来实现。”