概述
现在开发一个电商类的应用,比较流行的做法就是采用混合式开发,即原生应用里面嵌入网页,这种应用称为Hybrid App。这种开发方式优点是成本低,跨平台,周期短,灵活性强。
但是也有一个致命的缺点就是体验差,毕竟是网页,加载过程会比原生慢。但这不妨碍它的流行。今天来记录一下原生Android如何与js交互的要点。其实简单来讲就是两点:
- Android调用js方法。
- js调用Android方法。
Android调用js方法
两种方法:
- 通过WebView的loadUrl()
- 通过WebView的evaluateJavascript()
先新建一个项目,将一个html文件放进新项目的assets中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="utf-8">
<title>JSinAndroid</title>
<script>
function testJS(){
alert("Android调用了JS的callJS方法");
}
function testAndroid(){
test.hello("js调用了android中的hello方法");
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="testAndroid()">调用Android代码</button>
</body>
</html>
Android的布局文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="200dp"
android:text="调用js方法"/>
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
界面显示如下:
上面是一个最简单的Web,定义了一个Button,点击了之后Web调用testAndroid方法,这个后面讲。先看Andoid调用JS方法。
首先看testJS方法,在Android端点击按钮之后调用Web的testJS方法。具体实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54button.setOnClickListener(new View.OnClickListener() {//调用无参
public void onClick(View v) {
if (Build.VERSION.SDK_INT < 18) {//SDK版本,当SDK处于4.4以下的时候采用loadUrl的方式加载网页
mWebView.loadUrl("javascript:testJS()");
} else {//SDK版本,当SDK大于4.4的时候采用evaluateJavascript的方式加载网页
mWebView.evaluateJavascript("javascript:testJS()", new ValueCallback<String>() {
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
}
});
button2.setOnClickListener(new View.OnClickListener() {//传参
public void onClick(View v) {
if(Build.VERSION.SDK_INT < 18){
mWebView.loadUrl("javascript:testJS(4567)");
}else {
mWebView.evaluateJavascript("javascript:testJS(456)", new ValueCallback<String>() {
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
}
});
// 由于设置了弹窗检验调用结果,所以需要支持js对话框
// webview只是载体,内容的渲染需要使用webviewChromClient类去实现
// 通过设置WebChromeClient对象处理JavaScript的对话框
//设置响应js 的Alert()函数
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("来自JS的参数");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
});
显示结果:
加载网页需要注意的是:
- loadUrl效率较低,会使页面重新刷新
- evaluateJavascript效率高,该方法执行时不会使页面刷新,但是只能在Android4.4以后才能使用
日常使用的时候可以安装上述写法,让两者结合起来使用
js调用Android方法
js调用Android方法有两种方式:
- 定义AndroidinJS映射类,添加@JavascriptInterface注解
- 定义协议,复写WebViewClient类的shouldOverrideUrlLoading方法
具体实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40/**
* JS调用Android方法
*/
private void AndroidinJS(){
//方式1:
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript对象名
//参数2:Java对象名
mWebView.addJavascriptInterface(new AndroidinJS(), "test");//AndroidtoJS类对象映射到js的test对象
// 加载JS代码
// 格式规定为:file:///android_asset/文件名.html
mWebView.loadUrl("file:///android_asset/jstest.html");
//方式2:
// 复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
//假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
Uri uri = Uri.parse(url);
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
if ( uri.getScheme().equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri.getAuthority().equals("webview")) {
Set<String> collection = uri.getQueryParameterNames();
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
String k = iterator.next().toString();
Log.e("Tag","-----"+k+" -- "+uri.getQueryParameter(k));
}
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
);
方式1需要定义AndroidinJS类
缺点是存在严重的漏洞问题1
2
3
4
5
6
7
8public class AndroidinJS {
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
public void hello(String msg) {
Log.e("Tag",msg);
}
}
方式2需要添加约定协议
不存在漏洞问题,虽然有点麻烦,不过出于安全问题,推荐方式21
2
3
4function testAndroid1(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
调用结果: