Skip to content

Fix Android products arriving in GDScript as an opaque JavaObject#13

Open
DiplomaticRobot wants to merge 1 commit into
godot-x:mainfrom
DiplomaticRobot:fix-android-products-marshalling
Open

Fix Android products arriving in GDScript as an opaque JavaObject#13
DiplomaticRobot wants to merge 1 commit into
godot-x:mainfrom
DiplomaticRobot:fix-android-products-marshalling

Conversation

@DiplomaticRobot

Copy link
Copy Markdown

Problem

On Android, fetch_products never delivers usable products to GDScript. The price/products silently fail to load even though RevenueCat returns the product correctly.

The products signal emits the list as a java.util.ArrayList<Dictionary>. Godot's Android JNI converter (platform/android/jni_utils.cpp, _jobject_to_variant) has conversion cases for:

  • [Ljava.lang.Object;Array (recursing into each element)
  • [Ljava.lang.String;PackedStringArray
  • org.godotengine.godot.Dictionary / HashMapDictionary

…but no case for java.util.ArrayList / List. So the list reaches GDScript as an opaque JavaObject, and raw.call("get", i) returns null — the product dictionaries are unreachable.

Symptom in GDScript:

products payload: products_type=JavaObject size=1 first=<null>

Fix

Emit the products as an Object[] (arrayOfNulls<Any>) instead of an ArrayList. The JNI converts [Ljava.lang.Object; to a Godot Array and recurses into each Dictionary element.

Note: Array<Dictionary> (Dictionary[]) does not work — the JNI check is an exact string match on "[Ljava.lang.Object;", and Dictionary[] is "[Lorg.godotengine.godot.Dictionary;", which falls through to the opaque-object path. It must be a plain Object[].

Verification

Built and run on a physical device (Godot 4.6.2, RevenueCat 10.1.2, Android arm64). GDScript now receives a typed Array of product dictionaries:

products payload: products_type=Array size=1 first={id=… title=… price=…}

Product fetch, purchase, and restore all work end-to-end.

Scope is intentionally limited to this marshalling bug — no behavior or API changes.

fetch_products emitted the products list as a java.util.ArrayList. Godot's
Android JNI (jni_utils.cpp _jobject_to_variant) converts "[Ljava.lang.Object;",
"[Ljava.lang.String;", and Dictionary/HashMap, but has no case for
java.util.ArrayList/List — so the list reaches GDScript as an opaque JavaObject
whose .get(i) returns null, and products/prices never load on Android.

Emit an Object[] (arrayOfNulls<Any>) instead; the JNI converts it to a Godot
Array, recursing into each Dictionary element. Note Array<Dictionary> does not
work: the JNI check is an exact match on "[Ljava.lang.Object;", and Dictionary[]
is "[Lorg.godotengine.godot.Dictionary;". Verified on-device (Godot 4.6.2,
RevenueCat 10.1.2).
val result = Dictionary()
result["products"] = ArrayList<Dictionary>()
// Empty Object[] (not ArrayList) — see the onGetStoreProducts note below.
result["products"] = arrayOfNulls<Any>(0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result["products"] = emptyArray()

onError = { error ->
val result = Dictionary()
result["products"] = ArrayList<Dictionary>()
// Empty Object[] (not ArrayList) — see the onGetStoreProducts note below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unnecessary comments

onGetStoreProducts = { products ->
val list = ArrayList<Dictionary>()

// Emit the products as an Object[] of Dictionary, NOT a java.util.ArrayList.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unnecessary comments (279-288)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants