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,会更健壮。

相关文章
相关标签/搜索