最近做项目,遇到一个需求,因为是国际化项目,在新的国家部署机房,在该机房部署的H5域名和原来的不一样,要求客户端拦截H5,并更改Host。
尝试了几种方案如下:
- 重写WebView loadUrl()方法,在该方法中更改Url。
@Override
public void loadUrl(String url) {
......
url = convertUrl(url);
......
// 必须调用super,让系统继续加载新的url
super.loadUrl(url);
}
上面的方案,在测试中发现,H5页面中有跳转二级页面时,没有走。
- 重写WebViewClient shouldOverrideUrlLoading()方法 先来看下源码的注释
/**
* Give the host application a chance to take over the control when a new
* url is about to be loaded in the current WebView. 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.
* This method is not called for requests using the POST "method".
*
* @param view The WebView that is initiating the callback.
* @param url The url to be loaded.
* @return True if the host application wants to leave the current WebView
* and handle the url itself, otherwise return false.
* @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
* shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
*/
@Deprecated
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
/**
* Give the host application a chance to take over the control when a new
* url is about to be loaded in the current WebView. 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.
*
* <p>Notes:
* <ul>
* <li>This method is not called for requests using the POST "method".</li>
* <li>This method is also called for subframes with non-http schemes, thus it is
* strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
* with the request's url from inside the method and then return true,
* as this will make WebView to attempt loading a non-http url, and thus fail.</li>
* </ul>
* </p>
*
* @param view The WebView that is initiating the callback.
* @param request Object containing the details of the request.
* @return True if the host application wants to leave the current WebView
* and handle the url itself, otherwise return false.
*/
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return shouldOverrideUrlLoading(view, request.getUrl().toString());
}
如果开发没有设置WebViewClient,WebView会让Activity Manager选择合适的处理方法(可能使用浏览器)。如果设置了WebViewClient,该方法返回true,则主程序处理改url,如果返回false,则当前WebView处理,即系统默认处理。(不会执行WebView的loadUrl方法)
最开始的想法是在shouldOverrideUrlLoading()方法更改url,不过发现,这里可以拦截到,但更改后的url,无法让系统默认加载。只能主程序显示调用加载。
@Override
public boolean shouldOverrideUrlLoading(final String url) {
if (isInterceptUrlLoading(url)) {
// 重点处理二级页面跳转的情况
// 如果直接返回false,会由系统默认加载,则无法改变url,且也不会走到loadUrl中,无法在该方法中更改url
getWebView().loadUrl(url);
return true;
}
return false;
}
- 寻找其他更优解法
上面方法2的解法,会改变原来url的加载方式,所以找了找有没有其他更好的办法。
重写WebViewClient的shouldInterceptRequest方法。
/**
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is null, the WebView
* will continue to load the resource as usual. Otherwise, the return
* response and data will be used. NOTE: This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
* @param url The raw url of the resource.
* @return A {@link android.webkit.WebResourceResponse} containing the
* response information or null if the WebView should load the
* resource itself.
* @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest)
* shouldInterceptRequest(WebView, WebResourceRequest)} instead.
*/
@Deprecated
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
return null;
}
/**
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is null, the WebView
* will continue to load the resource as usual. Otherwise, the return
* response and data will be used. NOTE: This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
* @param request Object containing the details of the request.
* @return A {@link android.webkit.WebResourceResponse} containing the
* response information or null if the WebView should load the
* resource itself.
*/
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
}
该方法可以拦截所有请求,除了Html的页面,其他资源js、css都会拦截到。
通过注释,如果返回null,则WebView还是会默认加载原来的url,需要自己创建WebResourceResponse。
可以在此处做离线资源的加载,但不适合只更改url,因为无法创建WebResourceResponse。
- 结论
最后使用方法1和方法2的结合来处理的。