1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        前端工程師所需要了解的WebView

        共 8219字,需瀏覽 17分鐘

         ·

        2021-03-26 10:58

        點(diǎn)上方藍(lán)字關(guān)注公眾號「前端UpUp


        | 導(dǎo)語 現(xiàn)如今,在做移動端 H5 開發(fā)時,少不了與 Native 之間進(jìn)行交互。而在Native中,H5的承載容器為 WebView,其核心是使用 WebView 控件實(shí)現(xiàn)加載 url。

        WebView的概念

        WebView 用來展示網(wǎng)頁的 view 組件,該組件是你運(yùn)行自己的瀏覽器或者在你的線程中展示線上內(nèi)容的基礎(chǔ)。使用 Webkit 渲染引擎來展示,并且支持前進(jìn)后退等基于瀏覽歷史,放大縮小,等更多功能。


        簡單來說 WebView 是手機(jī)中內(nèi)置了一款高性能 Webkit 內(nèi)核瀏覽器,在 SDK 中封裝的一個組件。不過沒有提供地址欄和導(dǎo)航欄,只是單純的展示一個網(wǎng)頁界面。

        以上是一個客戶端開發(fā)者描述的,而站在一個前端開發(fā)者的角度,使用過后的感受就是:

        WebView 可以簡單理解為頁面里的 iframe 。原生app與 WebView 的交互可以簡單看作是頁面與頁面內(nèi) iframe 頁面進(jìn)行的交互。就如頁面與頁面內(nèi)的 iframe 共用一個 Window  一樣,原生與  WebView  也共用了一套原生的方法。

        既然我們使用了 WebView 來承載 H5 ,那么便少不了與 Native 之間發(fā)生交互, WebView 所承載的頁面,通過 JS 與 Native 進(jìn)行通信,我們將這個通信“橋梁”為 JSBridge 。如果你參與過微信內(nèi)置瀏覽器的 H5 開發(fā),會發(fā)現(xiàn)一個經(jīng)常出現(xiàn)的東西,叫做 WeixinJSBridge。

        JSBridge

        JSBridge 簡單來講,主要是 給 JavaScript 提供調(diào)用 Native 功能的接口,讓混合開發(fā)中的『前端部分』可以方便地使用地址位置、攝像頭甚至支付等 Native 功能。

        既然是『簡單來講』,那么 JSBridge 的用途肯定不只『調(diào)用 Native 功能』這么簡單寬泛。實(shí)際上,JSBridge 就像其名稱中的『Bridge』的意義一樣,是 Native 和非 Native 之間的橋梁,它的核心是 構(gòu)建 Native 和非 Native 間消息通信的通道,而且是 雙向通信的通道

        所謂 雙向通信的通道:

        • JS 向 Native 發(fā)送消息 : 調(diào)用相關(guān)功能、通知 Native 當(dāng)前 JS 的相關(guān)狀態(tài)等。

        • Native 向 JS 發(fā)送消息 : 回溯調(diào)用結(jié)果、消息推送、通知 JS 當(dāng)前 Native 的狀態(tài)等。

        JavaScript 是運(yùn)行在一個單獨(dú)的 JS Context 中(例如,WebView 的 Webkit 引擎、JSCore)。由于這些 Context 與原生運(yùn)行環(huán)境的天然隔離,我們可以將這種情況與 RPC(Remote Procedure Call,遠(yuǎn)程過程調(diào)用)通信進(jìn)行類比,將 Native 與  JavaScript 的每次互相調(diào)用看做一次 RPC 調(diào)用。如此一來我們可以按照通常的 RPC 方式來進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。

        在 JSBridge 的設(shè)計(jì)中,可以把前端看做 RPC 的客戶端,把 Native 端看做 RPC 的服務(wù)器端,從而 JSBridge 要實(shí)現(xiàn)的主要邏輯就出現(xiàn)了:通信調(diào)用(Native 與 JS 通信 和 句柄解析調(diào)用。(如果你是個前端,而且并不熟悉 RPC 的話,你也可以把這個流程類比成 JSONP 的流程。)

        通過以上的分析,可以清楚地知曉 JSBridge 主要的功能和職責(zé),接下來,就分析一下在 Android WebView 和 iOS WebView 中實(shí)現(xiàn) Native 與 JS 通信的原理。

        Android WebView

        Android 4.4前:Android WebView在低版本 & 高版本采用了不同的Webkit版本的內(nèi)核(正因?yàn)槿绱耍琀5的很多新特性,在Android版本小于4.4的安卓機(jī)上,都不支持)

        Android 4.4后:原本基于Webkit的WebView開始基于 Chromium內(nèi)核,這一改動大大提升了 WebView組件的性能以及對 HTML5, CSS3, JavaScript的支持。不過它的API卻沒有很大的改動,在兼容低版本的同時只引進(jìn)了少部分新的API,并不需要你做很大的改動。

        在 Android WebView,要實(shí)現(xiàn) JS 調(diào)用 Java,有 3 方法:

        • JavascriptInterface

        • WebViewClient.shouldOverrideUrlLoading()

        • WebChromeClient.onXXX()

        1、JavascriptInterface

        這是 Android 提供的 JS 與 Native 通信的官方解決方案。

        首先 Native 端需要實(shí)現(xiàn)這么一個類,給 JavaScript 調(diào)用。

        public class WebAppInterface {    @JavascriptInterface    public void showToast(String toast) {        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();    }}
        然后將這個 WebAppInterface類,通過以下代碼,添加到 WebView 的 JavaScriptInterface 中。
        WebView webView = (WebView) findViewById(R.id.webview);webView.addJavascriptInterface(new WebAppInterface(this), "Android"); // 這里的Android會被當(dāng)做一個變量,注入到頁面的window中。

        接著就可以在 JS 中調(diào)用 Native 了。

        function showAndroidToast(toast) {    Android.showToast(toast);}

        2、WebViewClient.shouldOverrideUrlLoading()

        這個方法的作用是攔截所有 WebView 的 URL Scheme 。

        URL Scheme 是一種類似于 url 的鏈接,是為了方便 app 直接互相調(diào)用設(shè)計(jì)的,形式和普通的 url 近似,主要區(qū)別是 protocol 和 host 一般是自定義的。

        攔截 URL Scheme 的主要流程是:Web 端通過某種方式(例如 iframe.src/location.href)發(fā)送 URL Scheme 請求,之后 Native 攔截到請求并根據(jù) URL Scheme(包括所帶的參數(shù))進(jìn)行相關(guān)操作。

        頁面可以構(gòu)造一個特殊格式的 URL Scheme 觸發(fā),shouldOverrideUrlLoading 攔截 URL 后判斷其格式,然后 Native 就能執(zhí)行自身的邏輯了。

        public class CustomWebViewClient extends WebViewClient {  @Override  public boolean shouldOverrideUrlLoading(    WebView view,    String url    ) {      if (isJsBridgeUrl(url)) {        // JSbridge的處理邏輯        return true;      }      return super.shouldOverrideUrlLoading(view, url);    }}

        3、WebChromeClient.onXXX()

        通過修改原來瀏覽器的 window某些方法,然后攔截固定規(guī)則的參數(shù),然后分發(fā)給Java 對應(yīng)的方法去處理

        • alert,可以被 WebView 的 WebChromeClient.onJsAlert() 監(jiān)聽

        • confirm,可以被 WebView 的 WebChromeClient.onJsConfirm() 監(jiān)聽

        • console.log,可以被 WebView 的 WebChromeClient.onConsoleMessage() 監(jiān)聽

        • prompt,可以被 WebView 的 WebChromeClient.onJsPrompt()監(jiān)聽

        prompt 簡單舉例說明,Web 頁面通過調(diào)用 prompt()方法,安卓客戶端通過監(jiān)聽WebChromeClient.onJsPrompt()事件,攔截傳入的參數(shù),如果參數(shù)符合一定協(xié)議規(guī)范,那么就解析參數(shù),扔給后續(xù)的 Java 去處理。

        window.prompt(message, value);

        WebChromeClient.onJsPrompt()就會受到回調(diào)。onJsPrompt()方法的 message參數(shù)的值正是JS的方法 window.prompt()的 message的值。

        public class CustomWebChromeClient extends WebChromeClient {  @Override  public boolean onJsPrompt(    WebView view,    String url,    String message,    String defaultValue,    JsPromptResult result    ) {        // 處理JS 的調(diào)用邏輯        result.confirm();        return true;    }}

        Java 調(diào)用 JavaScript

        Android,在 Kitkat(4.4)只能用 loadUrl 一段 JavaScript 代碼。

        webView.loadUrl("javascript:" + javaScriptString);

        而 Kitkat 之后的版本,也可以用 evaluateJavascript 方法實(shí)現(xiàn):

        webView.evaluateJavascript(javaScriptString, new ValueCallback<String>() {    @Override    public void onReceiveValue(String value) {      // native代碼    }  });

        IOS WebView

        In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView. Additionally, consider setting the WKPreferences property javaScriptEnabled to NO if you render files that are not supposed to run JavaScript.

        在 IOS8 之前,蘋果手機(jī)的 WebView 使用的 UIWebView,UIWebView長期以來存在某些問題:

        • 加載速度慢

        • 存在內(nèi)存泄漏

        • 內(nèi)存占用多,內(nèi)存優(yōu)化困難

        • 如果內(nèi)存占用過多還可能因?yàn)檎加眠^多被系統(tǒng)kill掉

        在 WWDC 2014 大會上,IOS8推出了 WKWebViewWKWebView 是現(xiàn)代 Webkit API 在 iOS 8 和 OS X Yosemite 應(yīng)用中的核心部分。它代替了 UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了統(tǒng)一的跨雙平臺 API。擁有 60fps 滾動刷新率、內(nèi)置手勢、高效的 app 和 web 信息交換通道、和 Safari 相同的 JavaScript 引擎。

        JavaScript ?? Swift 對話機(jī)制

        使用用戶腳本來注入 JavaScript

        WKUserScript 允許在正文加載之前或之后注入到頁面中。這個強(qiáng)大的功能允許在頁面中以安全且唯一的方式操作網(wǎng)頁內(nèi)容。

        一個簡單的例子如下,用戶改變背景的用戶腳本被插入到網(wǎng)頁中:

        let source = "document.body.style.background = \"#777;// 注入腳本 在文檔加載完成后執(zhí)行let userScript = WKUserScript()let userScript = WKUserScript(source: source, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)let userContentController = WKUserContentController()userContentController.addUserScript(userScript)
        let configuration = WKWebViewConfiguration()configuration.userContentController = userContentControllerself.webView =WKWebView(frame: self.view.bounds, configuration: configuration)

         對象可以以 JavaScript 源碼形式初始化,初始化時還可以傳入是在加載之前還是結(jié)束時注入,以及腳本影響的是這個布局還是僅主要布局。于是用戶腳本被加入到 WKUserContentController 中,并且以 WKWebViewConfiguration 屬性傳入到 WKWebView 的初始化過程中。

        這個樣例可以簡單擴(kuò)展為更為高級的頁面修改方法,例如去除廣告、隱藏評論等。

        Message Handlers

        利用以下代碼,可以跟Native進(jìn)行通信

        window.webkit.messageHandlers.{NAME}.postMessage()

        Handler的name可以通過 WKScriptMessageHandler 協(xié)議中的 addScriptMessageHandler() 接口函數(shù)設(shè)置:

        class NotificationScriptMessageHandler: NSObject, WKScriptMessageHandler {  func userContentController(    userContentController: WKUserContentController,    didReceiveScriptMessage message: WKScriptMessage!   ) {     println(message.body)  }}let userContentController = WKUserContentController()let handler = NotificationScriptMessageHandler()userContentController.addScriptMessageHandler(handler, name: "notification")

        于是當(dāng)通知進(jìn)入 app 的時候,比如說在頁面中創(chuàng)建一個新對象,相關(guān)信息就可以這樣傳遞:

        window.webkit.messageHandlers.notification.postMessage({body: '發(fā)送給Native'});

        添加用戶腳本來對 web 事件監(jiān)聽并用 Message Handler 將信息傳回 app。

        總結(jié)

        通信原理是 JSBridge 實(shí)現(xiàn)的核心,實(shí)現(xiàn)方式可以各種各樣,但是萬變不離其宗。這里,推薦的實(shí)現(xiàn)方式如下:

        • JavaScript 調(diào)用 Native 推薦使用 注入 API 的方式。( iOS6 忽略,Android 4.2以下使用 WebViewClient 的 onJsPrompt 方式。)

        • Native 調(diào)用 JavaScript 則直接執(zhí)行拼接好的 JavaScript 代碼即可。

        對于其他方式,諸如 React Native、微信小程序 的通信方式都與上描述的近似,并根據(jù)實(shí)際情況進(jìn)行優(yōu)化。

        以 React Native 的 iOS 端舉例:JavaScript 運(yùn)行在 JSCore 中,實(shí)際上可以與上面的方式一樣,利用注入 API 來實(shí)現(xiàn) JavaScript 調(diào)用 Native 功能。不過 React Native 并沒有設(shè)計(jì)成 JavaScript 直接調(diào)用 Object-C,而是 為了與 Native 開發(fā)里事件響應(yīng)機(jī)制一致,設(shè)計(jì)成 需要在 Object-C 去調(diào) JavaScript 時才通過返回值觸發(fā)調(diào)用。原理基本一樣,只是實(shí)現(xiàn)方式不同。


        緊追技術(shù)前沿,深挖專業(yè)領(lǐng)域
        掃碼關(guān)注我們吧!



        瀏覽 95
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费超级乱淫视频播放 | 日韩大操逼 | 国产露脸av | 国产精品久久久久久久久绿色 | 看操逼视频123 | 淫色淫色 www.b2gd.com | 国产chinese中国xxxx | 日日撸天天撸夜夜撸 | 国产精品人成A片一区二区 | AV偷拍|