Webview addJavaScriptInterface

191 views
Skip to first unread message

Gerard Fowley

unread,
Apr 16, 2015, 12:52:46 PM4/16/15
to rub...@googlegroups.com
Thank you for the latest 1.3.0 release, it's motivated me to return to an unfinished project.

I have run into a problem using Webview I hope I can get some help with...

I am using a Webview and Javascript to provide a custom user interface. The Javascript code needs to get data from the Activity via an object made available to Javascript with Webview.addJavaScriptInterface() :
See http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)

(Despite the name of this method, the passed object does not require implementation of a Java Interface.)
For testing purposes I am targeting SDK 16 to remove the Java Annotation requirement from the mix.

I can successfully pass existing Java objects and access their methods from a Chrome Javascript console for the Webview:
See https://developer.chrome.com/devtools/docs/remote-debugging#debugging-webviews
... which is very cool btw!

For an instance of java.lang.String (mywebview_activity.rb):
@wv.add_javascript_interface java::lang::String.new("abc"), "jsi_string"

Javascript console results are as expected:
jsi_string.toString()             #=> "abc"
jsi_string
.length()               #=> 3
jsi_string
.toUpperCase()          #=> "ABC"

But for instances of Ruby classes, with various attempts to specify their Java counterparts (mywebview_activity.rb):
class JSIRuby
  java_signature
"java.lang.String ok()"
 
def ok ; "ok" ; end
end

class JSIRubySubJava < java::lang::Object
  java_signature
"java.lang.String ok()"
 
def ok ; "ok" ; end
end

@wv.add_javascript_interface JSIRuby.new,                   "jsi_ruby"
@wv.add_javascript_interface JSIRubySubJava.new,            "jsi_rubysubjava"
@wv.add_javascript_interface JSIRuby.new.to_java,           "jsi_ruby_to_java"
@wv.add_javascript_interface JSIRubySubJava.new.to_java,    "jsi_rubysubjava_to_java"

Some Java Object methods are accessible from Javascript:
jsi_ruby.toString()               #=> "#<JSIRuby:0x3fd64234>"
jsi_ruby_tojava
.toString()        #=> "#<JSIRuby:0x1af07fd7>"
jsi_rubysubjava
.toString()        #=> "org.jruby.proxy.java.lang.Object$Proxy0@e176b18"
jsi_rubysubjava_tojava
.toString() #=> "org.jruby.proxy.java.lang.Object$Proxy0@36a420c4"

But not the Ruby methods:
jsi_ruby.ok()                     #=> Uncaught TypeError: undefined is not a function
jsi_ruby_tojava
.ok()              #=> Uncaught TypeError: undefined is not a function
jsi_rubysubjava
.ok()              #=> Uncaught TypeError: undefined is not a function
jsi_rubysubjava_tojava
.ok()       #=> Uncaught TypeError: undefined is not a function

I also tried a Ruby class with explicit Java backing class:
ruboto gen subclass java.lang.Object --name=JSIJavaCustom --method_base=none

... created Java methods (JSIJavaCustom.java):
  public java.lang.String ok() {
   
if (ScriptLoader.isCalledFromJRuby()) return "oops! - ScriptLoader.isCalledFromJRuby()";
   
if (!JRubyAdapter.isInitialized()) {
     
Log.i("Method called before JRuby runtime was initialized: JSIJavaCustom#ok");
     
return "oops! - !JRubyAdapter.isInitialized()";
   
}
   
String rubyClassName = scriptInfo.getRubyClassName();
   
if (rubyClassName == null) return "oops! - rubyClassName == null";
   
return (java.lang.String) JRubyAdapter.runRubyMethod(java.lang.String.class, scriptInfo.getRubyInstance(), "ok");
 
}

 
public java.lang.String ok_java() {
   
return "ok_java";
 
}



... created Ruby methods (jsi_java_custom.rb):
class JSIJavaCustom
  java_signature
"java.lang.String ok()"
 
def ok
   
"ok"
 
end
end

... passed an instance to the Webview (mywebview_activity.rb):
require_relative 'jsi_java_custom.rb'
@wv.add_javascript_interface JSIJavaCustom.new,             "jsi_javacustom"

For Javascript console result:
jsi_javacustom.toString()         #=> "#<JSIJavaCustom:0x33a028c6>"
jsi_javacustom
.ok_java()          #=> Uncaught TypeError: undefined is not a function
jsi_javacustom
.ok()               #=> Uncaught TypeError: undefined is not a function

...it seems that only the Java superclass methods are accessible.

I did all of the above with and without java_signature specified.

A final effort with .become_java! :
See https://github.com/jruby/jruby/wiki/GeneratingJavaClasses
...went nowhere fast:
# .newInstance() causes java.lang.InstantiationException: class org.jruby.RubyObject has no zero argument constructor
#  @wv.add_javascript_interface (JSIRuby.become_java!).newInstance(), "jsi_ruby_become_java"

# .become_java! does not work for subclasses of Java classes : https://github.com/jruby/jruby/issues/2359
# @wv.add_javascript_interface (JSIRubySubJava.become_java!).new, "jsi_rubysubjava_become_java"

Right now I have no idea what else to try.

Have I missed something about Ruboto/JRuby to Java interfacing ?
Does the Android Webview require a specific Java-ism that Ruboto/JRuby interfacing does not provide ?

I will share a repo of this test app.
Thanks.
-gf-
















Gerard Fowley

unread,
Apr 16, 2015, 1:08:49 PM4/16/15
to rub...@googlegroups.com

Uwe Kubosch

unread,
Apr 17, 2015, 7:00:35 AM4/17/15
to rub...@googlegroups.com
Hi Gerard!

Good to have you back.  :)  Could you open an issue in the tracker for this?  That makes it easier for me to track it :)

I am swamped with a huge backlog, but I will try to look at it this weekend.


--
You received this message because you are subscribed to the Google Groups "Ruboto (JRuby on Android)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruboto+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Uwe Kubosch

Gerard Fowley

unread,
Apr 17, 2015, 11:04:48 AM4/17/15
to ruboto
Thanks. I'll keep working this myself too.

For anybody who wants to follow, issue opened at: https://github.com/ruboto/ruboto/issues/719

-gf-

Reply all
Reply to author
Forward
0 new messages