-
Notifications
You must be signed in to change notification settings - Fork 0
Kotlin and Java Modules
The CLI calls this type Native Module.
Use it when the feature needs Android APIs, an Android or JVM library, user-owned Kotlin or Java code, or performance-sensitive work that can be grouped into a few larger calls.
Returned values are exposed to JavaScript as Promises. Methods returning Unit or void are called without await.
supernote-module doctor --type native
supernote-module add local-calculator --type native --yesThe examples below use:
Package name: local-calculator
JavaScript name: Calculator
Android namespace: com.example.calculator
Write Kotlin and Java files below:
local_modules/local-calculator/android/src/main/java/com/example/calculator/
The exact path follows the Android namespace chosen during generation.
The starter implementation is normally Example.kt. You can edit it, replace it, or add more Kotlin and Java files in the same user-owned source tree.
Do not place implementation inside generated annotation, processor, bridge, or registration packages. Update may replace those files.
Import the generated SupernoteExport annotation and place it on a public instance method:
package com.example.calculator
import com.example.calculator.nativemodule.annotation.SupernoteExport
class Example {
@SupernoteExport
fun add(left: Double, right: Double): Double {
return left + right
}
}Call it from JavaScript or TypeScript:
import Calculator from 'local-calculator';
const total = await Calculator.add(20, 22);The Android build scans the annotation and generates the React Native bridge and TypeScript declaration.
package com.example.calculator;
import com.example.calculator.nativemodule.annotation.SupernoteExport;
public class Example {
public Example() {}
@SupernoteExport
public double add(double left, double right) {
return left + right;
}
}The JavaScript call is unchanged:
const total = await Calculator.add(20, 22);Kotlin and Java can be used together in the same module.
| Kotlin or Java | JavaScript or TypeScript | Return behavior |
|---|---|---|
Boolean / boolean
|
boolean |
Promise |
Double / double
|
number |
Promise |
String |
string |
Promise |
Unit / void return |
void |
Direct call without await
|
The supported values can be used as parameters and return values.
The generated boundary does not currently support nullable values, arrays, collections, callbacks, or arbitrary objects.
Use richer types internally. Convert them to a small supported boundary, for example:
- Encode structured data as a string.
- Pass a few primitive parameters.
- Store complex native state and expose small operations around it.
By default, the JavaScript function uses the Kotlin or Java method name.
@SupernoteExport(name = "greet")
fun makeGreeting(name: String): String {
return "Hello, $name"
}const message = await Calculator.greet('Ziv');Export names must be unique in the module and valid JavaScript property names.
An exported class can request Android context through one of these public constructors, checked in this order:
ReactApplicationContextandroid.content.Context- A constructor with no arguments
Example:
package com.example.calculator
import android.content.Context
import com.example.calculator.nativemodule.annotation.SupernoteExport
class DeviceInfo(private val context: Context) {
@SupernoteExport
fun packageName(): String {
return context.packageName
}
}This is the main reason to choose a Native Module: implementation code is normal Kotlin or Java running as part of the Android application.
Activity injection and arbitrary constructor parameters are not supported. Use the provided context to reach the Android service or API required by the feature.
@SupernoteExport
fun setEnabled(enabled: Boolean) {
// Apply the setting.
}Calculator.setEnabled(true);A Unit or void method has no Promise to reject. If it throws, the generated module logs the exception but cannot return it to JavaScript.
Use a value-returning method when JavaScript needs to know whether the operation succeeded.
Exceptions from a value-returning method reject the generated Promise with the error code:
LOCAL_NATIVE_MODULE_ERROR
The generator exports ordinary public instance methods on public concrete classes.
The exported boundary does not support:
-
suspend,inline,operator, or static methods. - Generic or extension methods.
- Varargs.
- Nullable parameters or returns.
- Arrays, collections, callbacks, or arbitrary objects.
- Unsupported constructor parameters.
These features can still be used internally. Keep the exported method simple and call the more complex implementation behind it.
The expensive part is often not Kotlin or Java itself. It is repeatedly crossing between the isolated JavaScript and Android runtimes.
Prefer:
One JavaScript call
→ Native code processes a complete stroke, page, file, or operation
→ One result returns
Avoid one native call for every point, byte, or tiny state update.
Measure the complete feature on the target Supernote before moving more code into native code. A batched Native Module is often simpler and fast enough.
The generated package is a normal Android library, so dependencies can be added through Gradle.
The generated Gradle files are generator-owned and may be replaced by Update. Save the change in version control or a patch and be prepared to reapply it after updating the module.
The generator does not prove that a third-party library is compatible with the target Supernote Android version, permissions, firmware, or PluginHost.
supernote-module validate local-calculator --buildFor complete output:
supernote-module validate local-calculator --build --verboseRun a build whenever an exported method is added, removed, renamed, or changes signature.
See Managing Modules.
In summary:
supernote-module update local-calculator
supernote-module remove local-calculatorUpdate preserves the user-owned Kotlin and Java implementation tree, excluding generator-owned subpackages. Remove deletes the complete package, including the implementation.