Kotlin Call Javascript function in Android without webView - javascript

Is there any way we can call JS function from Kotlin without having WebView in Android?
Let's say as mentioned below I have one JS function helloJS() in test.js file,
test.js:-
function helloJS(){
return "Hello from JS"
}
And now I want to call this function from Kotlin file like
TestClass.kt:-
class TestHello{
fun getHelloFromJS(){
val name = test.helloJS()
}
}
Till now I am using Webview and loading JS file into that and getting result as call back
But, I read that Kotlin is interoperable with JS like Java
So I am curious to know if there is any way we can use that on Android without having webView

This is not possible straight forward but I found one library Execute JavaScript in Android without WebView for achieve this.
Read that blog carefully and follow below step.
Keep your JavaScript file (test.js) in assets folder in android project.
I have converted that code into Kotlin.
CallJavaScript.jks
import org.mozilla.javascript.Context
import org.mozilla.javascript.Function
import java.io.InputStreamReader
object CallJavaScript {
fun callFunction(mContext: android.content.Context): Any? {
var jsResult: Any? = null
val params = arrayOf<Any>("")
// Every Rhino VM begins with the enter()
// This Context is not Android's Context
val rhino = Context.enter()
// Turn off optimization to make Rhino Android compatible
rhino.optimizationLevel = -1
try {
val scope = rhino.initStandardObjects()
// Note the forth argument is 1, which means the JavaScript source has
// been compressed to only one line using something like YUI
val assetManager = mContext.assets
try {
val input = assetManager.open("test.js")
val targetReader = InputStreamReader(input)
rhino.evaluateReader(scope, targetReader, "JavaScript", 1, null)
} catch (e: Exception) {
e.printStackTrace()
}
// Get the functionName defined in JavaScriptCode
val obj = scope.get("helloJS", scope)
if (obj is Function) {
// Call the function with params
jsResult = obj.call(rhino, scope, scope, params)
// Parse the jsResult object to a String
val result = Context.toString(jsResult)
}
} finally {
Context.exit()
}
return jsResult
}
}
Add this line to build.gradle:
implementation 'org.mozilla:rhino:1.7R4'
In your assets folder, create a file called test.js:
function helloJS()
{
return "Hello from JS";
}
Now simply call above function from Activity.
Log.e("JS : ", CallJavaScript.callFunction(this).toString());
Output :
E/JS :: Hello from JS

It's not possible. You must not confuse the language with the platform.
Kotlin is interoperable with JS like Java
means Kotlin/JS can use and be used in a Javascript platform (Node.js or browsers). The Kotlin code compiled (transpiled) into js is able to call other js files. And external Js code can call the js code build from Kotlin. This is the interoperability with JS.
There is no interoperability between Kotlin/JS and Kotlin/JVM.

Kt looks like JS, but it is not. It will be compiled for Android runtime, not for the java script engine.
JS code require a JS runtime, but it is not in Android Runtime.
i.e. you cannot run JS directly in Java / Kt code for Android.

Am not a pro in Kotlin but Java is a pie for me. Anything you can implement in Java can be implemented in Kotlin and To execute Javascript code, I use rhino which does the job pretty easier than using the webview
Implement it:
try {
Scriptable scope = rhino.initStandardObjects();
rhino.evaluateString(scope, javaScriptCode, "JavaScript", 1, null);
Object obj = scope.get(functionNameInJavaScriptCode, scope);
if (obj instanceof Function) {
Function jsFunction = (Function) obj;
// Call the function with params
Object jsResult = jsFunction.call(rhino, scope, scope, params);
// Parse the jsResult object to a String
String result = Context.toString(jsResult);
}
}finally {
Context.exit();
}

Related

Is it possible to only convert a dart function to javascript

I am currently using the following package where the readme illustrates the following
final bool loaded = await JsIsolatedWorker().importScripts(['test.js']);
I am using the isolate worker package so my code can work on web and outside of web. I would like to generate javascript code from my dart code. I have one file with a top level function and I use
dart compile js -O0 -o test.js test.dart
which I found here
https://dart.dev/tools/dart2js
this is my dart file
void main(List<String> args) {
doComputation('');
}
String doComputation(String input) {
return 'output';
}
I can generate javascript only if I have a main function but this generates a javascript file where the doComutation is not a top level function, so I am not sure if the package can call the function. It looks like it generates an entire program instead of just generating one function.
The generated file is too long to post
So what my question comes down to is this. Is there a way to generate javascript from dart for 1 function with its dependencies included instead of having to generate the entire program? So that I can call this function from dart.
I am not an expert but I also had this problem. Here's what worked for me:
In web_worker.dart:
import 'package:js/js.dart';
main(){
allowInterop(doComputation);
}
#JS('doComputation')
String doComputation(String computationInput) {
// Replace this with your actual computation.
final String computationOutput = "output";
return computationOutput;
}
Compile it using:
$ dart compile js web_worker.dart -o webWorker.js
Manually edit webWorker.js, the JS file generated by the compiler:
Delete this line at the top of the file:
(function dartProgram() {
and the line at the bottom of the file with the corresponding closing brace:
})();
I don't understand what's going on here but I found the Javascript version of the function doComputation() defined in webWorker.js as a property of the object "A".
I defined a wrapper at the top of the file like this:
function doComputation(computationInput) {
return A.doComputation(computationInput)
}
and then I was able to use the file with JsIsolatedWorker like this:
final bool loaded =
await JsIsolatedWorker().importScripts(['../web/webWorker.js']);
if (loaded) {
final String computationResult = await JsIsolatedWorker()
.run(functionName: 'doComputation', arguments: computationInput);
} else {
debugPrint('Web worker is not available');
}
If someone who understands this better can elaborate or improve on this solution, that would be great. I don't really have any idea what I'm doing. I'm just posting this to hopefully help other people save time in the future by sharing what worked for me.
You can use js package to call JavaScript APIs from Dart code, or vice versa. To make a Dart function callable from JavaScript by name, use a setter annotated with #JS().
#JS()
library callable_function;
import 'package:js/js.dart';
/// Allows assigning a function to be callable from `window.functionName()`
#JS('functionName')
external set _functionName(void Function() f);
/// Allows calling the assigned function from Dart as well.
#JS()
external void functionName();
void _someDartFunction() {
print('Hello from Dart!');
}
void main() {
_functionName = allowInterop(_someDartFunction);
}
JavaScript code may now call functionName() or window.functionName().
Check google chartjs https://github.com/google/chartjs.dart/tree/master/example for a complete example.
From dart2js "Helping dart2js generate better code" there is a tip :
Don’t worry about the size of your app’s included libraries. The dart2js compiler performs tree shaking to omit unused classes, functions, methods, and so on. Just import the libraries you need, and let dart2js get rid of what you don’t need.
Related to this post https://stackoverflow.com/a/21124252/3733730.
You can do this instead by using the package dart2js
one make a program for your main.dart dart2js where the function or command you wanted to make is there, then create a separate dart file what function you wanted to call, and it will fix your problem, it is a separate file but will only execute one function that you needed to do so and that is the separate file

WebAssembly calling JavaScript methods from wasm i.e. within C++ code

I was playing with WebAssembly and so far and I was able to manage emscripten compile my test C++ project to wasm file
em++ provides me 2 files i.e.
mainTest.js mainTest.wasm
When I load mainTest.js in my html page then I get a JavaScript object called
"Module".
I did found how to call C++/wasm methods from javascript i.e. something like:
var myTestInteger = Module._callMyTestMethod();
and read strings from the
Module.wasmMemory.buffer
, but I do NOT understand how to call JavaScript from C++ code.
i.e. I would like to be able to do something like that:
#ifdef __cplusplus
extern "C" {
#endif
extern void testExternJSMethod();
int main()
{
cout << " Hello From my Test1 !" << endl;
testExternJSMethod();
return 0;
}
int EMSCRIPTEN_KEEPALIVE callMyTestMethod(){
return 26;
}
#ifdef __cplusplus
}
#endif
and the my js method testExternMethod that I am loading in another js file called utils.js
function testExternMethod() {
console.log("Hello from testExternMethod!" + )
}
Here I would like to call the JavaScript testExternJSMethod from C++.
When I run the page in Firefox get "-1" in the debugger console.
So what I am missing in this case? Unfortunately The Mozilla documentation is giving only examples in those S-expressions instead of C++.
What am I missing in example? In C++ I have defined the method with the extern keyword i.e.
extern void testExternJSMethod();
but I get the feeling that that is not all I have to do.
I believe that I should somehow link that JavaScript method to the Module somehow but I do not know how.
Module.asm gives me the exports. Which method call should give me the imports? since I believe that this _testExternJSMethod() should be in some imports method I can not figure out how to get to it.
I'm not exactly sure of your use case, but you are missing important steps to be able to use your function testExternalJSMethod. You have two options here:
Option 1 - Library
1 - Define your function in c/c++ .
extern void testExternalJSMethod();
2 - Create a file called myLibrary.js
3 - The JS function needs to be added to the LibraryManager in your library file with the following code:
function makeAlert(text) {
alert(text);
}
if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
testExternalJSMethod: function() {
makeAlert("Hello world");
}
});
4 - If testExternalJSMethod depends on anything outside of its own scope (for example, makeAlert above), make sure to include the script in your html page
<script async type="text/javascript" src="myLibrary.js"></script>
5 - Add option --js-library to your emcc command, and immediately after the relative path to myLibrary.js
emcc ... --js-library myLibrary.js
Option 2 - Passing Pointers
1 - Define your javascript function type in c/c++
typedef void testExternalJSMethod()
2 - Wherever you want this function to be used, accept an int param which will be the function pointer, and cast the pointer to your function
void passFnPointer(int ptr) {
((testExternalJSMethod*)ptr)();
}
3 - Use emscripten's addFunction() and store its returned value (the pointer in c/c++)
var fnPtr = Module.addFunction(function () {
alert("You called testExternalJSMethod");
});
4 - Use the stored pointer value from step 3 to pass to our function passFnPointer
var passFnPointer = Module.cwrap('passFnPointer', 'undefined', ['number']);
passFnPointer(fnPtr);
5 - Add option -s RESERVED_FUNCTION_POINTERS to your emcc command
emcc ... -s RESERVED_FUNCTION_POINTERS=10
Have you tried looking at the Emscripten documentation? It has a whole section on interacting with code that details how to expose C / C++ functions to JavaScript and call JavaScript functions from C / C++.
Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using emscripten_run_script() or writing “inline JavaScript”.
It is worth noting that the Mozilla documentation details plain WebAssembly, whereas Emscripten adds a lot more framework and tooling around WebAssembly in order to make it easier to port large C / C++ codebases.

Is it possible to import javascript files to a java ScriptEngine

I am using nashorn java ScriptEngine. I would like to evaluate a script which includes other scripts. I know I can use the load directive directly in the javascript itself, but I would prefer to import or load it directly from the java code instanciating the scriptEngine.
Is there a way to do this ? Something like :
void evaluateScript(String scriptName, String dependency) {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine jsEngine = factory.getEngineByName("nashorn");
jsEngine.load(depency); // does not exist.
jsEngine.eval();
}
I see the "load" function does not exist. How could I achieve this?
Thanks
Actually I found the answer myself: as mentioned in the comment, it is possible to call several eval with different scripts, same engine, and the engine will keep the evaluated scripts in its context. So here is my code:
public void executeScript(String scriptName, String[] dependencies) {
try {
FileReader script = new FileReader(scriptName);
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine jsEngine = factory.getEngineByName("nashorn");
if(dependencies != null) {
for (String dependency : dependencies) {
FileReader dependencyFile = new FileReader(dependency);
jsEngine.eval(dependencyFile);
}
}
jsEngine.eval(script);
}
}
I can define functions in my dependencies and use them in the script of name scriptName.
javax.script.ScriptEngine has many "eval" methods - there are java.io.Reader accepting eval methods like this -> eval
You can pass a java.io.FileReader or a jdk.nashorn.api.scripting.URLReader to load script from a file or a URL.

Notifying javascript from a node.js native addon (c++)

I do not have much knowledge about javascript. I have written in C++ a shared library that does certain things in a daemon thread. I needed this to be invoked from javascript. By using SWIG I've successfully able to generate a wrapper and compile my code along with it into .node module using node-gyp (wrote binding.gyp for it too). Now i can drop to node prompt and do something like:
> var a = require("./module_name")
> a.SomeCppFunction("SomeString")
and wonderfully invoke the cpp functions, start a detached thread there and return the control back to javascript. However I want to notify the javascript from the detached cpp thread about stuffs. I tried registering javascript functions by collecting function() {} signature types in void(*fp)() etc., to call them back later from c++, but that didn't work. Is there anyway to be able to achieve this ie., register javascript functions (or something else) as callback in the cpp code ?
You can use a combination of SWIG and Napi. An existing repo which does this is available here, with a blog here. But I'll sum up the process here.
Create your class to use in SWIG, which has a thread running in the threadMain method :
#include <Thread.H>
class Test : public ThreadedMethod {
void *threadMain(void);
public:
Test();
void setFnPointer(const char* s);
};
Now in the Napi code, you will generate your thread safe function like so :
Napi::ThreadSafeFunction tsfn; ///< The node api's threadsafe function
Napi::Value Start( const Napi::CallbackInfo& info ){
Napi::Env env = info.Env();
// Create a ThreadSafeFunction
tsfn = Napi::ThreadSafeFunction::New(env,
info[0].As<Napi::Function>(), // JavaScript function to call
"Resource Name", 0,1);
// return the tsfn as a pointer in a string
char addr[24];
sprintf(addr,"%p",&tsfn);
return Napi::String::New(env, addr);
}
// some small code to call NODE_API_MODULE here, check the file NapiCode.C in the repo
You compile the SWIG code to one module and the Napi code down to a different module and you pass the thread safe funciton pointer from one to the other like so :
var libNapiNodejs = require('../swig/.libs/libNapiNodejs');
let fp = libNapiNodejs.start(function () {
console.log("JavaScript callback called with arguments", Array.from(arguments));
}, 5);
// SWIG get our C++ and thread running
var libSwigCNodejs = require('../swig/.libs/libSwigCNodejs');
let test = new libSwigCNodejs.Test;
test.setFnPointer(fp); // tell swig the callback function pointer to execute
test.run(); // run the C++ thread in the SWIG module
You will see that the C++ thread calls the javascript function. This is what the C++ thread looks like in SWIG :
Napi::ThreadSafeFunction *tsfn; ///< The node api's threadsafe function
void *Test::threadMain(void){
printf("C++ Thread enter %s\n",__func__);
auto callback = []( Napi::Env env, Napi::Function jsCallback, int* value ) {
jsCallback.Call( {Napi::Number::New( env, *value )} );
};
for (int i=0; i<10; i++){
sleep(1);
if (*tsfn) {
printf("calling tsfn->BlockingCall\n");
napi_status status = tsfn->BlockingCall( &i, callback );
if ( status != napi_ok ) // Handle error
break;
}
}
tsfn->Release();
printf("C++ Thread exit %s\n",__func__);
return NULL;
}

Equivalent of Ruby's "require" in Javascript

How do you "require" another file into the existing file in Javascript? Is there anything similar to Ruby's "require" or "load"?
> Note: I'm using JS in server (Rhino)
Reason: I just need to access the methods in the other JS files.
Update: This works only when executing it from cmd line. When I try to call it programatically it fails. Here's my code: http://pastie.org/1240495
To use the load function in js embedded from Java, you must first expose it in on the scripting context. There's probably a way to do it from Java, but you can do it using js as well.
Disclaimer: this solution uses source code taken from an Apache-licensed project I have been working on. You can see the original source file here.
This js file sets up your global variables, and lives in a file named setupglobals.js:
var shell = org.mozilla.javascript.tools.shell.Main;
var args = ["-e","var a='STRING';"];
shell.exec(args);
var shellGlobal = shell.global;
//grab functions from shell global and place in current global
load=shellGlobal.load;
print=shellGlobal.print;
defineClass=shellGlobal.defineClass;
deserialize=shellGlobal.deserialize;
doctest=shellGlobal.doctest;
gc=shellGlobal.gc;
help=shellGlobal.help;
loadClass=shellGlobal.loadClass;
quit=shellGlobal.quit;
readFile=shellGlobal.readFile;
readUrl=shellGlobal.readUrl;
runCommand=shellGlobal.runCommand;
seal=shellGlobal.seal;
serialize=shellGlobal.serialize;
spawn=shellGlobal.spawn;
sync=shellGlobal.sync;
toint32=shellGlobal.toint32;
version=shellGlobal.version;
environment=shellGlobal.environment;
Here is your original Java host file, now augmented to evaluate setupglobals.js before any other scripts:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import org.mozilla.javascript.*;
public class RhinoRunner {
public static void main(String args[]) throws IOException
{
BufferedReader script = new BufferedReader(new FileReader("setupglobals.js"));
BufferedReader script2 = new BufferedReader(new FileReader("example.js"));
Context context = Context.enter();
try {
ScriptableObject scope = context.initStandardObjects();
context.evaluateReader(scope, script, "script", 1, null);
context.evaluateReader(scope, script2, "script2", 1, null);
Function fct = (Function)scope.get("abc", scope);
Object result = fct.call(context, scope, scope, new Object[] {2, 3});
System.out.println(Context.jsToJava(result, int.class));
}
finally
{
Context.exit();
}
}
}
Here is your example.js, now augmented to use the global load function to load the file hello.js:
function abc(x,y)
{
return x+y
}
load("hello.js")
And finally, here is hello.js:
print("hello world!")
When executed, RhinoRunner prints the following:
hello world!
5
In Rhino shell, you can should be able to use load(), which is a predefined global method:
load([filename, ...])
Load JavaScript source files named by string arguments. If multiple arguments are given, each file is read in and executed in turn.

Categories

Resources