Android WebView使用总结
基本设置
getSettings().setPluginState(WebSettings.PluginState.ON);//支持插件
/**
* 输入法控制
*/
getSettings().setNeedInitialFocus(true);//当webview调用requestFocus时为webview设置节点
getSettings().setSupportMultipleWindows(true);//设置支持多窗口
/**
* 手机如果开启了缩放字体,网页显示的比例会失调
*/
getSettings().setTextZoom(100);//禁止系统缩放字体大小
/**
* 以下三项禁止设置,否则退出应用后会因为缩放组件没有及时取消广播而闪退
*/
//getSettings().setSupportZoom(true);//设置支持缩放
//getSettings().setBuiltInZoomControls(true);//设置支持缩放
//getSettings().setUseWideViewPort(true);//用户可以可以网页比例
getSettings().setLoadWithOverviewMode(true);// 缩放至屏幕的大小
getSettings().setJavaScriptEnabled(true);// 可以使用javaScriptEnalsed
getSettings().setJavaScriptCanOpenWindowsAutomatically(true);//js可以自动打开窗口
getSettings().setAllowFileAccess(true);//支持引用文件
getSettings().setAppCacheEnabled(true);//设置支持本地存储
getSettings().setDomStorageEnabled(true);//可以手动开启DOM Storage
if (Build.VERSION.SDK_INT >= 17) {// 不需要请求控制直接播放媒体文件
//即可以自动播放音乐
getSettings().setMediaPlaybackRequiresUserGesture(false);
}
setWebChromeClient(new MyWebChromeClient());// 设置浏览器可弹窗客户端设置
setWebViewClient有好处,典型用处是屏蔽广告,加载本地js,以及加载本地资源
屏蔽广告
APP的网页经常会被运营商劫持,加载一些它推送的广告出现在页面的底部,这这个广告其实不是自己的APP的,是被用户所在的 运营商强行加上去的,遇到这个问题是在恼火,可以通过拦截域名的方式屏蔽广告
setWebViewClient(new WebViewClient() {
@Override
@Deprecated
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
try {// API21之前屏蔽广告
String hostName = new URL(url).getHost();
if (hostName != null) {
if (checkHostName(hostName)) {//checkHostName:检查是不是自己APP的域名,不是则不允许加载
return super.shouldInterceptRequest(view, url);
} else {
Log.e("siwebview", "no mine host!" + hostName);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
//不是自己的APP域名,返回一个null,则屏蔽了广告
return new WebResourceResponse(null, null, null);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest
request) {//高版本屏蔽广告同理
String url = request.getUrl().toString();
if (Build.VERSION.SDK_INT >= 21) {// API21后,屏蔽广告
String hostName = request.getUrl().getHost();
if (hostName != null) {
if (checkHostName(hostName)) {
} else {
return new WebResourceResponse(null, null, null);
}
}
}
return super.shouldInterceptRequest(view, request);
}
/**
* 覆写此方法是为了让webview仅加载自己的页面
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
/**
* 页面加载开始
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
/**
* 页面加载完成(注意部分手机并不会执行该方法)
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
});加载本地JS
有的APP需要在网页里注入js,而这个js的注入是由客户端完成的,因为js里面包含的一些用户的信息等,无法通过网页结果获取.
可以在页面加载开始的时候注入,或页面加载完毕的时候注入
setWebViewClient(new WebViewClient() {
/**
* 页面加载开始
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
try {
InputStream inputStream = getContext().getAssets().open("android_qqsd.js");
String a = convertStreamToString(inputStream);
loadUrl("javascript:" + a + "");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 页面加载完成(注意部分手机并不会执行该方法)
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//加载完毕注入同理
}
});但是仅仅在页面加载开始和完成就注入js其实并不能保证一定注入成功.
开始不能注入成功,是因为网页才刚刚开始加载,功能没有设置好,并不能马上的接受js的注入
完成不能注入成功,是因为部分手机并不会执行完成方法.
故js的注入还需要设置setWebChromeClient(),以及通过js交互的方式来完成注入,在后文中会有讲解
加载本地资源
有时候网页的素材过大,需要将部分资源提前放置到本地APP,加载的时候则不需要请求网络,这样可以极大提高 网页的打开速度,此时就需要拦截网页的url的请求,加载本地的资源并返回
注意:所有的url请求并必须是相同的域名,否则会产生跨域行为.
setWebViewClient(new WebViewClient() {
@Override
@Deprecated
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return interceptRes(url);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest
request) {
String url = request.getUrl().toString();
return interceptRes(url);
}
});
/**
* url请求,本地加载
*
* @param url url请求
* @return 拦截结果
*/
private WebResourceResponse interceptRes(String url) {
if (url.contains("/res/") ) {
//假设请求url包含res资源目录,则表明是去想服务器请求资源,而本地
//已经包含了这个资源,则拦截这个请求,返回本地的资源
//注意返回一个WebResourceResponse则表示拦截,如果返回null则表示不拦截,正常请求服务器
WebResourceResponse webResourceResponse = null;
try {
int dianIndex = url.lastIndexOf(".");
if (dianIndex != -1) {//获取资源文件的格式,例如这是一个.png,还是.js,还是.mp3
String mime = url.substring(dianIndex + 1);
if (mime.equals("js")) {//MimeTypeMap没有js格式需要特殊处理
mime = "application/x-javascript";
} else if (mime.equals("ttf")) {//MimeTypeMap没有字体格式需要特殊处理
mime = "application/octet-stream";
} else{//其他格式可以MimeTypeMap获取其mime类型
mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(mime);
}
if (mime == null) {//假设没有获取到正常的mime类型,则设置为通用类型
mime = "*/*";
}
Log.e("siyehua-new", " mime: " + mime);
File file = new File(url);//获取本地的资源
if (!file.exists()) {
//假设本地没有这个资源,即开始没有下载好,则返回null
//表示不拦截,去请求服务器的资源
Log.e("siyehua", "file no found");
return null;
}
//一切正常,创建webResourceResponse
webResourceResponse = new WebResourceResponse(mime, "utf-8", new
FileInputStream(url));
}
} catch (Exception e) {
e.printStackTrace();
}
return webResourceResponse;
}
return null;
}设置ChromeClient
setWebChromeClient除了让浏览器可弹窗外,还可以监听网页的进度变化等.
setWebChromeClient(new MyWebChromeClient());// 设置浏览器可弹窗
/**
* 浏览器可弹窗
*
* @author Administrator
*/
final class MyWebChromeClient extends WebChromeClient {
/**
* js的注入,除了前面的方法,还可以听过监听该方法来注入
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress > 80) {//80%是一个大概的数字,实际上该注入方法有可能失败
try {
InputStream inputStream = getContext().getAssets().open("android_qqsd.js");
String a = convertStreamToString(inputStream);
loadUrl("javascript:" + a + "");
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 设置该方法网页的js可以弹出确认/取消对话框
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult
result) {
new AlertDialog.Builder(getContext()).setTitle("App Titler").setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).create().show();
return true;
}
//获得网页的标题,作为应用程序的标题进行显示
public void onReceivedTitle(WebView view, String title) {
MainActivity.this.setTitle(title);
}
}JS交互
js交互可是说是WebView最重要的手段,默认是禁止js行为的,因为js会带来安全问题
getSettings().setJavaScriptEnabled(true);// 可以使用javaScriptEnalsed
//jsObjectStr是在网页中调用的对象
addJavascriptInterface(new JsObject(), "jsObjectStr");
class JsObject {
// @JavascriptInterface是为了支4.2及以上的js交互
@JavascriptInterface
public void app_method() {
//对应的逻辑操作
}
}
//对应的网页代码:
jsObjectStr.app_method();本地的js注入,最保险的方式就是通过js交互的方法注入
其他自定义方法同理,js交互需要注意以下几个问题
- js是在网页线程,不是主线程,不能操作view
- 传参数时,只能传入基本类型以及String,不能传入json对象等其他复杂对象类型
- js安全问题
其他问题
-
Activity销毁时需要调用webView.destroy()才能销毁webview
-
Activity返回时,需要重新返回方法,来判断网页是否能返回
@Override public void onBackPressed() { if (siWebView.canGoBack()) { siWebView.goBack(); return; } super.onBackPressed(); }