前言:
学习rn已经有大半年了,目前的项目是采用rn的,在项目中曾遇到webview中调用postMessage,然后在浏览器和真机中查看,发现在浏览器中会报错,后者不会报错,然后觉得很奇怪,因此就去粗略的研究了一下rn中webview的实现。由于webview涉及很多东西,所以这一篇写介绍一下安卓的webview,下篇再继续介绍ios的webview。
介绍:
webview主要是用来进行页面请求,页面加载,页面渲染,页面交互等处理,而安卓对于webview是没有区分UIWebview和WKWebview,而是直接采用webkit内核的wkwebview。
webview的工具类:
- websetting:
作用:主要使用来设置和管理webview;
获取WebSettings实例:
// 添加访问网络权限(AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET" />
// 创建webview实例
Webview webview = new WebView(this);
// 创建webSettings实例
WebSettings websettings = webview.getSettings();
WebSettings常用方法:
- webviewclient:
作用:处理各种通知 & 请求事件;
- shouldOverrideUrlLoading:该方法主要是在请求url的时候触发该事件,该事件会根据返回值来决定是否拦截url请求;如果返回true则会不让webview处理该url,而是交给系统来自行处理该url的请求,如果返回的是false,则会不做拦截,直接由webview来处理加载url。
- onPageStarted:页面资源开始加载,此时可以做一个loading操作。
- onPageFinished:页面资源加载结束时触发,此时可以关闭loading操作。
- onLoadResource:页面请求或者加载资源时出错会触发。
- webChromeClient:
作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。
- onProgressChanged(WebView view, int newProgress):获取网页的加载进度并且展示出来。
- onReceivedTitle(WebView view, String title):获取网页的title。
- onJsAlert,onJsConfirm,onJsPrompt:支持js的警告窗,确认窗和输入窗。
js与原生webview的交互:
- 安卓调用js代码:
1)webview的loadUrl:该方法可以在webview中执行本地资源的加载,远程资源的加载以及js代码的执行。
调用形式:
webview.loadUrl(远程url);
webview.loadUrl(本地文件);
webview.loadUrl('javascript:js代码'):该形式必须在在onPageFinished之后才能调用,因为要等到资源加载完毕后,才能执行js代码。其中如果要调用html文件内的代码,只能时js代码嵌入到html的script标签之中。
// html
<html>
...
<script>
function toCall() { ... }
</script>
...
</html>
// webview
shouldOverrideUrlLoading(Webview webview, String url) {
webview.loadUrl(url);
}
onPageFinished(Webview webview) {
webview.loadUrl("javascript: tocall()")
webview.loadUrl("javascript:console.log('asdf')")
}
优点:简单;
缺点:对于原生要采用js代码的结果时就会很麻烦,而且效率低,还有调用它来执行js代码会刷新页面;
使用场景:不需要获取返回值且性能要求不高;
2)webview的evaluateJavascript:可以直接调用js代码,并且能直接获取js代码中的返回值;
webview.evaluateJavascript("avascript:callJS()", new ValueCallback() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
})
优点:调用简单,且效率高,而且能获取js层的执行结果;
缺点:必须要在安卓4.4之后才能用;
使用场景:在安卓4.4条件下优先使用;
- js唤起安卓端代码:
1)webview的addJavascriptInterface:主要时将java类的对象和js对象进行映射,从而实现js调用java层代码;
调用形式:webview.addJavascriptInterface(java对象,js对象)
step1:制定特定的java类:
public class ToJs {
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
void executeJs() {
.....
}
}
step2:进行映射
// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new ToJs(), "test");
step3:在js层调用
window.test.executeJs();
缺点:存在安全性问题,因为可以通过test来获取整一个java类的所有方法,可能会获取系统或者用户等敏感信息。
2)WebViewClient的shouldOverrideUrlLoading:主要是对url请求进行拦截操作,对url的协议格式schema,协议名authority进行判断,如果符合则调用对应的方法并且返回true,允许url拦截,否则返回false,不支持url拦截。
协议形式:schema://authority?param1=xxx¶m2=xxx
// java
websetting.setJavascriptEnabled(true);
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
String schema = uri.getSchema();
String authority = uri.getAuthority();
Set collection = uri.getQueryParameterNames();
if (schema.equal('js') && authority.equal('webview')) {
...
webview.loadUrl('javascript: c"(" + result + ")')
return false
}
}
return true;
})
// js
function b() {
window.location = 'js://webview?a=1&b=2';
}
function c(data) {}
b()
优点:不存在安全漏洞;
缺点:很难在js端获取安卓端方法调用后的返回值;只能通过loadUrl方式将结果返回给js端;
使用场景:不需要返回值的时候;
3)WebChromeClient的onJsAlert()、onJsConfirm()、onJsPrompt():
原理:就是js层调用了alert,confirm,prompt时,webview可以通过设置webchromeclient来拦截弹窗的默认行为,从而对传过来的参数进行处理,一般传过来的参数是协议的格式传过来,主要是为了约束。
区别:
alert ---》没有返回值;
comfirm ---》只返回true或者false;
prompt ----》输入框输入什么,可返回什么,因此可以拦截它,然后改变返回的值;
优点:能获取webview的返回值;
缺点:就是比较麻烦;
(吐槽一下segmengfault这里的富文本框,贴代码特别不方便。)
js与RN的交互:
上面说了这么多,主要是为了给rn中webview和js层通信而做的一个铺垫,接下来就介绍本文的主题。
在rn中,对于视图组件在安卓中是会受到ViewManager控制;在rn中webview管理器的结构如下:
其中:
ReactWebViewClient:实现了WebviewClient,并且重写了几个重要的生命钩子;
ReactWebView:实现了webview,它根据rn的特点重写了很多方法,并且在setWebViewClient中将ReactWebviewClient注入进来。
ReactWebviewBridge:提供给js层调用。
当设置webview的source的时候,就会触发setSource方法,此时的处理逻是:
由此处可以看出当对于html或者uri会有不一样的处理,但最后还是通过webview.loadUrl来加载资源。
对于原生调用js代码,rn会多一层封装,将loadUrl和evaluteJavascript封装在一起evaluateJavascriptWithFallback;
通信原理图(有些复杂,ios的不会这么复杂):
(由于流程图比较大且复杂,所以分开三段展示)
关键步骤:
step1:js层调用webview层方法:
step2:rn层与rn-js层通信:
step3:rn-js层与rn层通信:
step4:rn层调用js方法:
总结:其实rn-js与html层通信,可以拆分成rn-js层和rn原生层通信+webview和js层的通信;
参考文章:
安卓webview
最全面总结 Android WebView与 JS 的交互方式
Android WebView 全面干货指南
你不知道的 Android WebView 使用漏洞
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。