Android 原生插件开发指引

一、开发环境准备

  1. 根据原生插件工具操作指引生成一个多端插件工程项目

  2. 参考下方文档,安装 微信开发者工具JDK 以及 Android Studio(Android SDK)

  3. 环境依赖:

二、插件工程介绍

  • 下面以工具生成的多端插件工程项目为例:

  • 根目录下的 Android 目录就是原生插件的项目工程

工程目录

.
├── app // 示例应用,基本无需改动,也不建议改动
├── build.gradle // Gradle 构建脚本
├── compile // 存放了证书
├── gradle // 存储 Gradle Wrapper 的目录
├── gradle.properties // Gradle 属性文件
├── gradlew // Gradle Wrapper 的启动脚本
├── gradlew.bat // Gradle Wrapper 的启动脚本
├── miniapp.json // 运行应用时的一些配置文件,基本可以不用动
├── miniapp.plugin.json // 插件相关配置,后面具体说明
├── plugin // 插件目录,插件开发主要需要修改该目录代码,后面具体说明
└── settings.gradle // Gradle 设置文件

miniapp.plugin.json

{
  "pluginId": "wx45c6d53ff86fd405", // 插件 ID,自动生成
  "pluginVersion": "0.0.1", // 插件 版本,构建时需要用到
  "debugSaaAVersion": "1.0.0", // 依赖的 saaa sdk 版本(该 sdk 仅包含基本渲染功能用于插件开发,进行插件开发时请勿调用除了插件暴露的 jsapi 外的方法,即wx.request等 wx api 均不支持)
  "pluginPackageName": "com.donut.wx45c6d53ff86fd405" // 自动生成的插件包名
}

plugin 目录

.
├── build.gradle // Gradle 构建脚本
├── consumer-rules.pro
├── gradle.properties
├── libs // 存放依赖的 sdk,sdk 会自动下载
├── proguard-rules.pro
└── src
    └── main
        ├── AndroidManifest.xml
        ├── java
        │   └── com
        │       └── donut
        │           └── wx45c6d53ff86fd405
        │               └── PluginManager.kt // 插件入口文件,后面详细说明
        └── resources
            └── META-INF
                └── services
                    └── com.tencent.luggage.wxa.SaaA.plugin.NativePluginInterface // 声明注册插件,如果上面的PluginManager类有变更,需要同步修改这里

三、运行多端插件项目

1. 工具上切换到 Android 模式后点运行,会直接拉起 Android Stuido

本地环境要求:

  • windows 需要安装在 C 盘 (安装在 C 盘以外的目录请手动打开Android Stuido导入项目)
  • mac 可能需要先创建命令行脚本

如果点运行拉起 Android Stuido 失败,你也可以自己手动在 Android Stuido 导入打开项目,不影响后续流程

注:导入的时候有可能会报unsupported Java 请确认你的 Android Studio 里使用的 java 版本:

2. Android Studio 中点击「运行」按钮,即可看到示例工程运行效果。

3. 先点击「加载多端插件」,加载成功后再点击「调用多端插件」,即可通过 vconsole 看到插件方法的返回。

注:1.06.2311282 及以上的版本的开发者工具不需要手动构建Android资源包,开发者工具保持运行时,在Android Studio 中点击「运行」时会自动构建最新的小程序代码包资源

构建日志如图:

如果这里显示 url 为空,请确保 在「设置」->「安全设置」中多端插件服务端口已经开启

四、插件开发

多端应用与多端插件的调用时序

在开始 Android 多端插件的开发前,理解多端应用与多端插件的调用时序将更好帮助开发者进行开发。

请注意,Android 的插件实例运行在小程序的子进程中。在此情境下获取的 Activity 是小程序运行时的 Activity。

注册加载流程 sdk 会实现,开发者只需要继承NativePluginInterface,实现需要暴露给 js 侧的方法

import com.tencent.luggage.wxa.SaaA.plugin.NativePluginInterface
import com.tencent.luggage.wxa.SaaA.plugin.SyncJsApi
import com.tencent.luggage.wxa.SaaA.plugin.AsyncJsApi
import org.json.JSONObject


class TestNativePlugin: NativePluginInterface {
    private val TAG = "TestNativePlugin"

    override fun getPluginID(): String {
        android.util.Log.i(TAG, "getPluginID")
        return BuildConfig.PLUGIN_ID
    }

    // 同步方法
    // js 侧调用 plugin.mySyncFunc 会触发 test 函数
    @SyncJsApi(methodName = "mySyncFunc")
    fun test(data: JSONObject?): String {
        android.util.Log.i(TAG, data.toString())
        return "test"
    }

    // 异步方法
    // js 侧调用 plugin.myAsyncFuncwithCallback 会触发 testAsync 函数
    @AsyncJsApi(methodName = "myAsyncFuncwithCallback")
    fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit) {
        android.util.Log.i(TAG, data.toString())

        callback("async testAsync")
    }

}

注册同步方法

  • 使用 SyncJsApi 注解
参数类型必填说明
methodNameString在 js 层可用获取的 pluginInstance.methodName 进行方法调用
  • 同步方法的入参与出参
参数类型必填说明
入参 1JSONObject?在 js 层调用 pluginInstance.methodName 时传入的 Object 类型参数
入参 2Activity小程序当前运行的 Activity,要正常获取该参数debugSaaAVersion 要更新到 1.0.1及以上版本
出参可序列化类型在 js 层调用 pluginInstance.methodName 的返回
  • 代码示例

Android 侧的插件同步方法实现

    @SyncJsApi(methodName = "mySyncFunc")
    fun test(data: JSONObject?): String {
        android.util.Log.i(TAG, data.toString())
        return "test"
    }

如果要获取 activity:

    @SyncJsApi(methodName = "mySyncFunc")
    fun test(data: JSONObject?, activity: Activity): String {
        android.util.Log.i(TAG, data.toString())

        val componentName = activity.componentName.toString()
        android.util.Log.i(TAG, componentName)
        
        return "test"
    }

JS 侧的方法调用


wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        console.log('load plugin success')
        const ret = plugin.mySyncFunc({ a: 'hello', b: [1,2] })
        console.log('mySyncFunc ret:', ret)
    },
    fail: (e) => {
        console.log('load plugin fail', e)
    }
})

注册异步方法

  • 使用 AsyncJsApi 注解
参数类型必填说明
methodNameString在 js 层可用获取的 pluginInstance.methodName 进行方法调用
  • 异步方法的入参与出参
参数类型必填说明
入参1JSONObject?在 js 层调用 pluginInstance.methodName 时传入的第 1 个 Object 类型参数
入参2函数该回调方法支持一个可序列化类型的参数。通过该方法回调传回给 js 层
入参3Activity小程序当前运行的 Activity,要正常获取该参数debugSaaAVersion 要更新到 1.0.1及以上版本
  • 代码示例 Android 侧的插件异步方法实现
@AsyncJsApi(methodName = "myAsyncFuncwithCallback")
fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit) {
    android.util.Log.i(TAG, data.toString())

    callback("async testAsync")
}

如果要获取 activity:

@AsyncJsApi(methodName = "myAsyncFuncwithCallback")
fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit, activity: Activity) {
    android.util.Log.i(TAG, data.toString())


    val componentName = activity.componentName.toString()
    android.util.Log.i(TAG, componentName)

    callback("async testAsync")
}

JS 侧的方法调用


wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        console.log('load plugin success')
        plugin.myAsyncFuncwithCallback({ a: 'hello', b: [1,2] }, (ret) => {
            console.log('myAsyncFuncwithCallback ret:', ret)
        })
    },
    fail: (e) => {
        console.log('load plugin fail', e)
    }
})

插件事件监听

  • 在 Native 侧通过调用继承于 NativePluginBase 的方法 sendMiniPluginEvent 可向 JS 侧发送事件。
参数类型必填说明
paramHashMap在 js 侧的监听方法获得的入参
  1. debugSaaAVersion 要更新到 1.0.2及以上版本
  2. 继承 NativePluginBase
  3. 调用 sendMiniPluginEvent,入参类型是 HashMap
import com.tencent.luggage.wxa.SaaA.plugin.NativePluginBase

class TestNativePlugin: NativePluginBase(), NativePluginInterface {
    private val TAG = "TestNativePlugin"

    override fun getPluginID(): String {
        android.util.Log.e(TAG, "getPluginID")
        return BuildConfig.PLUGIN_ID
    }

    @AsyncJsApi(methodName = "myAsyncFuncwithCallback")
    fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit) {
        android.util.Log.i(TAG, data.toString())

        // 有需要的时候向 js 发送消息
        val values1 = HashMap<String, Any>()
        values1["status"] = "testAsync start"
        this.sendMiniPluginEvent(values1)

        callback("async testAsync")

        // 有需要的时候向 js 发送消息
        val values2 = HashMap<String, Any>()
        values2["status"] = "testAsync end"
        this.sendMiniPluginEvent(values2)
    }
}
  • 在 JS 侧可使用插件实例的 onMiniPluginEvent 方法注册监听
参数类型必填说明
callbackFunctionNative 侧向 JS 侧发送事件时触发的回调,支持注册多个回调
  • 在 JS 侧可使用插件实例的 offMiniPluginEvent 取消监听。
参数类型必填说明
callbackFunction取消监听;当未指定需要取消的回调时,取消所有监听回调

JS 侧的方法调用


const listener1 = (param) => {
  console.log('onMiniPluginEvent listener1', param)
}

const listener2 = (param) => {
  console.log('onMiniPluginEvent listener2', param)
}

wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        plugin.onMiniPluginEvent(listener1)
        plugin.onMiniPluginEvent(listener2)
    }
})

五、插件使用

构建

安卓插件必需构建上传后,才能真正使用

在工具点击构建 -> 构建 Android 插件产物,会自动执行构建命令./gradlew :plugin:build,构建完成后会生成一个 localAar 文件夹,最终把 localAar 文件夹内容 copy 到目录 build/android 下,用于后续上传

  • 版本号读取的是miniapp.plugin.json里的pluginVersion字段

如果用户有第三方依赖 sdk(maven 源),可以配置 pom 文件 格式参考:

project.ext.pomDeps = [
    "com.tencent.tpns:tpns" : "1.4.0.1-release",
]

tip: 支持 Maven Central ,不支持第三方 Maven 仓库。

上传

安卓原生插件必须上传后才能在云构建的项目中使用

  • 为了云构建使用插件能正常运行,请在上传前先使用构建产物测试一下
  1. 确保 localAar 文件夹里已经有构建产物
  2. miniapp.plugin.json文件里新增一个debugWithAar字段,设置为 true
  3. Android Studio 中点击「运行」,在 build 日志里能看见 Use Aar 的输出
  4. 自测,确保使用构建产物下,逻辑也符合预期

请注意:

  1. 上传时会上传 build/androidandroid/plugin 两个文件夹
  2. 上传的时候版本号必须和构建版本号(miniapp.plugin.json里的pluginVersion)一致,否则使用会有问题

云构建使用插件

在你多端应用的项目里配置需要使用使用的插件 ID

  • 在使用插件的多端应用的项目里,Android Sdk 版本需要 >= 1.1.0
  • 如果需要 activity 或者 事件通知,Android Sdk 版本请选择 1.1.9 及以上

常见问题

Q:调试的时候可以,上传之后使用云构建就失败了

  1. 请务必先保证使用构建产物自测过(miniapp.plugin.json文件debugWithAartrue)
  2. 检查下混淆配置有没有问题,可以构建一个 release 版本的 apk 自测下
    • ./gradlew :app:assembleRelease 然后看 build/outpus/apk/arm64/release下的 apk
    • 如果需要新增混淆配置,可以在plugin/consumer-rules.pro文件内处理(请勿删除该文件原有配置)
  3. adb logcat 或者 Android Stuido 可以看错误信息,可以看下抛出的错误信息是否有用

Q: Execution failed for task ':plugin:generatePom'

请参考文档检查你的 pom 配置是否配置正确