学习rn已经有大半年了,目前的项目是采用rn的,在项目中曾遇到webview中调用postMessage,而后在浏览器和真机中查看,发如今浏览器中会报错,后者不会报错,而后以为很奇怪,所以就去粗略的研究了一下rn中webview的实现。因为webview涉及不少东西,因此这一篇写介绍一下安卓的webview,下篇再继续介绍ios的webview。javascript
webview主要是用来进行页面请求,页面加载,页面渲染,页面交互等处理,而安卓对于webview是没有区分UIWebview和WKWebview,而是直接采用webkit内核的wkwebview。html
做用:主要使用来设置和管理webview;java
获取WebSettings实例:android
// 添加访问网络权限(AndroidManifest.xml) <uses-permission android:name="android.permission.INTERNET" /> // 建立webview实例 Webview webview = new WebView(this); // 建立webSettings实例 WebSettings websettings = webview.getSettings();
WebSettings经常使用方法:ios
做用:处理各类通知 & 请求事件;web
做用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。chrome
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条件下优先使用;
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这里的富文本框,贴代码特别不方便。)
上面说了这么多,主要是为了给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 使用漏洞