关闭

WebView中常见问题总结

标签: title标题百度地图空白页自定义错误页面重定向返回https ssl 空白页
1419人阅读 评论(0) 收藏 举报
分类:

    最近在项目中遇到对WebView的需求应用,在开发中遇到了几点问题,记录下来以便不时之需。

    1、WebView中获取网页标题的问题:在利用WebView开发的时候,有多种获取页面标题的方法:

     (1)、最常用的方法:重写WebChromeClient的onReceivedTitle方法:

	webview.setWebChromeClient(new WebChromeClient(){
		@Override
		public void onReceivedTitle(WebView view, String title) {
			Toast.makeText(mContext, title, Toast.LENGTH_SHORT).show();
		}
	});

     (2)、利用WebView的getTitle()方法获取:

	webview.getTitle();

           在WebView的大多回调方法的参数中都保留对WebView的引用,可以直接利用该对象获取title,如在WebViewClient的方法onPageFinished中:

	@Override
	public void onPageFinished(WebView view, String url) {
		String title = view.getTitle();
		if (StringUtils.isNullOrEmpty(title)) {
			title = titleStr;
		}
		tv_title_content.setText(title);
	}

      (3)、通过WebView获取前进后退队列,然后得到title,WebView提供了获取前进后退队列的方法供调用:

	WebBackForwardList copyBackForwardList = webview.copyBackForwardList();
	WebHistoryItem currentItem = copyBackForwardList.getCurrentItem();
	if(currentItem!=null){
		String title =currentItem.getTitle();
		Toast.makeText(mContext, title, Toast.LENGTH_SHORT).show();
	}

      当然,利用copyBackForwardList 还可以做其他很多操作。

      综合上面几种获取title的方式,有几点需要注意的地方:① onReceivedTitle()方法在某些机型上执行WebView的goback()方法之后不会调用,故通过在onReceivedTilte中获取title然后自己维护的话,容易造成内容与标题不一致的情况出现,所以,在牵扯到网页内跳转较多的时候建议采用后两种方法;② 在android4.4的手机上onPageFinished()方法会多调用一次,所以复杂的逻辑上最好不要在这个函数中做。
  

    2、利用WebView加载百度地图的网页时出现空白页的问题解决,针对此问题,网上有各种答案,不能说谁对谁错,只能说针对不同的机型或百度地图的不同版本有不同的解决方案,当然,本人也没有针对全部机型进行测试,在这儿只记录我的解决方法:

    // 本人亲测,设置下面几行代码的话,当前日期下的百度地图在大多数手机上展示是没问题的
    webSettings.setJavaScriptEnabled(true);
    webSettings.setAllowFileAccess(true);
    webSettings.setDomStorageEnabled(true);

    另外,也有人说设置如下参数也可解决该问题,但是我在小米3手机上测试没有通过:

    webSettings.setBlockNetworkImage(false);
    webSettings.setBlockNetworkLoads(false);


    3、如果在网页中播放了音频文件,在页面返回之后声音依然在播放问题的解决。如果在页面返回的时候不对音频文件做处理的话,会保持对音频资源的引用,直到其播放完毕,可以在页面的onDestroy()方法中利用WebView的destroy()方法将WebView相关的资源释放掉,但是如果直接调用该方法,有可能会抛异常,可先将WebVew从页面中移除再调用该方法:

	@Override
	protected void onDestroy() {
		try {
			((RelativeLayout) findViewById(R.id.rl_root)).removeView(webview);
			webview.destroy();
		} catch (Exception e) {
			e.printStackTrace();
		}
		super.onDestroy();
	}


    4、自定义错误显示页面解决方案。当WebView加载网页出错时,特别是网络出错时,WebView展示的页面不太友好,这时可通过自定义错误页面的方式改善用户体验。自定义错误页面有两种方式,一种是在WebView所在布局中添加错误页面布局;另外一种是自定义错误网页,将其放在assets文件夹中,在网页出错时加载该错误网页。

    (1) 在布局中添加错误提示布局方式展示自定义错误页面:

         页面布局如下,ViewStub中的布局即为自定义的错误布局。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <include
        android:id="@+id/rl_title_container"
        layout="@layout/title_include_layout" />

    <ProgressBar
        android:id="@+id/progress_bar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:layout_below="@+id/rl_title_container"
        android:progressDrawable="@drawable/pro_bg" />

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/progress_bar" />

    <!-- 默认情况下此布局不会加载,提高性能,网页加载失败时填充 -->

    <ViewStub
        android:id="@+id/vs_nomsg_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/progress_bar"
        android:inflatedId="@+id/ll_nomsg_container"
        android:layout="@layout/common_nomsg_layout" />

</RelativeLayout>

      分别重写WebViewClient中的WebView的onReceivedError()和WebChromeClient中的onProgressChanged()方法处理错误页面展示的逻辑。由于onPageFinished方法在android4.4中会调用两次,所以不在此方法中处理。另,本人测试的机型,如果页面加载出错,均是先调用onReceivedError(),后调用onProgressChanged(),故这种处理方式可行。

private boolean isError=false;//网页是否加载失败

private class MyWebViewClient extends WebViewClient {

	@Override
	public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
		super.onReceivedError(view, errorCode, description, failingUrl);
		initNoMsgLayout(view);
		isError = true;
	}

	/** 延迟加载网页出错时展示的布局 */
	private void initNoMsgLayout(WebView view) {
		ViewStub stub = (ViewStub) findViewById(R.id.vs_nomsg_layout);
		if (stub != null) {// ViewStub在调用inflate()后会从页面中移除,再次findViewById时返回null
			ll_nomsg_container = stub.inflate();
		} else {
			ll_nomsg_container.setVisibility(View.VISIBLE);
		}
		view.setVisibility(View.GONE);
	}
}
private class MyWebChromeClient extends WebChromeClient {
	@Override
	public void onProgressChanged(WebView view, int newProgress) {
		if (newProgress == 100) {
			progress_bar.setProgress(newProgress);
			progress_bar.setVisibility(View.GONE);
			if (!isError) {
				if (ll_nomsg_container != null) {
					ll_nomsg_container.setVisibility(View.GONE);
				}
				view.setVisibility(View.VISIBLE);
			}
			isError = false;
		} else {
			if (progress_bar.getVisibility() == View.GONE)
				progress_bar.setVisibility(View.VISIBLE);
				progress_bar.setProgress(newProgress);
			}
		super.onProgressChanged(view, newProgress);
	}
}

    (2)自定义错误网页,将其放在assets文件夹中,在网页出错时加载该错误网页。在onReceivedError()方法中加载本地错误页面:

private class MyWebViewClient extends WebViewClient {

	@Override
	public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
		super.onReceivedError(view, errorCode, description, failingUrl);
		webview.loadUrl("file:///android_asset/error.html"); 
	}
}

     当然,如果自定义的错误页面error.html页面中有js的调用,还得针对WebVew设置相关属性:

webview.getSettings().setJavaScriptEnabled(true);//支持js调用
webview.getSettings().setUseWideViewPort(true);//页面展示时缩放到合适大小

     虽然此方法使用简单,但是如果网页中有链接跳转的话,想维护前进后退以及刷新功能的话,就得做额外处理了,因为此方法的处理将错误页面的加载也放到了前进后退中。找了好久WebView相关的API也没找到添加和移除浏览历史的方法,最后写了这么一个笨方法:

@Override
public void onClick(View v) {
	switch (v.getId()) {
	case R.id.tv_title_content:// 返回
		if (webview != null) {
			WebBackForwardList copyBackForwardList = webview.copyBackForwardList();
			int currentIndex = copyBackForwardList.getCurrentIndex();
			if (currentIndex > 0) {
				String currUrl = copyBackForwardList.getCurrentItem().getUrl();
				if (currUrl.contains("file:///")) {
					currUrl = copyBackForwardList.getItemAtIndex(currentIndex - 1).getUrl();
					currentIndex--;
				}
				int backStep = 1;// 累计需要后退的步数
				for (int i = currentIndex - 1; i >= 0; i--) {
					String backUrl = copyBackForwardList.getItemAtIndex(i).getUrl();
					if (backUrl.contains("file:///") || backUrl.equals(currUrl)) {// 当为错误页面或与当前页面一样时,累加需跳转的步数
						backStep++;
						continue;
					}
					if (webview.canGoBackOrForward(-backStep)) {
						webview.goBackOrForward(-backStep);
					} else {
						finish();
					}
					break;
				}
				if (backStep > currentIndex) {
					finish();
				}
			} else {
				finish();
			}
		} else {
			finish();
		}
		break;
	case R.id.tv_first_operator:// 刷新
		if (webview != null) {
			WebBackForwardList copyBackForwardList = webview.copyBackForwardList();
			WebHistoryItem currentItem = copyBackForwardList.getCurrentItem();
			if (currentItem != null) {
				String currUrl = currentItem.getUrl();
				if (currUrl.contains("file:///") && webview.canGoBack()) {
					// 加载网页出错时加载了一次本地的错误页面,所以如果当前展示的是错误页面,则执行后退操作即可回到上次加载的页面
					webview.goBack();
					WebBackForwardList copyBackForwardList2 = webview.copyBackForwardList();
					WebHistoryItem currentItem2 = copyBackForwardList2.getCurrentItem();
					if (currentItem2 != null) {// 因为此处的刷新操作不会导致WebBackForwardList中两个及以上的错误页面连续,所以回退一次之后一定是可用链接
						webview.loadUrl(currentItem2.getUrl());
					}
					return;
				} else {
					webview.reload();
				}
			}
		}
		break;
	}
}


5、关于有重定向页面时执行goback()方法无法正常后退的问题,当利用WebView加载页面时,如果加载的页面又重定向到了另外一个页面,造成页面无法利用goback()方法执行后退。其实这不是WebView的bug,而是我们在重写方法的时候不小心给自己设的一个陷阱。当我们为了防止点击页面中的链接而跳转到系统自带的浏览器时要重写WebViewClient中的shouldOverrideUrlLoading()方法,网上大部分的资料是这样的:

webView.setWebViewClient(new WebViewClient() {

	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		view.loadUrl(url);
		return true;
	}

});

 注意该函数的返回值,此函数默认是返回false的return super.shouldOverrideUrlLoading(view, url);,所以很多人想当然的认为返回true就可以限制到自己的WebView页了,殊不知这个返回值正是导致有重定向页面时无法正常后退的原因,改为返回false即可解决此问题。其实如果单单只是想将页面中的跳转限制到自己的WebView中的话只需要做如下操作即可:

webview.setWebViewClient(new 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.
     */
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return false;
    }
从方法的注释中我们可以看到,在一个新的链接被加载时,会出现以下三种情况:
1、如果没有提供WebViewClient对象,即没有调用WebView的setWebViewClient(WebViewClient client)方法为其设置Client对象,则WebView会请求android系统的Activity管       理器去处理该url链接的加载,一般情况就是启动系统浏览器来加载该url;
2、如果提供了WebViewClient对象并且shouldOverrideUrlLoading()方法返回true,则表示该Url交由当前应用程序自己来处理,当前WebView不会再加载该Url;
3、如果提供了WebViewClient对象并且shouldOverrideUrlLoading()方法返回false,则表示该Url交由当前应用程序的当前WebView自己来处理。

通过该注释我们清楚的了解到只要我们为WebView设置了新的WebViewClient对象,该url链接的加载就会交由当前应用自己来处理,即设置以下一行代码即可:
webview.setWebViewClient(new WebViewClient());
至于限制链接在当前应用中加载之后的处理,就要根据需求来了,如果想要当前WebView继续加载该Url链接,那就返回false,如果想终止掉当前WebView对该Url链接的加载,就返回true。

6、WebView访问https页面时出现空白页的问题。
    当使用WebView访问某些ssl加密的url时,页面会出现空白的情况,而使用系统自带的浏览器打开时,会弹出确认证书的对话框。这是用于某些站点需要证书确认才能访问的缘故,简单的处理方法是忽略这步证书的确认:
webView.setWebViewClient(new WebViewClient(){

<span style="white-space:pre">	</span>@Override
	public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
		handler.proceed();
	}
});


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:77073次
    • 积分:1781
    • 等级:
    • 排名:千里之外
    • 原创:102篇
    • 转载:17篇
    • 译文:1篇
    • 评论:8条
    文章分类