注意,如果在 java 与 Javascript 交互的时候出现如下错误:
Uncaught ReferenceError:getDeviceID
1
1
1
Uncaught ReferenceError: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.
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
@JavascriptInterface
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.
evaluateJavascript
void evaluateJavascript (String script, ValueCallbackresultCallback)
1
1
1
void evaluateJavascript (String script, ValueCallbackresultCallback)
参数:
- 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。
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代码
Title
36
1
2
3
4
5
Title
6
7
8
9
10
11
12
13
14
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
36
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
@JavascriptInterface
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
@JavascriptInterface
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