# Android Native Plugin Development Guidelines
# I. Development Environment Preparation
according toNative Plug-In Tool Operating GuidelinesGenerate a multi-terminal plug-in project
Refer to the documentation below and install Weixin DevTools、JDK as well as Android Studio(Android SDK)
Environmental Dependence:
- JAVA version:
JAVA8
<= JAVA version <=JAVA15
- Gradle Version:
6.7.1
- Weixin DevToolsPlease use
1.06.2311282
And above version
- JAVA version:
# II. Introduction to Plug-in Project
- Here's an example of a multi-terminal plug-in project generated by the tool:
- Under the root directory Android The directory is the project for native plug-ins
# Engineering Directory
.
app // Sample application, basically no changes, and do not recommend changes
build.gradle // Gradle Build Script
compile // Certificate stored
Gradle // storage Gradle Wrapper Directory of
gradle.properties // Gradle Property file
gradlew // Gradle Wrapper The startup script for
gradlew.bat // Gradle Wrapper The startup script for
miniapp.json // Some configuration files when running the application, the basic can not be moved
miniapp.plugin.json // Plug-related configuration, described later
plugin // Plug-in directory, plug-in development mainly need to modify the directory code, the following details
settings.gradle // Gradle Setup file
# miniapp.plugin.json
{
"pluginId": "wx45c6d53ff86fd405", // plug-in ID, automatically generated
"pluginVersion": "0.0.1", // plug-in Version, which needs to be used when building
"debugSaaAVersion": "1.0.0", // Dependent get sdk Version (the sdk Only basic rendering functions are included for plugin development. Do not call any of the jsapi Outside the method, i.e. wx.request, etc. wx api Not supported)
"pluginPackageName": "com.donut.wx45c6d53ff86fd405" // Automatically generated plug-in package names
}
# plugin catalog
.
build.gradle // Gradle Build Script
consumer-rules.pro
gradle.properties
libs // Store dependent sdk,sdk Will automatically download
proguard-rules.pro
src
Main
AndroidManifest.xml
java
com
donut
wx45c6d53ff86fd405
PluginManager.kt // Plugin entry file, described in more detail later
resources
META-INF
services
com.tencent.luggage.wxa.SaaA.plugin.NativePluginInterface // Declare the registration plug-in, if the above PluginManager class has changed, you need to synchronize the modification here
# III. Running a Multi-Plug Project
# 1. Switch on the tool Android Mode after the point to run, will directly pull up Android Stuido
Local Environmental Requirements:
- windows Need to be installed in the C disc (Installed in the C Please open Android manually for a directory other than the disk Stuido Import Project)
- mac You may need to create a command line script first
If the point runs pull up Android Stuido Failure, you can also manually create your own in the Android Stuido Import to open the project without affecting the subsequent process
Note: It is possible to report when importingunsupported Java
Please confirm your Android Studio Used in. java Version:
# 2. Android Studio Click here.runButton to see the sample project in action.
# 3. Click firstLoad multiterminal plugins, Load successfully and then clickCalling the Multiterminal Plugin, can be obtained by vconsole See the return of the plug-in method.
Note:1.06.2311282
And above versions of the Developer Tools do not require manual building of the Android resource pack, the Developer Tools remain running when in Android Studio Click here.runThe latest Mini Program code package resources are built automatically when
Build the log as shown:
If it shows here url Is empty. Make sure inSet up->Security settingsThe multiterminal plug-in service port is open
# IV. Plugin Development
# Call timing of multiterminal applications and multiterminal plug-ins
In the beginning Android Before the development of multi-terminal plug-ins, understanding the timing of multi-terminal applications and multi-terminal plug-in calls will help developers better.
sequenceDiagram
participant app as Multiterminal application
participant plugin as Multiterminal plugin
app->>app:wx.miniapp.loadNativePlugin Load plugin
app->>plugin: Load plugin
plugin->>plugin: Instantiate the plug-in object on load
app->>plugin: Load the plug-in, perform a series of initialization operations (including id Registration, plug-in method registration, plug-ins initPlugin Method call)
app->>app: Hold Plugin Instance pluginInstance
Note over app,plugin: Plugin Load Complete
app->>plugin: pluginInstance.func() Make a call to the plug-in method
Please note that Android An instance of the plug-in runs on the Mini ProgramChild processIn. Acquired in this context. Activity Is when the Mini Program is running. Activity。
Registration Load Process sdk Will be implemented, the developer only needs to inherit the NativePluginInterface, the implementation needs to be exposed to js Side method
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
}
// Synchronization method
// js Side-call plugin.mySyncFunc Will trigger test function
@SyncJsApi(methodName = "mySyncFunc")
fun test(data: JSONObject?): String {
android.util.Log.i(TAG, data.toString())
return "test"
}
// Asynchronous method
// js Side-call plugin.myAsyncFuncwithCallback Will trigger testAsync function
@AsyncJsApi(methodName = "myAsyncFuncwithCallback")
fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit) {
android.util.Log.i(TAG, data.toString())
callback("async testAsync")
}
}
# Register synchronization method
- use
SyncJsApi
annotation
parameter | type | Required | Introductions |
---|---|---|---|
methodName | String | yes | in js Layer available for acquisition pluginInstance.methodName Make a method call |
- Input and Output Parameters of Synchronization Method
parameter | type | Required | Introductions |
---|---|---|---|
Entry 1 | JSONObject? | no | in js Layer call pluginInstance.methodName When the incoming Object Type parameter |
Entry 2 | Activity | no | The Mini Program is currently running Activity, to get the parameter normallydebugSaaAVersion To update to the 1.0.1 And above version |
Ginseng | Serializable type | no | in js Layer call pluginInstance.methodName The return of |
- Code Examples
Android Implementation of plug-in synchronization method
@SyncJsApi(methodName = "mySyncFunc")
fun test(data: JSONObject?): String {
android.util.Log.i(TAG, data.toString())
return "test"
}
If you want to get 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 Side of the method call
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)
}
})
# Register Asynchronous Methods
- use
AsyncJsApi
annotation
parameter | type | Required | Introductions |
---|---|---|---|
methodName | String | yes | in js Layer available for acquisition pluginInstance.methodName Make a method call |
- Input and output parameters of asynchronous methods
parameter | type | Required | Introductions |
---|---|---|---|
Entry 1 | JSONObject? | yes | in js Layer call pluginInstance.methodName When the incoming No. 1 individual Object Type parameter |
Entry 2 | function | yes | The callback method supports a parameter of a serializable type. The method callback is passed back to the js layer |
Entry 3 | Activity | no | The Mini Program is currently running Activity, to get the parameter normallydebugSaaAVersion To update to the 1.0.1 And above version |
- Code Examples Android Plug-in asynchronous method implementation
@AsyncJsApi(methodName = "myAsyncFuncwithCallback")
fun testAsync(data: JSONObject?, callback: (data: Any) -> Unit) {
android.util.Log.i(TAG, data.toString())
callback("async testAsync")
}
If you want to get 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 Side of the method call
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)
}
})
# Plugin Event Listening
- in Native Side is inherited by calling the NativePluginBase The method of
sendMiniPluginEvent
May be directed to JS Side to send events.
parameter | type | Required | Introductions |
---|---|---|---|
param | HashMap | yes | in js The input obtained by the listening method of the side |
debugSaaAVersion
To update to the1.0.2
And above version- inheritance
NativePluginBase
- call
sendMiniPluginEvent
, the entry type isHashMap
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())
// When necessary, to js Send Message
val values1 = HashMap<String, Any>()
values1["status"] = "testAsync start"
this.sendMiniPluginEvent(values1)
callback("async testAsync")
// When necessary, to js Send Message
val values2 = HashMap<String, Any>()
values2["status"] = "testAsync end"
this.sendMiniPluginEvent(values2)
}
}
- in JS Side that can use the plug-in instance
onMiniPluginEvent
Method Register Listening
parameter | type | Required | Introductions |
---|---|---|---|
callback | Function | yes | Native Lateral JS The callback triggered when an event is sent on the side, and multiple callbacks can be registered |
- in JS Side that can use the plug-in instance
offMiniPluginEvent
Cancel the wire.
parameter | type | Required | Introductions |
---|---|---|---|
callback | Function | no | Cancel the wire.Cancels all listening callbacks when no callback is specified to cancel |
JS Side of the method call
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)
}
})
# Executing code in the main process
debugSaaAVersion
To update to the1.0.6
And above version
part sdk Requires that code must be initialized in the main process, encapsulating theNativePluginMainProcessTask
Used to facilitate execution in the main process, detailed parameters see the following code example
- First you need to inherit. NativePluginMainProcessTask
Please note that in the following example
valToSync1
andvalToSync2
Just data fields for cross-process transfer. If you don't need to transfer, you can just delete
import com.tencent.luggage.wxa.SaaA.plugin.NativePluginMainProcessTask
import kotlinx.android.parcel .Parcelize
@Parcelize
class TestTask(private var valToSync1: String, private var valToSync2: String) :
NativePluginMainProcessTask() {
private var clientCallback: ((Any) -> Unit)? = null
fun setClientCallback (callback: (data: Any) -> Unit) {
this.clientCallback = callback
}
/**
* Logic that runs in the main process. It is not recommended to perform operations that take too long in the main process
*/
override fun runInMainProcess() {
android.util.Log.e("MainProcess", " runInMainProcess, valToSync1:${valToSync1}, valToSync2:${valToSync2}")
// If you need to call back data from the main process to the Mini Program process, assign the value and call the callback function
valToSync1 = "runInMainProcess"
this.callback() // The callback function synchronizes the task data of the main process and calls runInClientProcess in the child process
}
/**
* Logic that runs in the Mini Program process
*/
override fun runInClientProcess() {
android.util.Log.e("ClientProcess", "valToSync1: ${valToSync1}, valToSync2:${valToSync2}")
this.clientCallback?.let { callback ->
callback(valToSync1)
}
}
override fun parseFromParcel(mainProcessData: Parcel?) {
// If you need to get the master process data, you need to rewrite parseFromParcel to manually parse the Parcel
this.valToSync1 = mainProcessData?.readString() ?: ""
this.valToSync2 = mainProcessData?.readString() ?: ""
}
}
- Actually execute runInMainProcess when needed
@AsyncJsApi(methodName = "registerPush")
fun registerPush(data: JSONObject?, callback: (data: Any) -> Unit, activity: Activity) {
val testTask = TestTask("test1", "test2")
testTask.setClientCallback (callback)
testTask.execAsync() // Real execution of runInMainProcess
}
# requestPermission
debugSaaAVersion
To update to the1.0.6
And above version
To reduce the hassle of requesting permissions on the plug-in side, wrap arequestPermission
method
@AsyncJsApi(methodName = "registerPush")
fun registerPush(data: JSONObject?, callback: (data: Any) -> Unit, activity: Activity) {
android.util.Log.i(TAG, data.toString())
this.requestPermission(
activity,
arrayOf(Manifest.permission.POST_NOTIFICATIONS)
) { permissions, grantResults ->
if (grantResults != null && grantResults.size > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
android.util.Log.i(TAG, "PERMISSION_GRANTED, do invoke again")
callback("PERMISSION_GRANTED")
} else {
android.util.Log.e(TAG, "reloadQRCode fail, SYS_PERM_DENIED")
callback("fail!")
}
}
}
# V. Plug-in Use
# to construct
Android plugins must be built and uploaded before they can really be used
Click on the toolto construct -> to construct Android Plug-in product
, will automatically execute the build command./gradlew :plugin:build
, when the build is complete, it will generate a localAar Folder, and eventually put localAar Folder Contents copy To the directory build/android
Down, for subsequent uploads
- The version number reads
miniapp.plugin.json
InpluginVersion
field
# Third-party dependence Sdk processing
- maven source
If the user has a third-party dependency sdkmaven Source), which can be configured pom file Format Reference:
project.ext.pomDeps = [
"com.tencent.tpns:tpns" : "1.4.0.1-release",
]
tip: Support Maven Central, and the following warehouses
- https://repo1.maven.org /maven2 /
- https://dl.google.com/dl/android/maven2
- https://developer.huawei.com/Repo/
libs or Not mentioned above maven Within the source list
Please refer to the use of(fat-aar-android)[https://github.com/kezong/fat-aar-android]To rely on embed enter aar package
# apply Gradle plugin
Android manufacturers may need to push the manufacturer's plug-in, for example, Huawei manufacturers may need toapply plugin: 'com.huawei.agconnect'
, when building remotely, it can be done inproject.miniapp.json
adoptpluginConfigPath
To configure
The profile for that path needs to be a legitimate json File (which is used when the cloud is built), the reference format is
{
"project": {
// Dependent configuration, optional, build app Is merged into the project root directory build.gradle (do not copy the comments when using)
"dependencies": [
"com.huawei.agconnect:agcp:1.6.0.300"
]
},
"app": {
// Gradle that needs to be used Plugin, Optional, Build app When it comes into effect. (Do not copy comments when using)
"plugins": [
"com.huawei.agconnect"
]
}
}
When added, the gradle plugin s dependencies are added at the top of the build.gradle in the project s root directory
buildscript {
dependencies {
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
in app catalog build.gradle Add to bottom of file apply plugin
apply plugin: 'com.huawei.agconnect'
If you need to upload other files, you can refer to the Android Configure native resources
# upload
Android native plug-ins must be uploaded before they can be used in cloud-built projects
- In order for the cloud build to work with the plugin, please use it before uploading
Construction product
Test it.
- ensure
localAar
There's already a build in the folder miniapp.plugin.json
Add one to the file.debugWithAar
Field, set to true- Android Studio Click here.run, In
build
You can see it in the log.Use Aar
The output of - Self-test to ensure that the logic is consistent with the expected use of the build product
Please note:
- Will be uploaded when it is uploaded
build/android
andandroid/plugin
Two folders - The version number must be the same as the build number when uploading(
miniapp.plugin.json
InpluginVersion
)Consistent, otherwise there will be problems with the use
# Cloud Build Using Plugins
Configure the plug-ins you need to use in your multi-application project ID
- In a multi-end application project that uses plug-ins, Android Sdk Version required
>= 1.1.0
- If needed
activity
orEvent Notification
,Android Sdk Version Please select1.1.9
And above
# debugSaaAVersion Version Update
Android Sdk Please see all versions.Android SDK Update log
# 1.0.6
Minimum Android Sdk Version:1.3.13
A
newly added allow[Executing code in the main process](#Executing code in the main process)A
newly added requestPermission
# Common problem
# Q: It works when debugging, but after uploading it, it fails using the cloud build
- Be sure to ensure that you useConstruction productSelf-tested(
miniapp.plugin.json
filedebugWithAar
fortrue
) - Check the obfuscation configuration for any problems. You can build a Release Version of the Apk Self-test
- before clean project(
./gradlew clean
) ./gradlew :app:assembleRelease
And watchbuild/outpus/Apk /arm64/Release
Down Apk- If you need to add a new obfuscation configuration, you can add it in the
plugin/consumer-rules.pro
In-file processing (do not delete the file's original configuration)
- before clean project(
- adb Logcat or Android Stuido You can look at error messages, you can see if the thrown error messages are useful
# Q: Execution failed for task ':plugin:generatePom'
Please refer to the documentation to check your pom Configuration is configured correctly
# Q: Why does the project change source code invalid and can not break point?
- Please confirm(
miniapp.plugin.json
filedebugWithAar
Was it set uptrue
) - Please verify that the breakpoint process has selected
:wxa_container0