react-native中安卓webview和js層通訊

前言:

學習rn已經有大半年了,目前的項目是採用rn的,在項目中曾遇到webview中調用postMessage,而後在瀏覽器和真機中查看,發如今瀏覽器中會報錯,後者不會報錯,而後以爲很奇怪,所以就去粗略的研究了一下rn中webview的實現。因爲webview涉及不少東西,因此這一篇寫介紹一下安卓的webview,下篇再繼續介紹ios的webview。javascript

介紹:

webview主要是用來進行頁面請求,頁面加載,頁面渲染,頁面交互等處理,而安卓對於webview是沒有區分UIWebview和WKWebview,而是直接採用webkit內核的wkwebview。html

webview的工具類:

  • websetting:

做用:主要使用來設置和管理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經常使用方法:
image.pngios

  • webviewclient:

做用:處理各類通知 & 請求事件;web

  1. shouldOverrideUrlLoading:該方法主要是在請求url的時候觸發該事件,該事件會根據返回值來決定是否攔截url請求;若是返回true則會不讓webview處理該url,而是交給系統來自行處理該url的請求,若是返回的是false,則會不作攔截,直接由webview來處理加載url。
  2. onPageStarted:頁面資源開始加載,此時能夠作一個loading操做。
  3. onPageFinished:頁面資源加載結束時觸發,此時能夠關閉loading操做。
  4. onLoadResource:頁面請求或者加載資源時出錯會觸發。
  • webChromeClient:

做用:輔助 WebView 處理 Javascript 的對話框,網站圖標,網站標題等等。chrome

  1. onProgressChanged(WebView view, int newProgress):獲取網頁的加載進度而且展現出來。
  2. onReceivedTitle(WebView view, String title):獲取網頁的title。
  3. 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&param2=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 ----》輸入框輸入什麼,可返回什麼,所以能夠攔截它,而後改變返回的值;

image.png

image.png

優勢:能獲取webview的返回值;
缺點:就是比較麻煩;
(吐槽一下segmengfault這裏的富文本框,貼代碼特別不方便。)

js與RN的交互:

上面說了這麼多,主要是爲了給rn中webview和js層通訊而作的一個鋪墊,接下來就介紹本文的主題。
在rn中,對於視圖組件在安卓中是會受到ViewManager控制;在rn中webview管理器的結構以下:

image.png

其中:
ReactWebViewClient:實現了WebviewClient,而且重寫了幾個重要的生命鉤子;
ReactWebView:實現了webview,它根據rn的特色重寫了不少方法,而且在setWebViewClient中將ReactWebviewClient注入進來。
ReactWebviewBridge:提供給js層調用。

當設置webview的source的時候,就會觸發setSource方法,此時的處理邏是:
image.png
由此處能夠看出當對於html或者uri會有不同的處理,但最後仍是經過webview.loadUrl來加載資源。

對於原生調用js代碼,rn會多一層封裝,將loadUrl和evaluteJavascript封裝在一塊兒evaluateJavascriptWithFallback;

image.png

通訊原理圖(有些複雜,ios的不會這麼複雜):

image.png
image.png
image.png

(因爲流程圖比較大且複雜,因此分開三段展現)

關鍵步驟:
step1:js層調用webview層方法:
image.png

image.png

image.png

image.png

step2:rn層與rn-js層通訊:
image.png

step3:rn-js層與rn層通訊:
image.png

step4:rn層調用js方法:
image.png

總結:其實rn-js與html層通訊,能夠拆分紅rn-js層和rn原生層通訊+webview和js層的通訊;

參考文章:
安卓webview
最全面總結 Android WebView與 JS 的交互方式
Android WebView 全面乾貨指南
你不知道的 Android WebView 使用漏洞

相關文章
相關標籤/搜索