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