Android WebView内处理302重定向

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

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

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

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

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

  • onPageStarted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 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
1
2
3
4
5
6
7
8
9
10
11
/**
* 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 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栈.

以下为代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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;
}
}

调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}

本文为原创,转载请遵守本站版权声明

更新于:2016年6月3日 21:06