WebView 和JS交互 addJavascriptInterface evaluateJavascript

本文详细介绍了在Android应用中如何实现Java与JavaScript的交互,包括使用addJavascriptInterface方法注入Java对象到WebView,使得JS能够调用Java方法。此外,还提供了具体的代码案例,展示了如何在JS中调用Java方法及如何从JS获取返回值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注意,如果在 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. 
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, 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。
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
<!DOCTYPE html>
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
    @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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值