目录
image.png原理
image.png如上图所示,web页面与Native的交互主要是通过Jsbridge完成的,web页面通过调用JsBridge中的Java接口来调用一些系统API,如打开摄像头等,这也是最常用的功能,最主要还是web页面调用一些系统接口,Native直接调用一些JS方法还是比较少见的,常用的方法是Native向web页面中注入一些JS代码动态修改页面,例如注入JS以达到对网页进行换肤,黑夜模式等。
Native调用JS
方式1:通过loadUrl(“javascript:”)
我们以一个小小的例子来说明这个过程,我们向页面中注入js弹出弹框。效果入下图
image.png
首先我们需要申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
12345
我还需要设置WebSettings,WebViewClient和WebChromeClient,其他配置参考第一篇文章,这里就不过多阐述了
WebSettings webSettings = webView.getSettings();
webView.setWebChromeClient(new WebChromeClient());
webSettings.setJavaScriptEnabled(true)
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webView.setWebViewClient(new myWebVliewClient());
12345
然后在WebClient的OnPageStarted中添加如下
webView.loadUrl("javascript: alert('Native注入的JS')");
1
可以注意到我们上面的图片的背景是白色的,其实我们load是http://www.baidu.com, 因为我们在onPageStarted中调用,当时资源还没有加载,所以背景是白色的,而且弹窗会阻塞资源的加载,不按取消页面就无法继续加载下去,会被阻塞在这里,所以对于注入的时机根据具体需求而定。
上面例子是非常简单的JS,如果是复杂的JS文件我们又该如何处理呢,有以下方法,其中我将test.js文件放在了Assets文件夹下,代码如下
InputStreamReader isr = null;
try {
isr = new InputStreamReader(this.getAssets().open("test.js"), "UTF-8");
BufferedReader bf = new BufferedReader(isr);
String content = "";
StringBuilder sb = new StringBuilder();
while (content != null) {
content = bf.readLine();
if (content == null) {
break;
}
sb.append(content.trim());
}
bf.close();
wholeJS = sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
123456789101112131415161718
其中wholeJS就是JS文件转化而来的字符串,然后调用
webView.loadUrl("javascript: " + wholeJS);
1
就可以看到和之前一样的效果。
方式2: 通过evaluateJavascript方法
相较于loadUrl,evaluateJavascript的优势在于异步加载,还可以将执行JS代码的结果带回来,我们以下面一个小例子来说明。
我们在web页面中声明如下方法:
<script>
function getUID() {
var id = 120;
return id + 1;
}
</script>
123456
然后我们注入js代码调用这个函数,并将121这个结果带回来来,在onPageFinished中调用以下代码
webView.evaluateJavascript("getUID()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.d("CALLBACK", value);
}
});
123456
结果如下
image.png
JS调用Native
JS调用Native是最常用也是最重要的方法,在实现上有两种思路,一种是拦截schema,有两种方式拦截,一种是注册接口。我们依然以一个例子取说明这个过程,也很简单,我们load一个在本地的页面,页面上只有一个按钮如下图,按下这个按钮去调用原生的Toast方法。[图片上传失败...(image-10ee8e-1611136677543)]
方式1:拦截URL schema
URL schema是一种请求格式,用拦截URL schema的方式我就需要自定义一个URL schema,如jsbridge://jsbridge://webview?&arg1=hello&arg2=world,我们的html页面如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
html,body{
width: 100%;
height: 100%;
}
div{
width:100%;
height: 100%;
background: aquamarine;
}
</style>
</head>
<body>
<div style="bac">
<button onclick="callAndroid()" style="height: 26px ;width:160px; text-align: center; vertical-align: middle ">JS 调用Native</button>
</div>
</body>
<script>
function callAndroid() {
location.href= "jsbridge://webview?&arg1=hello&arg2=world"
}
</script>
</html>
1234567891011121314151617181920212223242526272829303132
我们需要到WebViewClient的shouldOverrideUrl去拦截,并解析参数,代码如下
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String s) {
Uri uri = Uri.parse(s);
Log.d("test112", s);
if(uri.getScheme().startsWith("jsbridge")) {
String arg1 = uri.getQueryParameter("arg1");
String arg2 = uri.getQueryParameter("arg2");
String s1 = "JS调用Native,参数1:"+arg1+"参数2:"+arg2;
Toast.makeText(MainActivity.this, s1, Toast.LENGTH_LONG).show();
}
return true;
}
123456789101112
最后的结果如下图
image.png
还有另一种拦截就是通过WebChromeClient 拦截web的prompt,过程类似,这里就不再赘述。
方式2:注册Javascript接口
首先创建一个管理接口的类如下
public class AndroidToJS extends Object {
@JavascriptInterface
public void hello(String msg) {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
}
}
123456
然后通过addJavascriptInterface去注册和映射接口如下,JS中就可以 test.函数名 调用函数
webView.addJavascriptInterface(new AndroidToJS(), "test");
1
html页面如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
html,body{
width: 100%;
height: 100%;
}
div{
width:100%;
height: 100%;
background: aquamarine;
}
</style>
</head>
<body>
<div style="bac">
<button onclick="callAndroid()" style="height: 26px ;width:160px; text-align: center; vertical-align: middle ">JS 调用Native</button>
</div>
</body>
<script>
function callAndroid() {
test.hello("JS调用Native");
}
</script>
</html>
1234567891011121314151617181920212223242526272829303132
效果如下
image.png
网友评论