注意,如果在 java 与 Javascript 交互的时候出现如下错误:
Uncaught ReferenceError: <pre name="code" class="html">getDeviceID
1
1
1
Uncaught ReferenceError: <pre name="code" class="html">getDeviceID
很可能是因为在 html 页面还没有加载完,就加载里面的 JS 方法,这样是找不到这个方法的。
解决方法为:把调用放到 onPageFinished() 里面。
addJavascriptInterface
void addJavascriptInterface(Object object, String name)
1
1
1
void addJavascriptInterface(Object object, String name)
参数
- Object object : the Java object to inject注入 into this WebView's JavaScript context. Null values are ignored.
- String name : the name used to expose暴露 the object in JavaScript
Injects
the supplied Java object into this WebView.
The object is injected into the JavaScript context of the main frame , using the supplied name.
The object is injected into the JavaScript context of the main frame , using the supplied name.
This allows the Java object's methods to be
accessed
from JavaScript.
For applications targeted to API level
JELLY_BEAN_MR1
and above, only public methods that are
annotated with注释
JavascriptInterface
can be accessed from JavaScript.
For applications targeted to API level JELLY_BEAN or below, all public methods (including the inherited
继承的
ones) can be accessed, see the important security note
安全注意事项
below for implications意义、含义.
Note that injected objects will not appear in JavaScript until the page is next (re)loaded. JavaScript should be enabled before injecting the object. For example:
class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}
webview.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
x
1
class JsObject {
2
3
public String toString() { return "injectedObject"; }
4
}
5
webview.getSettings().setJavaScriptEnabled(true);
6
webView.addJavascriptInterface(new JsObject(), "injectedObject");
7
webView.loadData("", "text/html", null);
8
webView.loadUrl("javascript:alert(injectedObject.toString())");
IMPORTANT:
This method can be used to allow JavaScript to control the host application.
This is a powerful feature
功能
, but also presents a security risk
风险
for apps targeting目标版本为
JELLY_BEAN
or earlier.
Apps that target a version later than
JELLY_BEAN
are still vulnerable
很容易受到攻击
if the app runs on a device running Android earlier than
4.2
.
The most secure way to use this method is to target JELLY_BEAN_MR1 and to ensure the method is called only when running on Android 4.2 or later.
With these older versions, JavaScript could use
reflection
to access an injected object's public fields.
Use of this method in a WebView containing untrusted content could allow an attacker
攻击者
to manipulate
操纵
the host application in unintended
非预期
ways, executing
执行
Java code with the permissions of the host application.
Use extreme care
格外小心
when using this method in a WebView which could contain untrusted content.
JavaScript
interacts with
交互
Java object on a private, background thread of this WebView. Care is therefore required to maintain
保持
thread safety.
The Java object's fields are not accessible.
For applications targeted to API level LOLLIPOP and above, methods of injected Java objects are enumerable 枚举 from JavaScript.
The Java object's fields are not accessible.
For applications targeted to API level LOLLIPOP and above, methods of injected Java objects are enumerable 枚举 from JavaScript.
evaluateJavascript
void evaluateJavascript (String script, ValueCallback<String> resultCallback)
1
1
1
void evaluateJavascript (String script, ValueCallback<String> resultCallback)
参数:
- String script : the JavaScript to execute.要执行的JavaScript。
- ValueCallback resultCallback : A callback to be invoked when the script execution completes with the result of the execution (if any). May be null if no notification of the result is required.
当脚本执行完成时的回调,如果有执行结果的话会携带执行结果。如果不需要结果通知,可以为null。
Asynchronously evaluates JavaScript in the context of the currently displayed page.
在当前显示页面的上下文中异步执行JavaScript。
在当前显示页面的上下文中异步执行JavaScript。
If non-null, resultCallback will be invoked with any result returned from that execution.
如果非空,resultCallback会被回调并携带调用后返回的结果。
This method must be called on the UI thread and the callback will be made on the UI thread.
此方法必须在UI线程上调用,并且回调也是运行在UI线程上的。
Compatibility note.
兼容性说明
Applications targeting N or later, JavaScript state from an empty WebView is no longer persisted across navigations like loadUrl(String).
目标版本为N或更高的应用程序,空的WebView中的JavaScript状态不再继续像loadUrl那样在navigations中保留。
For example, global variables and functions defined before calling loadUrl(String) will not exist in the loaded page.
例如,在加载后的页面中将不会存在调用loadUrl之前定义的全局变量和函数。
Applications should use addJavascriptInterface(Object, String) instead to persist JavaScript objects across navigations.
应用程序应该使用 addJavascriptInterface 来跨navigations维护JavaScript对象。
原生和js交互案例
1、注册
webview.addJavascriptInterface(new JSInterface(this, webview), JSInterface.JS_INTERFACE_NAME);
x
1
webview.addJavascriptInterface(new JSInterface(this, webview), JSInterface.JS_INTERFACE_NAME);
2、JS和HTML代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button onclick='jscalljava("包青天")'>点击后调用 JS 的 jscalljava 方法,在此方法中 JS 调用原生 JSInterface 类中的 hello 方法,原生 hello 方法执行完操作后回调 JS 中的 javacalljs 方法</button>
<button onclick='jscalljava2()'>演示原生调用 JS 方法后,JS 将执行完return的结果通过原生提供的 ValueCallback 对象的 onReceiveValue 方法回调给原生</button>
</body>
<script type="text/javascript">
function jscalljava(content){
console.log("【jscalljava】");
JSInterface.hello(content);
}
function jscalljava2(){
console.log("【jscalljava2】");
var content="[\"包青天\",\"白乾涛\",\"bqt\"]";
JSInterface.hello2(content);
}
function javacalljs(content){
console.log("【js中的javacalljs方法被调用了】content="+content);
}
function javacalljs2(content){
console.log("【js中的javacalljs2方法被调用了】content="+content);
return 20094;
}
</script>
</html>
36
1
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8">
5
<title>Title</title>
6
</head>
7
<body>
8
9
<button onclick='jscalljava("包青天")'>点击后调用 JS 的 jscalljava 方法,在此方法中 JS 调用原生 JSInterface 类中的 hello 方法,原生 hello 方法执行完操作后回调 JS 中的 javacalljs 方法</button>
10
<button onclick='jscalljava2()'>演示原生调用 JS 方法后,JS 将执行完return的结果通过原生提供的 ValueCallback 对象的 onReceiveValue 方法回调给原生</button>
11
12
</body>
13
14
<script type="text/javascript">
15
function jscalljava(content){
16
console.log("【jscalljava】");
17
JSInterface.hello(content);
18
}
19
20
function jscalljava2(){
21
console.log("【jscalljava2】");
22
var content="[\"包青天\",\"白乾涛\",\"bqt\"]";
23
JSInterface.hello2(content);
24
}
25
26
function javacalljs(content){
27
console.log("【js中的javacalljs方法被调用了】content="+content);
28
}
29
30
function javacalljs2(content){
31
console.log("【js中的javacalljs2方法被调用了】content="+content);
32
return 20094;
33
}
34
35
</script>
36
</html>
3、原生代码
public class JSInterface {
public static final String JS_INTERFACE_NAME = "JSInterface";//JS调用类名
private Context mContext;
private WebView webView;
public JSInterface(Context context, WebView webView) {
this.mContext = context;
this.webView = webView;
}
@JavascriptInterface
public void hello(String content) {
Log.i("bqt", "JS 调用原生时是否发生在主线程:" + (Looper.myLooper() == Looper.getMainLooper()));//false
new Handler(Looper.getMainLooper()).post(() -> //WebView等UI操作必须在主线程中进行
Toast.makeText(mContext, "原生的hello方法被调用了:" + content, Toast.LENGTH_SHORT).show());
SystemClock.sleep(3000);//模拟耗时操作
String call = "javascript:javacalljs(" + System.currentTimeMillis() + ")";//格式很重要,大部分错误都是由于格式问题导致的
new Handler(Looper.getMainLooper()).post(() -> webView.loadUrl(call));//WebView等UI操作必须在主线程中进行
}
@JavascriptInterface
public void hello2(String content) {
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(mContext, content, Toast.LENGTH_SHORT).show());
SystemClock.sleep(3000);//模拟耗时操作
String call = "javascript:javacalljs2(" + System.currentTimeMillis() + ")";//JS此方法的返回值会通过onReceiveValue回调到原生
new Handler(Looper.getMainLooper()).post(() -> webView.evaluateJavascript(call, value -> {
Log.i("bqt", "ValueCallback 是否发生在主线程:" + (Looper.myLooper() == Looper.getMainLooper()));//true
Toast.makeText(mContext, "【onReceiveValue】" + value, Toast.LENGTH_SHORT).show();
}));
}
}
35
1
public class JSInterface {
2
public static final String JS_INTERFACE_NAME = "JSInterface";//JS调用类名
3
private Context mContext;
4
private WebView webView;
5
6
public JSInterface(Context context, WebView webView) {
7
this.mContext = context;
8
this.webView = webView;
9
}
10
11
12
public void hello(String content) {
13
Log.i("bqt", "JS 调用原生时是否发生在主线程:" + (Looper.myLooper() == Looper.getMainLooper()));//false
14
new Handler(Looper.getMainLooper()).post(() -> //WebView等UI操作必须在主线程中进行
15
Toast.makeText(mContext, "原生的hello方法被调用了:" + content, Toast.LENGTH_SHORT).show());
16
17
SystemClock.sleep(3000);//模拟耗时操作
18
19
String call = "javascript:javacalljs(" + System.currentTimeMillis() + ")";//格式很重要,大部分错误都是由于格式问题导致的
20
new Handler(Looper.getMainLooper()).post(() -> webView.loadUrl(call));//WebView等UI操作必须在主线程中进行
21
}
22
23
24
public void hello2(String content) {
25
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(mContext, content, Toast.LENGTH_SHORT).show());
26
27
SystemClock.sleep(3000);//模拟耗时操作
28
29
String call = "javascript:javacalljs2(" + System.currentTimeMillis() + ")";//JS此方法的返回值会通过onReceiveValue回调到原生
30
new Handler(Looper.getMainLooper()).post(() -> webView.evaluateJavascript(call, value -> {
31
Log.i("bqt", "ValueCallback 是否发生在主线程:" + (Looper.myLooper() == Looper.getMainLooper()));//true
32
Toast.makeText(mContext, "【onReceiveValue】" + value, Toast.LENGTH_SHORT).show();
33
}));
34
}
35
}
2017-8-25