webview的使用场景挺多的,比如说一些临时性质的活动,直接使用webview来做,效果更快,而且不用更新客户端。等等。
本地文件用:webView.loadUrl(file:///android_asset/XXX.html);这里的格式是固定的,文件位置 assets目录下
LOAD_DEFAULT: 默认缓存规则: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
判断网络要加上权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
上面的代码处理了接收错误的情况。
上面的代码会显示一个进度条,交互更加友好。
开始直接webView.destory(),发现会报
如果发现链接里面是有login?这个字段的,可以支持跳转到客户端已经支持的页面去,我写的demo里面是跳到用户登录页面。
1 webview的基本使用
使用webview,首先要加上权限:<uses-permission android:name="android.permission.INTERNET"/>,否则会在webiew上报找不到对应的链接。
1.1 基本加载网页:
网络用:webView.loadUrl("http://www.baidu.com");本地文件用:webView.loadUrl(file:///android_asset/XXX.html);这里的格式是固定的,文件位置 assets目录下
1.2 网页缓存模式:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据LOAD_DEFAULT: 默认缓存规则: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
如:www.taobao.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;
在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。www.360.com.cn的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
总结:根据以上两种模式,建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。 if (isNetworkConnected(webActivity.this)) {
wSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
} else {
wSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
public boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager
.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
判断网络要加上权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1.3 goBcak canGoBack goForward canGoForward
这个是在后退的时候,不直接退出,而是返回到上一个页面,知道无法后退。
@OnClick(R.id.btn_left)
void goBack() {
if (webView.canGoBack()) {
webView.goBack();
} else {
btn_left.setEnabled(true);
}
}
@OnClick(R.id.btn_right)
void goForward() {
if (webView.canGoForward()) {
webView.goForward();
} else {
btn_right.setEnabled(true);
}
}
@InjectView(R.id.btn_exit)
Button btn_exit;
@OnClick(R.id.btn_exit)
void exit() {
finish();
}
@OnClick(R.id.btn_refresh)
void goRefresh() {
webView.reload();
}
2 WebViewClient 和 WebChromeClient 区别
WebView主要负责解析渲染,WebViewClient 和WebChromeClient是用来辅助WebView。
2.1 WebViewClient
setWebClient就是帮助WebView处理各种通知、请求事件。
onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest
onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest
webView.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) { // Handle the error
Toast.makeText(getApplicationContext(), "网络连接失败 ,请连接网络。",
Toast.LENGTH_SHORT).show();
}
});
上面的代码处理了接收错误的情况。
2.2 WebChromeClient
setWebChromeClient辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等。
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
progressbar.setVisibility(View.GONE);
} else {
if (progressbar.getVisibility() == View.GONE) {
progressbar.setVisibility(View.VISIBLE);
}
progressbar.setProgress(newProgress);
}
super.onProgressChanged(view, newProgress);
}
});
3 WebView页面中播放了音频,退出Activity后音频仍然在播放
自己在做的时候看到别人提到这点,就做了一个实验。打开百度音乐,播放音乐,然后退出,果然仍然在播放。
/**
* 防止音频,视频结束后仍然播放!,Error: WebView.destroy() called while still attached!
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (webView != null) {
parentlayout.removeView(webView);
webView.removeAllViews();
webView.destroy();
}
}
开始直接webView.destory(),发现会报
WebView.destroy() called while still attached!
这个错误,需要在destory之前把webView从父容器中删除。
4 webview支持定位
这个是对接了一个网站,人家支持web定位,我这边没做。运营报给我的错误。
首先加上权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
添加对应的代码:
wSettings.setDatabaseEnabled(true);
String dir = this.getApplicationContext()
.getDir("database", Context.MODE_PRIVATE).getPath();
// 启用地理定位
wSettings.setGeolocationEnabled(true);
// 设置定位的数据库路径
wSettings.setGeolocationDatabasePath(dir);
// 开启DomStorage缓存
wSettings.setDomStorageEnabled(true);
//配置权限(同样在WebChromeClient中实现)
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
5 webview跳转到之前已经完成的功能
在一个大的Android项目中,由于客户端来不及更新和实现,经常会内嵌一些网页(在一些大型的互联网公司,PC的产品总是跑在客户端的前面),比如活动页面,通常可以内嵌用html5实现的页面,可以适配手机。但是这些网页中有好多链接,但是这些链接有些内容有是我们客户端已经实现的,比如有一个注册链接,其实客户端也实现了注册功能,我们不想再继续跳转到网页注册,而是打开客户端某个注册Activity,可以通过以下方式来实现
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
// view.loadUrl(url);
if (url != null && url.contains("login?")) {
Map<String, String> mapRequest = UrlUtils.URLRequest(url);
String userId = mapRequest.get("userid");
if (!StringUtils.textIsNull(userId)) {
Intent i = new Intent(webActivity.this,
LoginActivity.class);
Bundle bundle = new Bundle();
bundle.putString("userID", String.valueOf(userId));
i.putExtras(bundle);
startActivity(i);
finish();
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
6 js和原生交互
这个是我之前在做sdk开发的时候遇到的问题,也总结到这个里面把。
首先如果您在编写HTML5应用,需要在JS代码中访问Java中的函数,则您会用到WebView的addJavascriptInterface()函数。因为安全问题,在Android4.2中(如果应用的android:targetSdkVersion数值为17+)JS只能访问带有 @JavascriptInterface注解的Java函数。
之前,任何Public的函数都可以在JS代码中访问,而Java对象继承关系会导致很多Public的函数都可以在JS中访问,其中一个重要的函数就是 getClass()。然后JS可以通过反射来访问其他一些内容。通过引入 @JavascriptInterface注解,则在JS中只能访问 @JavascriptInterface注解的函数。这样就可以增强安全性。
如果您的应用android:targetSdkVersion数值为17或者大于17记得添加 @JavascriptInterface 注解。
之前,任何Public的函数都可以在JS代码中访问,而Java对象继承关系会导致很多Public的函数都可以在JS中访问,其中一个重要的函数就是 getClass()。然后JS可以通过反射来访问其他一些内容。通过引入 @JavascriptInterface注解,则在JS中只能访问 @JavascriptInterface注解的函数。这样就可以增强安全性。
如果您的应用android:targetSdkVersion数值为17或者大于17记得添加 @JavascriptInterface 注解。
// 网页中包含JavaScript内容需调用以下方法,参数为true
wSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsObject(), "demo");
final class JsObject {
@JavascriptInterface
public void clickOnAndroid() {
mHandler.post(new Runnable() {
@SuppressLint("JavascriptInterface")
public void run() {
webView.loadUrl("javascript:wave()");
}
});
}
}
对于的网页,我放在assets下面
<html>
<script >
function wave() {
document.getElementById("android").src="android_waving.png";
}
</script>
<body>
<a onClick="window.demo.clickOnAndroid()">
<img id="android" src="ic_launcher.png" mce_src="ic_launcher.png"/><br>
Click me!
</a>
<br/>
<br/>
<a href="http://test/login?userId=1234">login</a>
</body>
</html>
webview需要加进一个回调的代理类JsObject,并给它一个调用的名称:demo
js调用java方法
<a onClick="window.demo.clickOnAndroid()">
Java要调用js的方法,只需知道js的方法名称即可:
上面的例子显示出来了js调用java方法,然后java调用了js的方法,更换了图片。
webView.loadUrl("javascript:wave()");
7 安全的js
https://github.com/pedant/safe-java-js-webview-bridge
大家可以看下github的demo,我试了一下,对应的jsObject方法必须是static,感觉不太好使,网上没有具体的使用例子。
butterknife 配置参考:http://blog.csdn.net/hlllmr1314/article/details/36375273?utm_source=tuicool
demo地址:http://download.csdn.net/detail/jjdhshdahhdd/9056235