Webview在我们平时开发中十分常用,几乎项目里面都会有嵌套网页。但是Webview可不是像我们平时loadUrl()就完了,它还有很多属性方法我们平时可能就没有开发出来。
最基本功能使用:
//加载一个远程网页
webView.loadUrl("https://www.baidu.com");
// 加载assets中资源
webView.loadUrl("file:///android_asset/web.html");
//加载sdcard中资源
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
-
WebSettings类有哪些属性可以设置?
WebSettings webSettings = webView.getSettings();
//设置支持javascript
webSettings.setJavaScriptEnabled(true);
//支持插件
webSettings.setPluginsEnabled(true);
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
webSettings.setTextZoom(100);//字体百分比,替代原API:setTextSize
//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏屏幕中的虚拟缩放按钮
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //开启webview中缓存
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setBlockNetworkImage(true); //阻塞图片加载
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
//优先使用缓存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
-
Webview属性
webView.clearCache(true); 清除缓存
webView.clearHistory(); 清除历史记录
webView.reload(); 重新加载
Https 加载 Http 混合模式
当 WebView 加载 https 的地址中有 http 的地址时(比如 https 地址含有 http 的图片) WebView 无法加载 http 的资源,可使用setMixedContentMode方法设置。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
-
页面监听与拦截
1.WebViewClient
WebViewClient主要用来处理请求和加载页面监听。示例如下:
public class MyWebViewClient extends WebViewClient {
private Context context;
public MyWebViewClient(Context context) {
this.context = context;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//用户可选择是否拦截加载 URL
//如果返回值为 true,拦截 WebView 加载 url,false 允许 WebView 加载 url
//所以在实际项目中,可以在这里处理自定义的一些跳转协议。
if (url.startsWith("tel:")) { //比如点击到已经定义好的 url 协议 电话号码 tel:// 时,那么可以在这里做拦截,跳转到系统拨号界面。
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
@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);
}
@Override
public void onLoadResource(WebView view, String url) {
//在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
super.onLoadResource(view, url);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
//App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面
// view.loadUrl("file:///android_assets/error_handle.html");
super.onReceivedError(view, request, error);
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
//响应服务器返回的 Http 错误,当一个 http 正常响应时,状态码会是 200,当状态码异常时可以用该方法监听。
int code = errorResponse.getStatusCode();
switch (code) {
case 400:
// 重新登录
break;
}
super.onReceivedHttpError(view, request, errorResponse);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//SSL 证书验证错误
super.onReceivedSslError(view, handler, error);
}
}
-
WebChromeClient
WebChromeClient主要辅助WebView处理Javascript的对话框、网站图标、网站标题、加载进度等。
public class MyWebChromeClient extends WebChromeClient {
private Context context;
public MyWebChromeClient(Context context) {
this.context = context;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
//这个方法会在网页加载过程中多次触发,当 newProgress = 100 时,可以认为网页加载完成。这个方法比 onPageFinished 更为准确,一般用来实现自定义进度条加载。
Log.i("minfo", "当前加载进度: " + newProgress);
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
//显示页面标题 title为该页面标题
Log.i("minfo", "页面title: " + title);
super.onReceivedTitle(view, title);
}
/**
* 页面提示框
* @param message alert 弹出窗口中的提示信息(提示或警告信息对话框,仅一个确认按钮)
* @param result 向网页中的 Javascript 代码反馈本次操作结果(result.confirm 代表点击了确定按钮,result.cancel 代表点击了取消按钮)
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//页面提示框 onJsAlert()
new AlertDialog.Builder(context)
.setTitle("JsAlert")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setCancelable(false)
.show();
return super.onJsAlert(view, url, message, result);
}
/**
* 页面选择框
* @param message confirm 弹出窗口中的提示信息(确认对话框,有确认、取消两个按钮)
* @param result 向网页中的 Javascript 代码反馈本次操作结果(result.confirm 代表点击了确定按钮,result.cancel 代表点击了取消按钮)
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
}
-
Webview内存泄漏解决
1、在使用webview时不用xml布局中引用,在代码中创建并用viewgroup调用addView()的方式。
2、在使用Activity的onDestroy方法中,让webview加载空,移除webview,webview销毁并置null。
@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
-
Webview设置缓存机制
WebSettings settings = webView.getSettings();
//设置缓存路径
String cacheDirPath = getFilesDir().getAbsolutePath()+"cache/";
settings.setAppCachePath(cacheDirPath);
//设置缓存大小
settings.setAppCacheMaxSize(20*1024*1024);
//开启AppCache存储机制
settings.setAppCacheEnabled(true);
-
启动指定浏览器
在Android程序中我们可以通过发送显式Intent来启动指定的浏览器。例如,启动手机自带浏览器。
Intent intent =newIntent();
intent.setAction("android.intent.action.VIEW");
Uri content_url =Uri.parse("https://www.baidu.com");
intent.setData(content_url);
intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");
startActivity(intent);
启动其他浏览器,只要修改以intent.setClassName(package,activity)中2个参数,对应的应用浏览器程序packagename和要启动的activity即可启动其他浏览器。
uc浏览器 | com.uc.browser | com.uc.browser.ActivityUpdate |
---|---|---|
opera浏览器 | com.opera.mini.android | com.opera.mini.android.Browser |
qq浏览器 | com.tencent.mtt | com.tencent.mtt.MainActivity |
Webview基本功能总结如上,最后,还有其他功能Webview与Android交互。
问题:
-
WebView 的 addJavascriptInterface()的作用
webView.addJavascriptInterface(this, "android")
-定义一个与 JS 对象映射关系的 Android 类:AndroidtoJs:
1.定义 JS 需要调用的方法,被 JS 调用的方法必须加入@JavascriptInterface
注解。
2.通过 addJavascriptInterface()将 Java 对象映射到 JS 对象。
-
前进 / 后退网页
//是否可以后退
Webview.canGoBack()
//后退网页
Webview.goBack()
//是否可以前进
Webview.canGoForward()
//前进网页
Webview.goForward()
//以当前页面为准,前进或者后退到历史记录中指定的steps页面数
//如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(int steps)
-
按系统back键怎么控制网页后退而不是直接关闭webview?
在当前展示Webview的Activity中处理Back事件,监听系统back键点击,在其中调用webview.goBack()方法。
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
webview cookie管理
什么是cookie:最简单理解就是由http衍生出来的一种特殊的浏览器的缓存,特点是具有时效性、账户相关性、存储在客户端等。Android 中Cookie的管理相关:说到cookie的管理,其实本质上就是数据的存储问题。在早期的cookie是由CookieSyncManager进行管理的,但是在API 21 之后CookieSyncManager被抛弃了,换成了CookieManager来进行管理。
Android中Cookie的存储:项目中使用 WebView 其实会自动将 Cookie 保存在本地数据库中。保存是路径为 data/data/package_name/app_WebView/Cookies 虽然不是 .db 结尾的,实际就是一个 .db 文件
webview有一个CookieManager这个类,他是专门管理cookie的,这个类可以设置一个或多个cookie,而且当你在里面设置好cookie以后接口会自动根据你设置时的url来使用。
public class CookieHandle {
private CookieManager cookieManager;
public void setCookies(Activity context, String url) {
HttpCookie cookie = new HttpCookie("name", "value");
cookie.setDomain("baidu.com"); //设置域名
cookie.setPath("files"); //设置域名下的path
cookie.setMaxAge(10000); //设置过期时间
// 调用CookieManager 的方法设置cookie
// 具有相同的 host 和 path 和 name 的任何现有的 Cookie 将会被替换为新的 Cookie
CookieManager.getInstance().setCookie(url, cookie.toString());
}
/**
* 删除cookie操作:底层实现是异步清除数据库的记录
*/
public void deleteCookie() {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush(); //这个flush()方法就是立即同步cookie的操作
}
/**
* 设置是否保存cookie
*/
fun setAcceptCookies(accept: Boolean) {
CookieManager.getInstance().setAcceptCookie(accept)
}
/**
* 清理cookie
*/
fun clearCookie() {
CookieManager.getInstance().removeAllCookie()
}
/**
* 获取某个网站cookie
*/
fun getCookie(url: String) {
CookieManager.getInstance().getCookie(url)
}
/**
* 同步cookie
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun synchCookie() {
CookieManager.getInstance().flush()
}
/**
* 清理webview网站账号密码
*/
@RequiresApi(Build.VERSION_CODES.ECLAIR_MR1)
fun clearPasswords(context: Context) {
var db = WebViewDatabase.getInstance(context)
db.clearUsernamePassword()
db.clearHttpAuthUsernamePassword()
}
@RequiresApi(Build.VERSION_CODES.ECLAIR_MR1)
fun clearDataBase() {
WebStorage.getInstance().deleteAllData()
}
fun clearCache() {
WebIconDatabase.getInstance().removeAllIcons()
}
/**
* 清理缓存
*/
fun clearAllData(context: Context) {
clearCookie()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
clearPasswords(context)
clearDataBase()
}
clearCache()
}
}
注:只有cookie的domain和path与请求的URL匹配才会发送这个cookie。
WebView调用本地相册
1.重写方法(WebChromeClient类中的onShowFileChooser)
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
context.startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
return true;
}
2.在调起的activity中重写onActivityResult方法获取data中选择的本地文件。
参考:
https://juejin.cn/post/7152861867973705758
https://www.jianshu.com/p/0d1bd9443caa