學習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 使用漏洞