Android WebView内处理302重定向

2016年2月15日 16:28

开发中处理WebView有面临着以下几个问题:

  1. H5页面在调用Android系统的WebView.goBack()会在一些特殊情况下失效;
  2. H5页面如有重定向页面, 无法返回上一页.

为解决问题1, 我们需要使用WebView.loadUrl()加载上一个页面的链接来替代调用系统APIWebView.goBack(), 但这样会污染系统内部的URL栈, 所以我们还需要自行维护一个URL栈.

解决问题2, 则需要判断当前链接的状态码, 但WebView中没显式的API来获取当前链接的HTTP Response Code. 但也可以通过一些黑技术来解决.

首先, 需要着重一下WebViewClient这几个方法:

  • onPageStarted
/** * Notify the host application that a page has started loading. This method * is called once for each main frame load so a page with iframes or * framesets will call onPageStarted one time for the main frame. This also * means that onPageStarted will not be called when the contents of an * embedded frame changes, i.e. clicking a link whose target is an iframe. * * @param view The WebView that is initiating the callback. * @param url The url to be loaded. * @param favicon The favicon for this page if it already exists in the * database. */ public void onPageStarted(WebView view, String url, Bitmap favicon) { }
  • onPageFinished
/** * Notify the host application that a page has finished loading. This method * is called only for main frame. When onPageFinished() is called, the * rendering picture may not be updated yet. To get the notification for the * new Picture, use {@link WebView.PictureListener#onNewPicture}. * * @param view The WebView that is initiating the callback. * @param url The url of the page. */ public void onPageFinished(WebView view, String url) { }
  • 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. */ public boolean shouldOverrideUrlLoading(WebView view, String url) { return false; }

可以在这几个方法中添加打印标记, 来加深对调用顺序的理解

首先确定不可以在onPageFinished中记录URL, 然后在onPageStarted中记录会出现多次记录的问题. 通过打印日志发现, 当链接出现一次重定向时方法shouldOverrideUrlLoadingonPageStarted都会再次被调用, 但onPageFinished只会被调用一次, 所以我们可以根据这一点来记录URL栈.

以下为代码实例:

public class WebClient extends WebViewClient { /** * 记录URL的栈 * 规则: * 1.不可在{@code WebView.onPageFinished();}中开始记录URL; * 2.记录需要屏蔽重定向URL. */ private final Stack<String> mUrls = new Stack<>(); /** * 判断页面是否加载完成 */ private boolean mIsLoading; private String mUrlBeforeRedirect; @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); if (mIsLoading && mUrls.size() > 0) { mUrlBeforeRedirect = mUrls.pop(); } recordUrl(url); this.mIsLoading = true; } /** * 记录非重定向链接, 避免刷新页面造成的重复入栈 * * @param url 链接 */ private void recordUrl(String url) { //这里还可以根据自身业务来屏蔽一些链接被放入URL栈 if (!TextUtils.isEmpty(url) && !url.equalsIgnoreCase(getLastPageUrl())) { mUrls.push(url); } else if (!TextUtils.isEmpty(mUrlBeforeRedirect)) { mUrls.push(mUrlBeforeRedirect); mUrlBeforeRedirect = null; } } /** * 获取上一页的链接 **/ private synchronized String getLastPageUrl() { return mUrls.size() > 0 ? mUrls.peek() : null; } /** * 推出上一页链接 */ public String popLastPageUrl() { if (mUrls.size() >= 2) { mUrls.pop();//pop current page url return mUrls.pop(); } return null; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (this.mIsLoading || url.startsWith("about:")) { this.mIsLoading = false; } } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return false; } }

调用方式

WebView web=getWebView(); WebClient client = new WebClient(); web.setWebViewClient(client); /** * 返回上一页 * return True表示处理的网页中返回, False表示没有处理返回需要另行处理 **/ public boolean pageGoBack(WebView web, WebClient client) { final String url = client.popLastPageUrl(); if (url != null) { web.loadUrl(url); return true; } return false; }

文章作者: qbeenslee

CC BY-NC 4.0