Skip to content

LiquidCore as a Raw JavaScript engine for Android (v. 0.7.0 )

Eric Lange edited this page Jan 14, 2020 · 1 revision

JavaScript API

LiquidCore is built on top of Node.js, which is in turn built on V8. So, the V8 API is natively available to any app which includes the LiquidCore library. In addition to directly interacting with V8 (a powerful, but incredibly complex API), LiquidCore provides two additional APIs: a Java Native Interface (JNI) API for Android, and a JavaScriptCore API for iOS and React Native.

Installation

You can follow the instructions for installing LiquidCore, but if you don't plan on using the Node.js features and only want V8, you can follow these instructions instead for a somewhat lighter weight footprint.

Step 1: Make sure your project is configured for use with npm

In the root directory of your project, you must have a package.json file. If you do not already have one, you can create it by running:

$ npm init

and following the steps in the wizard.

Step 2: Install the liquidcore npm package

$ npm i liquidcore

Step 3: Include liquidcore-V8 into your project

In your app's build.gradle, add the following line to the end:

apply from: new File(rootProject.projectDir, 'node_modules/liquidcore/include.V8.gradle')

Java Native Interface (JNI) for Android

See the Javadoc for complete documentation of the API.

To get started, you need to create a JavaScript JSContext. The execution of JS code occurs within this context, and separate contexts are isolated virtual machines which do not interact with each other.

JSContext context = new JSContext();

This context is itself a JavaScript object. And as such, you can get and set its properties. Since this is the global JavaScript object, these properties will be in the top-level context for all subsequent code in the environment.

context.property("a", 5);
JSValue aValue = context.property("a");
double a = aValue.toNumber();
DecimalFormat df = new DecimalFormat(".#");
System.out.println(df.format(a)); // 5.0

You can also run JavaScript code in the context:

context.evaluateScript("a = 10");
JSValue newAValue = context.property("a");
System.out.println(df.format(newAValue.toNumber())); // 10.0
String script =
  "function factorial(x) { var f = 1; for(; x > 1; x--) f *= x; return f; }\n" +
  "var fact_a = factorial(a);\n";
context.evaluateScript(script);
JSValue fact_a = context.property("fact_a");
System.out.println(df.format(fact_a.toNumber())); // 3628800.0

You can also write functions in Java, but expose them to JavaScript:

JSFunction factorial = new JSFunction(context,"factorial") {
    public Integer factorial(Integer x) {
        int factorial = 1;
        for (; x > 1; x--) {
        	   factorial *= x;
        }
        return factorial;
    }
};

This creates a JavaScript function that will call the Java method factorial when called from JavaScript. It can then be passed to the JavaScript VM:

context.property("factorial", factorial);
context.evaluateScript("var f = factorial(10);")
JSValue f = context.property("f");
System.out.println(df.format(f.toNumber())); // 3628800.0

JavaScriptCore API

There are two major open source JavaScript implementations: V8, which is popularized by Google Chrome, and JavaScriptCore, which is part of WebKit, backed by Apple's Safari. LiquidCore uses V8, simply because it is built on Node.js, which is difficult to decouple from V8. However, the JavaScriptCore API has a few advantages: (1) it is far simpler to use than V8, (2) it is a familiar interface to iOS developers as the JavaScriptCore framework has been available since iOS 7, and (3) other very useful projects, like React Native require the library. So, to take advantage of this, LiquidCore provides a JavaScriptCore -> V8 bridge, where projects that require the JavaScriptCore API can use the V8 backend with little or no modification.

To use the JSC->V8 translation layer, you must be using the NDK. In your build.gradle file, replace the include file line with the following:

apply from: new File(rootProject.projectDir, 'node_modules/liquidcore/addon.gradle')

This will extract the header files into build/liquidcore-addon/include and the liquidcore-V8 shared libraries into build/liquidcore-addon/jni.

In your CMakeLists.txt:

include_directories(
  ...
  build/liquidcore-addon/include/JavaScriptCore
)

add_library( js-lib SHARED IMPORTED )
set_target_properties(
  js-lib
  PROPERTIES IMPORTED_LOCATION
  ${PROJECT_SOURCE_DIR}/build/liquidcore-addon/jni/${ANDROID_ABI}/libliquidjs.so
)

And then add js-lib to target_link_libraries. You should now be able to use the JavaScriptCore C API.