• Godot Help
  • Unable to Call functions in Java Plugin - Java JNI

Godot Version

4.5dev3 (or any)

Question

Long Story Short:
I made this Java Plugin to manage Android Audio Capture.

The plugin Builds correctly, with Gradle,
and generates the corresponding .aar file.

Everything is on place.

When running we get:
[AudioCaptureService] _ready() CALLED. OS: Android
[AudioCaptureService] Running on Android. Will check for Java plugin 'AudioCaptureMaster'.
[AudioCaptureService] Engine.has_singleton('AudioCaptureMaster') is TRUE.
[AudioCaptureService] SUCCESS: Java plugin 'AudioCaptureMaster' instance obtained: <JNISingleton#2835349665>
ERROR: [AudioCaptureService] ERROR: Java plugin instance does NOT have the method 'checkConnection()' (checked exact Java name)!
ERROR: [AudioCaptureService] This implies a fundamental issue with @UsedByGodot method exposure or the naming convention.
ERROR: [AudioCaptureService] WARNING: Java plugin instance does NOT have the method 'getPluginName()' (checked exact Java name).

So what happens is:
The plugin is indeed Included on Build.
And its name even read and printed..

However the functions/methods inside cannot be Called.

-Just calling the most basic method gives the above error.

Functions:
@UsedByGodot
@Override
public String getPluginName() {
Log.i(TAG, "Java getPluginName() called.");
return "AudioCaptureMaster"; // Use the TAG for plugin name
}

@UsedByGodot
public String checkConnection() {
    Log.i(TAG, "Java checkConnection() method was successfully called from GDScript!");
    return "Java plugin is connected and responding!";

}

Calls:

var exact_java_test_method_name = "checkConnection" # Exact Java method name
				if _plugin_instance.has_method(exact_java_test_method_name):
					# Call using the exact Java name
					var connection_result = _plugin_instance.checkConnection() # CALLING CAMEL CASE
					print("[%s] Call to Java method '%s()' successful! Result: '%s'" % [SCRIPT_NAME, exact_java_test_method_name, connection_result])


var exact_java_get_plugin_name_method = "getPluginName" # Exact Java method name
				if _plugin_instance.has_method(exact_java_get_plugin_name_method):
					var reported_name = _plugin_instance.getPluginName() # CALLING CAMEL CASE
					print("[%s] Java plugin's %s() reports: '%s'" % [SCRIPT_NAME, exact_java_get_plugin_name_method, reported_name])
					if reported_name != java_plugin_name_to_check:
						printerr("[%s] WARNING: Java plugin name mismatch! Expected '%s', got '%s'" % [SCRIPT_NAME, java_plugin_name_to_check, reported_name])
				else:
					printerr("[%s] WARNING: Java plugin instance does NOT have the method '%s()' (checked exact Java name)." % [SCRIPT_NAME, exact_java_get_plugin_name_method])
					printerr("[%s] This might be normal if getPluginName is not intended for direct GDScript calls." % SCRIPT_NAME)

Doesnt matter what you try.
It's not callable!

I thought it could be a snake_case vs camelCase problem, but nope..

-About the Java Plugin build...
I'm building the plugin .aar file with Gradle.

This needs to be built with:

(in build.gradle)

dependencies {
    compileOnly files('libs/godot-lib.template_release.aar')
    
    implementation 'androidx.core:core:1.9.0'

The file godot-lib.template_release.aar comes directly from the android_source.zip file,
be it on the Android Templates, or Github etc..

But the Java Plugn can also be built online with a Maven dependency repo:
implementation 'org.godotengine:godot:4.4.1.stable'

So in essence, we're using the right files for the Gradle build..
the Java code iapparently tight, and thats why the build is succesful.. ?

But In any case, doesnt matter how you build the Java Plugin,
the result is the same:

Plugin is Included on Build.
Godot Can find it!
Plug Cannot be Called..

Doesnt matter what I try, and as far as I can tell,
or Gemini and Grok can tell... (me noob)

Both Java Plugin, and Godot Plugin code is perfectly on the line and should be ready to go.

Long Version:
-I'm loading this with a custom made V2 style Godot Plugin.

plugin.cfg:
[plugin]
name="AudioCaptureMaster"
script="export_plugin.gd"

And somehow managed to include it on Build with: (export_plugin.gd)
func _get_android_libraries(platform, debug):
return PackedStringArray(["../android/plugins/audiocapturemaster/acmrelease.aar"])

---
@tool
extends EditorPlugin

var export_plugin : AndroidExportPlugin

func _enter_tree():
	print("AudioCaptureMaster ExportPlugin entered tree.")
	export_plugin = AndroidExportPlugin.new()
	add_export_plugin(export_plugin)
	add_custom_type("AudioCaptureSignals", "Node", preload("res://addons/audio_capture/AudioCaptureSignals.gd"), null)

func _exit_tree():
	print("AudioCaptureMaster ExportPlugin exited tree.")
	remove_export_plugin(export_plugin)
	remove_custom_type("AudioCaptureSignals")
	export_plugin = null

class AndroidExportPlugin extends EditorExportPlugin:
	var _plugin_name = "AudioCaptureMaster"

	func _supports_platform(platform):
		if platform is EditorExportPlatformAndroid:
			return true
		return false

	func _get_android_libraries(platform, debug):
		return PackedStringArray(["../android/plugins/audiocapturemaster/acmrelease.aar"])
----

This grants the inclusion of the .aar plugin file on Build.

However I cannot even do the most basic Calls:
they get lost on the way
or lost in translation..

-About the Project/Plugin config..
The Plugin is loaded on Project Settings/Plugins
(thanks to the .cfg file)

A Singleton - AudioCaptureService.gd is loaded in Globals/Autoload.
that is what makes the basic call, getPluginName, just to check the plug availability..

The plugin doesnt appear on Export on its own,
but it's included on build, we know that:
[AudioCaptureService] Engine.has_singleton('AudioCaptureMaster') is TRUE.
[AudioCaptureService] SUCCESS: Java plugin 'AudioCaptureMaster' instance obtained: <JNISingleton#2835349665>

In order to make the plugin appear on Export menu, you need to have a .gdap file.

for instance:
[config]
name="AudioCaptureMaster"
binary_type="local"
binary="audiocapturemaster/acmrelease.aar"

But that is unnecesary, (and will not work on its own)
the Plugin will be included on build thanks to:

func _get_android_libraries(platform, debug):
return PackedStringArray(["../android/plugins/audiocapturemaster/acmrelease.aar"])

And that's the situation!

-I have seen other Godot Java plugins work: Admob or Appodeal (outdated).

We have revised how those plugins are made, Top to Bottom,
and as far as we can tell, our Java plug, and the accompanying Godot plug, are well made.

But yeah, doesnt matter what we try..!

It's just not possible to Call the functions/methods in our Java plugin from within Godot.

Any ideas?

What's the correct way to do this?

Ofc we have checked the documentation:
https://docs.godotengine.org/en/stable/tutorials/platform/android/android_plugin.html

And we're adhering to it 100%.

And that's part of the problem too..
If we're following the given steps and doing a semingly correct implementation,
why is this failing?

Where's the rest of Android/Java related documentation?

What can we do to make Godot be able to Call the functions contained inside a Java plugin, which we know is included on Build?

-This is really maddening..

If we could communicate with the Java Plug we will see if it's well made/not,
if it works/not, and we'll be able to work on it and make it Go..!

But since we cannot Call the plugin on the most fundamental level,
we're Blocked and there's nothing we can do!

Just to tiple check, I have built this Godot-Android-Plugin-Template on Github:
https://github.com/m4gr3d/Godot-Android-Plugin-Template

Downloaded the whole thing.

Updated:
plugin/build.gradle.kts - > jvmTarget = "17" // compileSdk = 34 // implementation("org.godotengine:godot:4.4.1.stable")

gradle-wrapper.properties -> to use gradle-8.2-bin.zip (same version Godot uses)

settings.gradle.kts -> rootProject.name = "JavaTemplateTest"

export_plugin.gd -> var _plugin_name = "JavaTemplateTest"

plugin.cfg -> name="JavaTemplateTest"

Placed the files where they need to be:
addons/JavaTemplateTest➤ tree
.
├── bin
│ ├── debug
│ │ └── JavaTemplateTest-debug.aar
│ └── release
│ └── JavaTemplateTest-release.aar
├── export_plugin.gd
├── export_plugin.gd.uid
└── plugin.cfg

Added this on _ready:

if OS.get_name() == "Android":
if Engine.has_singleton(_template_plugin_name):
template_android_plugin_instance = Engine.get_singleton(template_plugin_name)
if is_instance_valid(_template_android_plugin_instance): # Good to check instance validity
print("Template plugin '", _template_plugin_name, "' instance obtained: ", _template_android_plugin_instance)
call_template_plugin_test_method() # Call your test function
else:
printerr("Failed to get a VALID template plugin instance for '", _template_plugin_name, "', but singleton exists.")
else:
printerr("Couldn't find template plugin '", _template_plugin_name, "' using Engine.has_singleton().")

func template_test(): # The demo scene has a button
if _android_plugin:
print("Attempting to call helloWorld() on template plugin...")
android_plugin.helloWorld() # This is the actual call
print("Called helloWorld(). Check for Toast and logcat.")
else:
printerr("Template plugin instance not available to call helloWorld().")
func call_template_plugin_test_method(): # Renamed for clarity
if is_instance_valid(
template_android_plugin_instance): # Check validity again before use
print("Attempting to call helloWorld() on template plugin '", _template_plugin_name, "'...")
var method_to_call = "helloWorld" # Exact Java/Kotlin method name
if _template_android_plugin_instance.has_method(method_to_call):
_template_android_plugin_instance.helloWorld() # This is the actual call
print("Called helloWorld() on template plugin. Check for Toast and logcat.")
else:
printerr("Template plugin '", _template_plugin_name, "' instance does NOT have method '", method_to_call, "'.")
else:
printerr("Template plugin '", _template_plugin_name, "' instance not available or invalid to call helloWorld().")

In Godot, Enabled the JavaTemplateTest Plugin on Project Settings/Plugins

-First of all the Gradle build was Succesful..
BUILD SUCCESSFUL in 2m 5s
52 actionable tasks: 51 executed, 1 up-to-date

-Result:
Godot Engine v4.5.dev3.mono.official.28089c40c - https://godotengine.org
OpenGL API OpenGL ES 3.2 build 1.13@5776728 - Compatibility - Using Device: Imagination Technologies - PowerVR Rogue GE8320

[AudioCaptureService] _ready() CALLED. OS: Android
Template plugin 'JavaTemplateTest' instance obtained: <JNISingleton#2734686363>
Attempting to call helloWorld() on template plugin 'JavaTemplateTest'...
ERROR: Template plugin 'JavaTemplateTest' instance does NOT have method 'helloWorld'.
ERROR: Template plugin instance not available to call helloWorld().
Attempting to call helloWorld() on template plugin 'JavaTemplateTest'...
ERROR: Template plugin 'JavaTemplateTest' instance does NOT have method 'helloWorld'.

Template plugin 'JavaTemplateTest' instance obtained: <JNISingleton#2734686363>
ERROR: Template plugin 'JavaTemplateTest' instance does NOT have method 'helloWorld'.

Great!

Then I updated:
build.gradle.kts
plugins {
id("com.android.library") version "8.3.2" apply false // Example: AGP 8.3.2


id("org.jetbrains.kotlin.android") version "1.9.23" apply false // Example: Kotlin 1.9.23

}

which required:
gradle-wrapper.properties -> gradle-8.4-all.zip

Build OK:
BUILD SUCCESSFUL in 2m 51s
57 actionable tasks: 57 executed

Result:
[AudioCaptureService] _ready() CALLED. OS: Android
Template plugin 'JavaTemplateTest' instance obtained: <JNISingleton#2734686363>
Attempting to call helloWorld() on template plugin 'JavaTemplateTest'...
ERROR: Template plugin 'JavaTemplateTest' instance does NOT have method 'helloWorld'.
ERROR: Template plugin instance not available to call helloWorld().
Attempting to call helloWorld() on template plugin 'JavaTemplateTest'...
ERROR: Template plugin 'JavaTemplateTest' instance does NOT have method 'helloWorld'.

This is the HelloWorld function inside the template plugin:
@UsedByGodot
fun helloWorld() {
runOnUiThread {
Toast.makeText(activity, "Hello World", Toast.LENGTH_LONG).show()
Log.v(pluginName, "Hello World")

The most basic functions are not callable...

-I'm building this on Debian with:
Godot 4.5dev3 mono
Android SDK 34 / NDK 27
Java 17

So I guess we can conclude,
there's a base communication problem between this Template plugin, and Godot.

Now it's not my Java plugin/code anymore..
Maybe this "old" template is not up to date to the latest Godot changes?

Maybe casually this template does the same mistakes I did,
or misses the same details I missed?

-I also tried reducing my own AudioCapture Java code to the absolute minimum,
just the bare calls.. no luck, same result.

So you tell me..!

-Anybody has had any success compiling a Java plugin and using it in Godot?
what version of Godot?

Some Godot plugins are clearly able to do this correctly: (today)
Admob or Appodeal (even tho it's outdated)

But some clearly are Not..

Maybe the Godot-Android-Plugin-Template is wrong on a fundamental level?

Why cannot we Call the functions contained on the Java plugins, if they are included on build?

What does Godot 4.4/4.5 need in order to build and integrate Java plugs correctly?

Any ideas??

Just installed Godot 4.5dev4 mono,
and built the app with a fresh Android Template..

Same result!

Template plugin ‘JavaTemplateTest’ instance obtained: <JNISingleton#2734686363>
Attempting to call helloWorld() on template plugin ‘JavaTemplateTest’…
ERROR: Template plugin ‘JavaTemplateTest’ instance does NOT have method ‘helloWorld’.

-So I posted a issue Report on Github:
https://github.com/godotengine/godot/issues/106436

    ernzo -So I posted a issue Report on Github:

    Time-saving tip:

    When posting a Github issue, you can copy the Godot version string to the clipboard by clicking on the Godot version at the bottom of the Godot editor's main window, or in the Help / About Godot... dialog.

    You can copy the System information to the clipboard using Help / Copy System Info.