Android WebView Async Call to Java Function - javascript

I'm looking for a way to make async calls to Android's native code from JS
I have a Main Activity with the following code to make accessible native code to JS:
webView.addJavascriptInterface(new BindingHelper(this), "Android");
webView.loadUrl("file:///android_asset/www/index.html");
The BindingHelper Class contains something like this:
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(theContext, toast, Toast.LENGTH_SHORT).show();
}
#JavascriptInterface
public String SuperDuperComplexFunction () {
//A function that will need some time to finish...
return "{}";
}
The previous Methods can be called from the Index.html linked JS as follows:
<script type="text/js">
Android.showToast("Toast");
</script>
In that way the showToast() function is executed synchronously. What I need is to call the method SuperDuperComplexFunction(); in a aSync way (just like an AJAX Request), and when the method success take some action.
Any ideas?

One option is to use an Http Server in java code and then make the AJAX call on localhost. That way the Javascript call would be the exact same as any other AJAX call, and since you control the Http Server you can just have it call your SuperDuperComplexFunction()
I've used NanoHttpd in the past for something kind of similar but not quite the same.

Related

Passing String variable from Java to JavaScript via Webview (vice versa)

I am trying to make an app where two users can interact through webview. I want to get a String variable from javascript to Java so that I can save it in my Sqlite database.(And vice versa)
Calling Java/Kotlin code from JavaScript
There's a feature of Android WebView called Javascript Interface which is what you should look for.
It allows you to run your java/kotlin code from your javascript code.
You can define an interface by
val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")
Here, WebAppInterface defines the methods which you want to be called from javascript.
/** Instantiate the interface and set the context */
class WebAppInterface(private val mContext: Context) {
/** Show a toast from the web page */
#JavascriptInterface
fun showToast(toast: String) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
}
}
"Android" here is an interface added to your webview and can be used in your javascript code to call Android methods.
Once this is done, you can call the methods from your javascript,
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
</script>
Keep in mind that the interface callbacks are not always called in main thread. So, if you want to perform any UI operations, make sure you use runOnUiThread.
Calling JavaScript function from Java/Kotlin code
There's a feature of Android WebView called Evaluate Javascript for API 19+ to call your javascript functions from java/kotlin code.
For example, for the following javascript:
<script type="text/javascript">
function printName(name) {
console.log(name);
}
</script>
You can write this code to call this JS function:
yourWebview.evaluateJavascript("printName(\'TEST\')")

Calling a Blazor method From JavaScript

I'm experimenting with Blazor in Visual Studio, specifically with calling Blazor code from JavaScript. I'm reasonably confident that I've got all the right libraries in place. However, when I attempt to call my Blazor method with invokeMethodAsync, I get the message "no .net call dispatcher has been set". In my Index.Html file, I have this:
<script>
DotNet.invokeMethodAsync("BlazorFour.App", "HelloYou").then(data => alert(data), reason => alert(reason));
</script>
(It's the alert(reason) that generates the error message)
I've added a class file to my Blazor project and it contains this:
using Microsoft.JSInterop;
using System.Threading.Tasks;
public class HelloWorld
{
[JSInvokable]
public static Task<string> HelloYou()
{
return Task.FromResult("Hello, ");
}
}
I've used all the templates in Visual Studio and the dotnet -new blazor commandline utility to create my start points but get the same message in all of the projects. It's seems likely to me that I'm missing something fundamental here.
Peter Vogel:
It would be great if you could call Blazor C# methods from your JavaScript code. Right now, that's not quite possible: Simply calling Blazor C# from JavaScript won't work (you'll get a message about a call dispatcher not being available).
What you can do, however, is once your Blazor code is running, call JavaScript code from your C# code. Once you've done that, your JavaScript code can, in turn, call Blazor code.
Hope this solves the problem...
I'm using server-side-blazor
I was getting js errors
No .NET call dispatcher has been set
So yep .. looks like Blazor wasn't initialized..
Not keen on a timeout
.. seems hacky and gives a laggy UI
Not keen on 'call JavaScript code from your C# code. Once you've done that, your JavaScript code can, in turn, call Blazor code.'
.. again seems hacky
This works for me ..
async function myBlazoryFunctionThing() {
// see https://sourcegraph.com/github.com/aspnet/AspNetCore#bd65275148abc9b07a3b59797a88d485341152bf/-/blob/src/Components/Web.JS/src/Boot.Server.ts#L41:9
await window.Blazor.reconnect();
..now it's safe to do my stuff
}
it 'seems' that Blazor.reconnect() will re-use the existing connection (if there is one)
...so it's not actually 're-connecting' (so 'seems' not much overhead ;-))
I tried a setTimeout (5000 ms delay) and it worked for me.(of course it is not needed to use the setTimeout every time, just the first time).
e.g. setTimeout( DotNet.invokeMethod(...), 5000);
I guess this is a timing issue, not everything is loaded/initialized.
Have you tried putting the js code in a button onclick event?
using Microsoft.JSInterop;
[Inject] protected IJSRuntime JSRuntime { get; set; }
From your blazor function call this function from this method
await JSRuntime.InvokeAsync<object>("fnToJavascriptCall", DotNetObjectReference.Create(this));
Create this function in blazor file
[JSInvokable]
public async Task fnToCallFromJavascript(int param1, int param2)
{
//Do some stuff
StateHasChanged();
}
In your javascript file
var fnToJavascriptCall = function (param1,currentInstance)
{
//Do your stuff
currentInstance.invokeMethodAsync("fnToCallFromJavascript", param1, param2);
}
The No .NET call dispatcher has been set error can be received when the JavaScript interop is used at the wrong time. Have a look at this StackOverflow question.

Android webview javascript not working on API 18 or higher

I want to display a website in WebView with manipulating some DOM element. I mean, I want to remove a special div element or replace it with something.
For this purpose I did something like this:
For replacing something I use this model
document.getElementById("demo").innerHTML = "Hello World!";
I apply it for Android WebView like this:
public class WebClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, final String url) {
view.loadUrl("javascript:document.getElementById(\"jumbotron-company\").innerHTML = \"\";");
}
}
It is work on my device which API is 18 but not working on emulators or device which have API greater than 18
What #Mattia said is correct. Use evaluateJavascript if possible. It has another advantage of being able to return the value of the result back from JavaScript.
But if you want an API-agnostic workaround, make sure that the code you evaluate using loadUrl("javascript:...") just doesn't produce any return value. An assignment returns the assigned value. In newer versions of WebView this value is used as contents for a new page that replaces the existing one.
You can make sure that the expression you are evaluating doesn't produce a return value in two ways:
append void(0); statement at the end, e.g. loadUrl("javascript:elt.innerHTML='Hello World!';void(0);");
run your code inside a function that doesn't return a value, e.g. loadUrl("(function(){elt.innerHTML='Hello World!';})()");
From API level 19 you should use evaluateJavascript:
public void evaluateJavascript (String script, ValueCallback resultCallback)
Added in API level 19 Asynchronously evaluates JavaScript in the
context of the currently displayed page. If non-null, |resultCallback|
will be invoked with any result returned from that execution. This
method must be called on the UI thread and the callback will be made
on the UI thread.
Please refer to migration guide for WebView in Android 4.4:
http://developer.android.com/guide/webapps/migrating.html

Override current Javascript functions with Android Webview

I'm porting my website to Android, and using an Webview to show the content to user. There are a lot of Javascript functions in my website, and I want to intercept them. I already seen a "solution" here.
However, I think there should be a more proper way, using Javascript Interface:
this.webView.addJavascriptInterface(this.webJavascriptInterface, "Android");
This way, I have to modify my website to call both myFunction() and Android.myFunction(). I tried to leave a blank String in the interface:
this.webView.addJavascriptInterface(this.webJavascriptInterface, "");
but the result was as I guess, it couldn't work. Is there a way to override current Javascript functions in Webview?
// android
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webView.loadUrl("javascript:function inputClick(val){native.abcd(val);}"); // override js function
}
});
webView.loadUrl("file:///android_asset/test.html");
webView.addJavascriptInterface(new Object() {
#JavascriptInterface
public void abcd(int val) {
Log.e(TAG, "#js abcd" + val);
}
}, "native");
<!-- html -->
<input type="button" onclick="inputClick(2)" value="button">
== add some explain at Sep 07,2015
because javascript function could be override, so you can override javascript function while page finished.
if not, normal implement maybe like this:
// js code
function inputClick(val) {
native.abcd(val); // native and abcd defined in WebView method addJavascriptInterface
}
but this normal implement is seems not work in iOS(Object-C), and must edit HTML page.
is just move the java-javascript bridge code from HTML to Java. (see method onPageFinished in example code)
You can do this by injecting a new javascript that redefines old function, basically you can override existing function with this.
For example, to override window.print() function, I use this
webView.loadUrl("javascript:window.print = function() {Android.printPage();}")

How do I pass return values from a javascript function to android?

I want to let my android app call a function written in javascript and expect a return value from that.
I understand that WebView.loadUrl works asynchronously, so what I am doing now is to let javascript notify my android app when it is done and pass in the return value by calling a java function using javascriptinterface.
I wonder if there are better ways of doing this and whether anyone has noticed any message loss between javascript and android.
I just got your problem.
Have a JS function like this.
function androidResponse() {
window.cpjs.sendToAndroid("I am being sent to Android.");
}
Set up Android (Java).
Have a final class like this
final class IJavascriptHandler {
IJavascriptHandler() {
}
// This annotation is required in Jelly Bean and later:
#JavascriptInterface
public void sendToAndroid(String text) {
// this is called from JS with passed value
Toast t = Toast.makeText(getApplicationContext(), text, 2000);
t.show();
}
}
Then on your WebView load have.
webView.addJavascriptInterface(new IJavascriptHandler(), "cpjs");
Call JS function
webView.loadUrl("javascript:androidResponse();void(0)");
UPDATED
Also I had a very bad time experiencing problems while passing hundreds of lines of string to JS from Java and I have subsequent post on StackOverflow with no good answers but finally resolved it knowing problme was of special characters inside string so take of special characters when you use string passing to and fro.
Passing Data From Javascript To Android WebView
HTML String Inside Nested String
HTML TextArea Characters Limit Inside Android WebView

Categories

Resources