WebView#shouldOverrideUrlLoading究竟要返回true仍是false

各大教程的常見作法

初接觸Android WebView的萌新,在博覽前人代碼的時候都會看到,WebView控件並非一初始化後就直接loadUrl的,這其中還有一系列的初始化設置。html

好比:若是沒有給WebView設置WebViewClient,那麼,你點擊網頁內的a連接,該連接會跳轉到系統自己的瀏覽器去打開,而若是想要本身處理這個a連接,就要設置自定義的WebViewClient實例,而且實現shouldOverrideUrlLoading方法。如何實現呢,網上常見的實現方式以下:前端

@Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        webView.loadUrl(url);
        return true;
    }
複製代碼

這樣,就能夠完美的讓a連接在本身的WebVie裏面加載啦。java

返回值爲何是boolean類型

看到這裏,也許你會想考究一下這個boolean返回值的做用。c++

boolean返回值的方法在Android SDK裏很常見,好比View#dispatchKeyEvent方法的返回值就是boolean,在實現該方法時,若是你返回true則表示開發者本身來處理這個keyevent,讓系統不要再往下一個View傳播了;返回false則表示你不處理,讓系統接着把事件告訴下一個View吧。web

那麼,WebView#shouldOverrideUrlLoading的返回值也是這個做用嗎?瀏覽器

返回true的做用咱們已經證明了,確實是表示開發者本身處理了這個url的加載。那麼返回false試試呢?安全

@Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        return false;
    }
複製代碼

編譯運行,發現和前一種處理方式的結果同樣,WebView本身來加載a連接。找找源碼,發現shouldOverrideUrlLoading的註釋裏有這麼一句:app

/** * If WebViewClient is not * provided, by default WebView will ask Activity Manager to choose the * proper handler for the url. If WebViewClient is provided, return true * means the host application handles the url, while return false means the * current WebView handles the url. */
複製代碼

就是說:若是沒有設置WebViewClient實例,那麼就會由系統默認瀏覽器來處理url,若是設置了WebViewClient且本方法返回true,就表示已經處理了這個url;若是返回false,就表示讓當前webview來處理url。如此看來,這個模式和事件機制仍是很相似的,是一個責任鏈模式,若是你WebViewClient不想處理,那就把事件傳給WebView,WebView的處理方式就是加載。ide

另外能夠看到shouldOverrideUrlLoading自己的返回值就是false,也就是說,其實根本用不着自定義這個方法,傳一個原生的WebViewClient實例給WebView,就能愉快的讓WebView本身處理全部a連接跳轉啦!post

WebView webView = new WebView(context);
    webView.setWebViewClient(new WebViewClient());
    webview.loadUrl("http://www.uc.cn");
複製代碼

至於爲何網上教程裏基本上都是寫return true的那種處理方法,這個,筆者天然也不得而知了。可是既然跟到了這裏,不妨更加深刻一點。到底兩個處理方式有沒有區別呢?

shouldOverrideUrlLoading詳解

何時會有shouldOverrideUrlLoading回調?
在網頁內點擊一個a連接,會回調;
302跳轉前,會回調;
用JavaScript裏的location.href來跳轉,會回調;
主動調用WebView.loadUrl方法來加載url,不會回調。
...
若是枚舉下去,狀況太多。因此仍是看代(zhu)碼(shi)吧。

/** * Give the host application a chance to take over the control when a new * url is about to be loaded in the current WebView. ... * This method is not called for requests using the POST "method". */
複製代碼

「給你的宿主app一個機會來控制‘當前WebView將要加載新的url’的狀況」——簡單來講,就是除了你主動用webview加載的url,其餘的加載都會回調shouldOverrideLoading。
固然,後面還有一句註釋:post請求不會回調shouldOverrideLoading。這個就很明顯是爲的數據安全考慮了。

區別

終於要說到區別了,這個區別的狀況也是筆者偶然間才發現的。
前端有時候會在html的<head>節點裏用JavaScript作一些判斷,而後跳轉到另外一個url。好比,咱們有一個a網頁,它的源代碼以下:

<html>
    <head>
        <script> location.href = "http://www.uc.cn" </script>
    </head>
</html>
複製代碼

這個跳轉固然能夠在shouldOverrideUrlLoading裏面捕獲到,然而,這個時候,用true仍是false的方式來處理,就會出現區別了。
若是用主動loadUrl來實現,而且返回true,那麼,a網頁會被加入到WebView的歷史記錄中,也就是說,跳轉後你按返回鍵,是能夠回到a網頁的,固然,這個時候又會觸發js裏面的跳轉,跳去了「http://www.uc.cn」。
若是用直接return false的方式來實現呢。a網頁是不會被加入WebView的歷史記錄的,返回鍵也沒法回到a網頁,似乎它歷來沒有出現過。

產生這種區別的緣由是什麼?這就關係到WebView的底層的實現了。location.href被重置時,會調用JNI層的NavigationScheduler類

bool NavigationScheduler::MustReplaceCurrentItem(LocalFrame* target_frame) {
  // Non-user navigation before the page has finished firing onload should not
  // create a new back/forward item. See https://webkit.org/b/42861 for the
  // original motivation for this.
  if (!target_frame->GetDocument()->LoadEventFinished() &&
      !Frame::HasTransientUserActivation(target_frame))
    return true;

  // Navigation of a subframe during loading of an ancestor frame does not
  // create a new back/forward item. The definition of "during load" is any time
  // before all handlers for the load event have been run. See
  // https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation
  // for this.
  Frame* parent_frame = target_frame->Tree().Parent();
  return parent_frame && parent_frame->IsLocalFrame() &&
         !ToLocalFrame(parent_frame)->Loader().AllAncestorsAreComplete();
}
複製代碼

當這個方法返回true時,location.href傳的參數會替換掉當前url,也就是說,不留下歷史記錄。何時返回true呢?在主文檔的onload event發生以前改動href,就會返回true。咱們的例子中的js是在<head>裏面執行的,屬於這一類狀況。
因此,當咱們在shouldOverrideUrlLoading裏面直接返回false,讓WebView使用本身默認的方式來加載,就不會有歷史記錄存在了。
然而,當咱們經過主動調用loadUrl並返回true的方式來干預加載時,WebView會先執行完a網頁的加載(發生了onload event)以後,再執行loadUrl加載新的url,如此,a網頁就被加入到歷史記錄裏面了。

基於此,筆者的建議是,shouldOverrideUrlLoading回調時,若是你不須要作其餘的特殊操做,僅僅只是要加載新url的話,能夠直接return false,讓系統WebView以本身的方式來加載url,會更健壯。

相關文章
相關標籤/搜索