java与javascript

1.映射Java对象到JavaScript对象上

前言

MainActivity.java

本文中所有的代码使用 JavaScript 编写,但你也可以用其他兼容 JSR 223 的脚本语言。这些例子可作为脚本文件也可以在交互式 Shell 中一次运行一个语句的方式来运行。在 JavaScript 中访问对象的属性和方法的语法与 Java 语言相同。

package com.example.jsdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity {
    private WebView wView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        wView = (WebView) findViewById(R.id.wView);
        wView.loadUrl("file:///android_asset/demo1.html");
        WebSettings webSettings = wView.getSettings();
        //①设置WebView允许调用js
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDefaultTextEncodingName("UTF-8");
        //②设置支持js调用java
        wView.addJavascriptInterface(new AndroidAndJSInterface(),"Android"");
    }

 class AndroidAndJSInterface{
        @JavascriptInterface
        public void showToast(){
            Toast.makeText(MainActivity.this, "我被js调用了", Toast.LENGTH_SHORT).show();
        }
    }
}

本文包含如下几部分:

注意:解决该WebView.addJavascriptInterface接口不起作用的两种办法

1、访问 Java 类

①针对版本改成16

为了在 JavaScript 中访问原生类型或者引用 Java 类型,可以调用 Java.type()函数,该函数根据传入的完整类名返回对应对象的类型。下面代码显示如何获取不同的对象类型:

②在JavaScript接口类的方法加上@JavascriptInterface注解

?

2.JavaScript调用Java对象示例

1
2
3
4
var ArrayList = Java.type("java.util.ArrayList");
var intType = Java.type("int");
var StringArrayType = Java.type("java.lang.String[]");
var int2DArrayType = Java.type("int[][]");

demo1.html

在 JavaScript 中使用 Java.type() 函数返回的类型对象的方法跟在 Java 的类似。

<input type="button" value="点击Android被调用" onclick="window.Android.showToast()" />

例如你可以使用如下方法来实例化一个类:

?

1
var anArrayList = new Java.type("java.util.ArrayList");

Java 类型对象可用来实例化 Java 对象。下面的代码显示如何使用默认的构造函数实例化一个新对象以及调用包含参数的构造函数:

?

1
2
3
var ArrayList = Java.type("java.util.ArrayList");
var defaultSizeArrayList = new ArrayList;
var customSizeArrayList = new ArrayList(16);

你可以使用 Java.type() 方法来获取对象类型,可以使用如下方法来访问静态属性以及方法:

?

1
2
var File = Java.type("java.io.File");
File.createTempFile("nashorn", ".tmp");

如果要访问内部静态类,可以传递美元符号 $ 给 Java.type()方法。

下面代码显示如何返回java.awt.geom.Arc2DFloat内部类:

?

1
var Float = Java.type("java.awt.geom.Arc2D$Float");

如果你已经有一个外部类类型对象,那么你可以像访问属性一样访问其内部类,如下所示:

?

1
2
var Arc2D = Java.type("java.awt.geom.Arc2D")
var Float = Arc2D.Float

由于是非静态内部类,必须传递的是外部类实例作为参数给构造函数。

虽然在 JavaScript 中使用类型对象跟在 Java 中类似,但其与 java.lang.Class对象还是有些区别的,这个区别就是 getClass()方法的返回值。你可以使用 classstatic属性来获取这个信息。

下面代码显示二者的区别:

?

1
2
3
4
5
6
7
8
9
var ArrayList = Java.type("java.util.ArrayList");
var a = new ArrayList;
 
// All of the following are true:
print("Type acts as target of instanceof: " + (a instanceof ArrayList));
print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()));
print("Type is not the same as instance's getClass(): " + (a.getClass() !== ArrayList));
print("Type's `class` property is the same as instance's getClass(): " + (a.getClass() === ArrayList.class));
print("Type is the same as the `static` property of the instance's getClass(): " + (a.getClass().static === ArrayList));

在语法和语义上,JavaScript 在编译时类表达式和运行时对象都和 Java 语义类似。不过在 Java 中 Class 对象是没有名为 static 这样的属性,因为编译时的类表达式不作为对象。

2、导入 Java 包和类

为了根据其简单的名称来访问 Java 类,我们可以使用importPackage()importClass()函数来导入 Java 的包和类。这些函数存在于兼容性脚本文件 (mozilla_compat.js) 中。

下面例子展示如何使用importPackage()importClass()函数:

?

1
2
3
4
5
6
7
8
9
10
11
12
// Load compatibility script
load("nashorn:mozilla_compat.js");
// Import the java.awt package
importPackage(java.awt);
// Import the java.awt.Frame class
importClass(java.awt.Frame);
// Create a new Frame object
var frame = new java.awt.Frame("hello");
// Call the setVisible() method
frame.setVisible(true);
// Access a JavaBean property
print(frame.title);

可以通过 Packages 全局变量来访问 Java 包,例如Packages.java.util.Vector 或者 Packages.javax.swing.JFrame。但标准的 Java SE 包有更简单的访问方式,如: java 对应 Packages.java, javax 对应 Packages.javax, 以及 org 对应 Packages.org。

java.lang 包默认不需要导入,因为这会和 ObjectBooleanMath等其他 JavaScript 内建的对象在命名上冲突。此外,导入任何 Java 包和类也可能导致 JavaScript 全局作用域下的变量名冲突。为了避免冲突,我们定义了一个 JavaImporter 对象,并通过 with语句来限制导入的 Java 包和类的作用域,如下列代码所示:

?

1
2
3
4
5
6
7
8
9
// Create a JavaImporter object with specified packages and classes to import
var Gui = new JavaImporter(java.awt, javax.swing);
 
// Pass the JavaImporter object to the "with" statement and access the classes
// from the imported packages by their simple names within the statement's body
with (Gui) {
 var awtframe = new Frame("AWT Frame");
 var jframe = new JFrame("Swing JFrame");
};

3、使用 Java 数组

为了创建 Java 数组对象,首先需要获取 Java 数组类型对象并进行初始化。JavaScript 访问数组元素的语法以及 length属性都跟 Java 一样,如下列代码所示:

?

1
2
3
4
5
6
7
8
9
var StringArray = Java.type("java.lang.String[]");
var a = new StringArray(5);
 
// Set the value of the first element
a[0] = "Scripting is great!";
// Print the length of the array
print(a.length);
// Print the value of the first element
print(a[0]);

给定一个 JavaScript 数组 我们还可以用 Java.to()方法将它转成 Java 数组。我们需要将 JavaScript 数组作为参数传给该方法,并指定要返回的数组类型,可以是一个字符串,或者是类型对象。我们也可以忽略类型对象参数来返回 Object[] 数组。转换操作是根据 ECMAScript 转换规则进行的。下面代码展示如何通过不同的 Java.to()的参数将 JavaScript 数组变成 Java 数组:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建一个 JavaScript 数组
var anArray = [1, "13", false];
 
// 将数组转换成 java 的 int[] 数组
var javaIntArray = Java.to(anArray, "int[]");
print(javaIntArray[0]); // prints the number 1
print(javaIntArray[1]); // prints the number 13
print(javaIntArray[2]); // prints the number 0
 
// 将 JavaScript 数组转换成 Java 的 String[] 数组
var javaStringArray = Java.to(anArray, Java.type("java.lang.String[]"));
print(javaStringArray[0]); // prints the string "1"
print(javaStringArray[1]); // prints the string "13"
print(javaStringArray[2]); // prints the string "false"
 
// 将 JavaScript 数组转换成 Java 的 Object[] 数组
var javaObjectArray = Java.to(anArray);
print(javaObjectArray[0]); // prints the number 1
print(javaObjectArray[1]); // prints the string "13"
print(javaObjectArray[2]); // prints the boolean value "false"

你可以使用 Java.from()方法来将一个 Java 数组转成 JavaScript 数组。

下面代码演示如何将一个包含当前目录下文件列表的数组转成 JavaScript 数组:

?

1
2
3
4
5
6
7
8
// Get the Java File type object
var File = Java.type("java.io.File");
// Create a Java array of File objects
var listCurDir = new File(".").listFiles();
// Convert the Java array to a JavaScript array
var jsList = Java.from(listCurDir);
// Print the JavaScript array
print(jsList);

注意:

大多数情况下,你可以在脚本中使用 Java 对象而无需转换成 JavaScript 对象。

4、实现 Java 接口

在 JavaScript 实现 Java 接口的语法与在 Java 总定义匿名类的方法类似。我们只需要实例化接口并用 JavaScript 函数实现其方法即可。

下面代码演示如何实现 Runnable接口:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
// Create an object that implements the Runnable interface by implementing
// the run() method as a JavaScript function
var r = new java.lang.Runnable() {
 run: function() {
  print("running...n");
 }
};
 
// The r variable can be passed to Java methods that expect an object implementing
// the java.lang.Runnable interface
var th = new java.lang.Thread(r);
th.start();
th.join();

如果一个方法希望一个对象,这个对象实现了只有一个方法的接口,你可以传递一个脚本函数给这个方法,而不是传递对象。例如,在上面的例子中 Thread()构造函数要求一个实现了 Runnable接口的对象作为参数。我们可以利用自动转换的优势传递一个脚本函数给 Thread()构造器。

下面的例子展示如何创建一个 Thread对象而无需实现 Runnable接口:

?

1
2
3
4
5
6
7
8
9
10
// Define a JavaScript function
function func() {
 print("I am func!");
};
 
// Pass the JavaScript function instead of an object that implements
// the java.lang.Runnable interface
var th = new java.lang.Thread(func);
th.start();
th.join();

你可以通过传递相关类型对象给 Java.extend()函数来实现多个接口。

5、扩展抽象 Java 类

你可以实例化一个匿名的抽象类的子类,只需要给构造函数传递一个 JavaScript 对象,对象中包含了一些属性对应了抽象类方法实现的值。如果一个方法是重载的,JavaScript 函数将会提供所有方法变种的实现。下面例子显示如何初始化抽象类 TimerTask 的子类:

?

1
2
var TimerTask = Java.type("java.util.TimerTask");
var task = new TimerTask({ run: function() { print("Hello World!") } });

本文由金沙官网线上发布于Web前端,转载请注明出处:java与javascript

您可能还会对下面的文章感兴趣: