Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GWT: how to pass java array into javascript native method?

I have a java array of String, and now I want to pass it into a JSNI function. I try to use JsArrayString in GWT, however I find that it can not be initialized directly, because it doesn't have a visible constructor. So how can I pass my String array into the JSNI function and use it in my javascript code? The code is looks like follows:

public void callJSNI() {
    String[] stringArray = xxx;

    //How should I convert the array into a JSNI-readable format here?
}

private native void JSNIMethod(JsArrayString array) /*-{
    //some code to use the array in javascript
}-*/
like image 988
Yuhao Avatar asked Aug 31 '25 16:08

Yuhao


2 Answers

The API does not provide an easy way to do it, you'd have to create a utility method that will:

  1. Create a new JSNI array
  2. Iterate over the Java array's arguments and populate the JSNI array

Something like this:

public static JsArrayString toJsArray(String[] input) {
    JsArrayString jsArrayString = createEmptyJsArrayString();
    for (String s : input) {
        jsArrayString.push(s);
    }
    return jsArrayString; 
}

private static native JsArrayString createEmptyJsArrayString() /*-{
    return [];
}-*/;

As the OP suggested, we can, of course, skip the native initialization and use JsArrayString.createArray().

Now we can get rid of the native initialization, so our code reduces to this:

public static JsArrayString toJsArray(String[] input) {
JsArrayString jsArrayString = JsArrayString.createArray().cast();
    for (String s : input) {
        jsArrayString.push(s);
    }
    return jsArrayString; 
}
like image 191
Eliran Malka Avatar answered Sep 02 '25 06:09

Eliran Malka


The answer of Eliran Malka is a good starting point. But be aware that there is an extendend soloution that is much more efficient in some cases. That's why I create another answer.

The simple solution is this:

public static JsArrayString toJsArray(String[] input) {
    JsArrayString jsArrayString = JsArrayString.createArray().cast();
    for (String s : input) {
        jsArrayString.push(s);
    }
    return jsArrayString; 
}

The extended/more efficient solution needs some background knowledge...

The GWT compiler uses JS arrays when the Java code used Java arrays. So in this case you already have a JS array. But this can lead to side-effects that result in different behavior in Dev and Prod mode:

As we have real Java arrays in Dev mode, a conversion to a JS array by copying the values is always necessary. Manipulations in one of the arrays won't be visible to the other array.

In Prod mode if we use the Java array directly as JS array we would only have one array at all. So manipulations in either Java or JS code would affect the other world.

But if the following things apply to your use-case, you can use the array without creating another one:

  • One of these statements must be true
    • The array isn't manipulated by the JS code at all
    • The array isn't used by the Java code after it was given to the JSNI method
  • One of these statements must be true
    • The array is only used once in the JS code (the JS code doesn't create references to the array that last beyond the native method call)
    • The array isn't manipulated by Java code after the native call.

In this case you will not have side effects (different behavior in Dev and Prod mode) and the array can directly be used in the native code.

This kind of implementation is contained in GWT's JsArrayUtils. But unfortunately there is currently no implementation for String arrays. But the implementation will look like this:

public static JsArrayString readOnlyJsArray(String[] array) {
    if (GWT.isScript()) {
        return arrayAsJsArrayForProdMode(array).cast();
    }
    JsArrayString dest = JsArrayString.createArray().cast();
    for (int i = 0; i < array.length; ++i) {
        dest.push(array[i]);
    }
    return dest;
}
private static native JavaScriptObject arrayAsJsArrayForProdMode(Object array) /*-{
    return array;
}-*/;
like image 45
Steffen Schäfer Avatar answered Sep 02 '25 05:09

Steffen Schäfer