Passing a JavaScript Object Using Addjavascriptinterface() on Android

Can I pass a Javascript object out to an Android WebView?

Android's WebView contains a method called addJavascriptInterface(Object obj, String interfaceName) that should be useful here.

Using this method, the object obj that you add as the interface can be accessed via JavaScript code in the Web View. In your case, you could pass in an object that has a setter method that transfers some JavaScript object back to Java.

You'll still need to create the glue code that converts your JavaScript object into the JSON object. For a quick approach, you can just have your interface generate a JSONObject on the Java side using a JSON string passed from JavaScript. The JSONObject class in Java has a constructor that accepts a String containing JSON data. So, you can pass the stringified result directly back to Java and create the object that way. For example:

class JSInterface {
HashMap<String, JSONObject> mObjectsFromJS = new HashMap<String, JSONObject>();
public void passObject(String name, String json) {
mObjectsFromJS.put(name, new JSONObject(json));
}
}

//At some point, register using:
mJSInterface = new JSInterface();
mWebView.addJavascriptInterface(mJSInterface, "Android");

Then, on the JavaScript side, in the handler that has a blob of unparsed JSON in the variable jsonData:

Android.passObject("pageItems", jsonData);

Now, your JSInterface on the Java side will have a JSONObject containing the items, which you can access using the getters provided by JSONObject. The objects created via the Javascript call will be in the mObjectsFromJS map. You'll of course want to add additional helper methods to the JSInterface class to allow for managing the objects better.

I haven't compiled or tested any of these methods, so you may have to tweak them a bit for proper operation. But hopefully this gives you the idea.

However, if the objects have a consistent interface and data items, it would be more sensible to just create a simple JavaScript glue function that binds the JavaScript object properties to the Java-side object fields using setter methods.

PLEASE NOTE
This is giving remote code ability to trigger native code on your device. If you do not have complete control over the pages/scripts being loaded into the WebView, you should ensure that the behavior exposed by obj doesn't allow any exploits.

Passing a JavaScript object using addJavascriptInterface() on Android

AFAIK, addJavascriptInterface() only works with primitive types and Strings, and so you cannot pass arbitrary Javascript objects.

How to pass non-primitive Object from Java to JS by Android addJavascriptInterface?

This has been one of the most hectic hack for me.

The major problem occurs from Android UI-thread and non-UI-thread bind with addJavascriptInterface

Firstly, my mention:
As far as I know, the only way to pass JAVA object to JS is :
wv.addJavascriptInterface(JavaObject, "JsObject");

is wrong.

In fact, I could pass WebSocketNew= my custom Java object properly to JS as return value of getInstance.

However, the object intended to pass/return to JS must be in scope of Android UI-thread.

If we just do return new WebSocketNew(new URI(url))), it's not found by JS, probably because JS runs in UI-thread, and the origin runs on non-UI-thread of addJavascriptInterface.

As I mentioned earlier, since WebScoket instance is on-demand, so I decided to create Object pool in UI-thread first.
This can be done with HashMap with the requested url String.

Java

    final HashMap<String, WebSocketNew> ws = new HashMap<>();
//runs on non-UI-thread
class WebSocketFactory
{
public WebSocketFactory()
{
}

@JavascriptInterface
public WebSocketNew getInstance(String url)
{
System.out.println("============WebSocketFactory============getInstance " + url);

try
{
ws.put(url, new WebSocketNew(new URI(url)));
ws.get(url).connect();

System.out.println("=====WebSocketNew=====" + url + " " + ws.get(url).getReadyState());

return ws.get(url);
}
catch (Exception e)
{
System.out.println("==========ERROR");
return null;
}
}
}

wv.addJavascriptInterface(new WebSocketFactory(), "factoryJ");

wv.loadUrl("file:///android_asset/Content/app.html");

JS

         window.WebSocket = function(url)
{
console.log('####################creating new WebScoketInstance JS ' + url);

var p = {
url: null
};
p.url = url;

var ws = factoryJ.getInstance(p.url);

var obj = {
send: function(data)
{
console.log('--- send: function(data)----- ws.send1(data);------');
ws.send1(data);
},
//.......
//.......

}
return obj;

};

The JS code is related to topic : JavaScript Event implementation to Closure based Object

Send Object from Javascript to Kotlin using Webview

As far as i know, the javaScript interface methods only accepts primitive types of data as parameters (as discussed on this question).
If you still want to achieve that, you may serialize the object (to JSON format, for instance) in the javaScript and then Deserialize it in Java.
Hope it helps :)

passing a JSON object from JavaScript to Android

Add a JavascriptInterface to your webview with:

webView.addJavascriptInterface(new MyJavascriptInterface(), "HTMLOUT");
webView.getSettings().setJavaScriptEnabled(true);

Let the JavacriptInterface have a method like:

public void receiveJSON(String json);

Call this method in javascript with:

document.write(window.HTMLOUT.receiveJSON(myJSONText));

Understanding Android's webview addjavascriptinterface

I want to know that if there is some time before i can access my object

Yes, I think there is a delay, because WebView.addJavascriptInterface will run in the WebView's internal worker thread. Perhaps you've thought about this, and realized that WebView has to maintain at least one worker thread to do asynchronous network IO. Maybe you also noticed these threads in DDMS while using a WebView.

It turns out that it also uses a thread to do work for a number of other public methods. I really wish the docs from Google made this clearer! But I hope I can help and show you how I tried to confirm this for myself.

Follow me as I take a look at the source for WebView. It's reasonably readable, even if you can't follow exactly what's going on, it's possible to trace through answer some questions with respect to threads.

You can download the Android framework source through the SDK manager tool, but it's also mirrored on Github, so that's what I've linked to here. I guessed and picked a tag that's close to some version of ICS. It's not hard to find WebView.addJavascriptInterface. I just Googled "WebView.java site:github.com/android".

The method WebView.addJavascriptInterface sends a message to an instance of WebViewCore:

mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);

In WebViewCore.java there are a bunch of overloaded methods called sendMessage, but we don't really need to know which exactly is being called, since they do pretty much the same thing. There's even a nice comment to give us a hint that we're in the right place! All of them are delegating to an instance of EventHub which is some inner class. This method turns out to be synchronized, and is sending a message to an instance of Handler, which is a good indication that this is probably running in another thread, but for completeness sake, let's find out!

That Handler is instantiated in EventHub.transferMessages which is called from WebViewCore.initialize. There are a few more hops here, but eventually I found out that this is called from run in WebCoreThread (subclass of Runnable), which is instantiated along with a new Thread right here.

What an adventure! So, even though I really can't say for sure what's going on with all these moving parts, I am pretty confident to say that this method is not synchronous, and sends a message to the WebView's worker thread. I hope that makes sense!

if so, how do i get to know how much time should i wait to call my object?

Unfortunately, I don't know the answer to this. I was researching this exact issue and found this question on StackOverflow in the course of my Googling. I think you have the following options, some of which are nicer or easier than others:

1) Just Thread.sleep for 100 ms or something between addJavascriptInterface and loadUrl("javascript:..."). Blech, I don't like this, but it is potentially the easiest.

2) Another possibility is that you could call WebView.loadUrl with a snippet of JavaScript that specifically tests if the interface is set, and catches the ReferenceError that is thrown if it's not set yet. However, as you might have guessed, this kind of involves adding a JavaScript interface to the WebView!

3) Call WebView.setWebChromeClient instead, and catch JavaScript's alert() or console.log instead. From my experiments, this method is synchronous, so there is no delay. (I have confirmed this in source, but I'll leave details as an exercise for the reader) You should probably come up with some special string to call alert with and check for it inside onJsAlert, so you aren't just catching all alert()s.

Sorry for the length of this answer, I hope that helps. Good luck!

How to pass a Blob object from javascript to Android?

I found a solution by converting the blob to Base64 String

 var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var base64data = reader.result;
// send base64data string to android as a method param
}


Related Topics



Leave a reply



Submit