一、简介
这部分主要介绍下 WebView,WebView 是一个用来显示 Web 网页的控件,继承自 AbsoluteLayout,和使用系统其他控件没什么区别,只是 WeView 控件方法比较多比较丰富。因为它就是一个微型浏览器,包含一个浏览器该有的基本功能,例如:滚动、缩放、前进、后退下一页、搜索、执行 Js等功能。
二、开始最简单的使用
2.1 加载一个最简单的页面
2.2 xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ly_web"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
</LinearLayout>
但是我们一般不是很支持直接在xml中静态页面写上 webview 控件,因为防止webview出现内存泄露,下文会提及到。
2.3 activity载入某宝页面
webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl(" https://www.taobao.com/");
2.4 在销毁activity的时候
@Override
protected void onDestroy() {
if (webview != null) {
webview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
webview.clearHistory();
((ViewGroup) webview.getParent()).removeView(webview);
webview.destroy();
webview = null;
}
super.onDestroy();
}
2.5 因为要加载网页页面 切记记得加上权限
<uses-permission android:name="android.permission.INTERNET"/>
三、webview的渲染
webview的渲染主要就介绍两个类,下面先简单的介绍使用方式
WebViewClient
主要帮助WebView处理各种通知、请求事件的,有以下常用方法
onPageFinished 页面请求完成
onPageStarted 页面开始加载
shouldOverrideUrlLoading 拦截url
onReceivedError 访问错误时回调,例如访问网页时报错404,在这个方法回调的时候可以加载错误页面
WebChromeClient
主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等,有以下常用方法
onJsAlert webview不支持js的alert弹窗,需要自己监听然后通过dialog弹窗
onReceivedTitle 获取网页标题
onReceivedIcon 获取网页icon
onProgressChanged 加载进度回调
1、创建 WebViewClient实例
private WebViewClient webViewClient = new WebViewClient() {
@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 boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//处理其他情况,比如国内不能访问google,拦截该url
return super.shouldOverrideUrlLoading(view, request);
}
};
2.创建WebChromeClient实例
private WebChromeClient webChromeClient =new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
//加载进度回调
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
//获取网页标题
super.onReceivedTitle(view, title);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//因为WebView不支持alert弹窗,在这个方法中用AlertDialog去弹窗
return super.onJsAlert(view, url, message, result);
}
};
3.设置 WebViewClient 和 WebChromeClient
wv_baidu.setWebViewClient(webViewClient);
wv_baidu.setWebChromeClient(webChromeClient);
四、Webview类常用方法
4.1 加载url
//方式1. 加载一个网页:
webView.loadUrl("http://www.google.com/");
//方式2:加载apk包中的html页面
webView.loadUrl("file:///android_asset/test.html");
//方式3:加载手机本地的html页面
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
第二种的html存放路径位于 app/src/build/assets
4.2 关于前进 / 后退网页
//是否可以后退
Webview.canGoBack()
//后退网页
Webview.goBack()
我们常用于在重写 物理控件返回的回调 onBackPressed 之中,避免一触摸返回键就退出
@Override
public void onBackPressed() {
if (webview.canGoBack()) {
//后退
webview.goBack();
} else {
//退出
finish();
}
}
4.3 webview.requestFocusFromTouch();//设置支持获取手势焦点
五、WebSettings类
//设置支持 webview js方法
webview.getSettings().setJavaScriptEnabled(true);
//打开 webview 插件
webview.getSettings().setPluginState(WebSettings.PluginState.ON);
//缓存处理
if (isConnectIsNomarl()) {
webview.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}
String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
webview.getSettings().setGeolocationDatabasePath(dir);
// 1. 设置缓存路径
webview.getSettings().setAppCacheMaxSize(4*1024*1024);
// 2. 设置缓存大小
webview.getSettings().setAppCacheEnabled(true);
//设置自适应屏幕,两者合用
webview.getSettings().setUseWideViewPort(true); //将图片调整到适合webview的大小
webview.getSettings().setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//缩放操作
webview.getSettings().setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webview.getSettings().setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webview.getSettings().setDisplayZoomControls(false); //隐藏原生的缩放控件
//其他细节操作
// webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webview.getSettings().setAllowFileAccess(true); //设置可以访问文件
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webview.getSettings().setLoadsImagesAutomatically(true); //支持自动加载图片
webview.getSettings().setDefaultTextEncodingName("utf-8");//设置编码格式
六、js调用安卓的方法
通过小demo简单介绍
6.1、自创建一个html页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// 由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello("凉凉");
}
function callJSOne(){
alert("Android调用了JS的callJS方法");
}
function callJSTwo(){
return "hello world";
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="callAndroid()">点击调用安卓代码</button>
<a href="tel://17875305749 ">拨打电话</a>
</br>
<iframe height=498 width=510 src='http://player.youku.com/embed/XMzM4NTQ3ODcwNA==' frameborder=0
allowfullscreen='true'></iframe>
</body>
</html>
6.2、使用 addJavascriptInterface 通过映射 ,使 js 拥有一个 java 的一个上下文引用。
使用案例 参数一 就是 上下文 ,参数二就是 引用
webview.addJavascriptInterface(new Webviewjs(WebViewDemoActivity.this), "test");
// 通过addJavascriptInterface()将Java对象test映射到JS对象
webview.addJavascriptInterface(new Webviewjs(WebViewDemoActivity.this), "test");
// 设置一下路径,标准格式如下
webview.loadUrl("file:///android_asset/myjs.html");
6.3、我们在html中已经给我们的button控件注册点击事件了
<button type="button" id="button1" onclick="callAndroid()">点击调用安卓代码</button>
function callAndroid(){
// 由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello("凉凉");
}
6.4 传入 WebViewDemoJS类
public class Webviewjs extends Activity{
private Context context;
public Webviewjs(Context context) {
this.context = context;
}
@JavascriptInterface
public void hello(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
七、安卓调用js的代码
(注意 想调用 js 必须在 onPageFinished 结束之后才够使用)
webview.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
btnOne.setEnabled(true);
btnTwo.setEnabled(true);
}
在这里我给大家简单介绍,还有涉及一个小技巧
7.1、先写js的方法。
function callJSOne(){
alert("Android调用了JS的callJS方法");
}
7.2、给我们的原生按钮设置监听这些,我就不多加啰嗦了,直接上如何引用,及其一个方法
我们在上文已经说到 webview不支持 alert 弹框,所以我们可以这样,用 我们安卓的弹框来进行代替。
webview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(WebViewDemoActivity.this);
b.setTitle("Alert");
// Log.e("onJsAlert: ", );
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
}
调用的方式就是通过 webview.loadUrl 来进行调用
case R.id.btn_one:
webview.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
webview.loadUrl("javascript:callJSOne()");
}
});
break;
为什么要通过post方法呢,因为在经常状态下我们都会独立的开一个进程给我们的webview使用,避免出现崩溃问题出现,导致整一个应用程序的崩溃。
你以为结束了吗,并没有,我这里还有一点小干货让你吸收。
其实 我们 不止 可以通过上面我介绍的那个方法来调用 js 方法 ,但是呢,我在这里不介绍,这是因为另一个方法并不支持 4.4以下的。但是那个方法又有反回值,我们可以获得他所返回给我们的数据。
这里我就是想来介绍一下我们如何通过 webview.loadUrl( ); 方法来获得 js 给我们的返回值 如下案例
function callJSTwo(){
return "hello world";
}
case R.id.btn_two:
webview.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
webview.loadUrl("javascript:alert(callJSTwo())");
}
});
我相信你看完已经明白了。
8、进度问题
webview.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
progress.setVisibility(View.GONE);
} else {
progress.setVisibility(View.VISIBLE);
}
}
9、视频播放横放问题
lyweb是webview的父控件。
@BindView(R.id.ly_web)
LinearLayout lyWeb;
//视频切换
private View myView = null;
//内核
private WebChromeClient chromeClient = null;
private WebChromeClient.CustomViewCallback myCallBack = null;
webview.setWebChromeClient(new WebChromeClient() {
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (myView != null) {
callback.onCustomViewHidden();
return;
}
//设置横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
myCallBack = callback;
//隐藏网页
lyWeb.removeView(webview);
//添加视频
lyWeb.addView(view);
myView = view;
}
@Override
public void onHideCustomView() {
if (myView == null) {
return;
}
//设置竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//隐藏视频
lyWeb.removeView(myView);
//添加网页
lyWeb.addView(webview);
myView = null;
myCallBack.onCustomViewHidden();
}
});
10、点击打电话,发邮箱等等等操作。
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http:") || url.startsWith("https:")) {
return false;
} else if (url.startsWith(WebView.SCHEME_TEL) ||
url.startsWith("sms:") ||
url.startsWith(WebView.SCHEME_MAILTO) ||
url.startsWith(WebView.SCHEME_GEO) ||
url.startsWith("maps:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
} catch (ActivityNotFoundException e) {
}
}
return true;
}
11、解决https出错的问题
webview.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
handler.proceed(); //表示等待证书响应
// handler.cancel(); //表示挂起连接,为默认方式
// handler.handleMessage(null); //可做其他处理
}
12、记得记得(避免出现泄露)
@Override
protected void onResume() {
super.onResume();
webview.getSettings().setJavaScriptEnabled(true);
webview.onResume();
}
@Override
protected void onStop() {
super.onStop();
webview.getSettings().setJavaScriptEnabled(false);
}
@Override
protected void onPause() {
super.onPause();
webview.onPause();
}
@Override
protected void onDestroy() {
if (webview != null) {
webview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
webview.clearHistory();
((ViewGroup) webview.getParent()).removeView(webview);
webview.destroy();
webview = null;
}
super.onDestroy();
}
13、坑
1.WebView内存泄漏
当你要用webview的时候,记得最好另外单独开一个进程去使用,并且当这个进程结束时,手动调用System.exit(0)。使用此方法,所有因为webview引发的资源无法释放等问题都可以解决
https://blog.csdn.net/qq_36523667/article/details/79053625
https://www.jianshu.com/p/b66c225c19e2
2.getSettings().setBuiltInZoomControls(true) 引发的crush
这个方法调用以后 如果你触摸屏幕 弹出那个提示框还没消失的时候 你如果activity结束了 就会报错了。3.0以上 4.4以下很多手机会出现这种情况所以为了规避他,我们通常是在activity的onDestroy方法里手动的将webiew设置成 setVisibility(View.GONE)
3.onPageFinished 函数到底有用没有?
这个函数并没有什么卵用,有的时候是提前结束,有的时候就迟迟无法结束,你信这个函数 还不如信上帝,甚至于onProgressChanged这个函数都比onPageFinished 要准一些,更加靠谱的方法是使用onprogresschange方法代替该方法的功能,当newProgress为100的时候,即是页面加载完成
4.后台无法释放js 导致耗电
如果webview加载的html里 有一些js 一直在执行比如动画之类的东西,如果此刻webview 挂在了后台这些资源是不会被释放 用户也无法感知。。。导致一直占有cpu 耗电特别快,所以大家记住了,如果遇到这种情况 请在onstop和onresume里分别把setJavaScriptEnabled();给设置成false和true。
webview加载网页的时候,会自动创建线程,如果如果使用不当,这些线程会永远在后台运行,导致你的应用耗电量居高不下,这个问题的解决方式是在activity的ondetory方法中销毁webview
5.前端最好不要使用了ES6语法
6.webview动态添加到其他布局的时候,在activity销毁的生命周期时,需要主动调用webview.removeallviews和webview的ondestory方法释放内存,否则会导致内存泄漏
7.webview硬件加速导致渲染问题,比如加载的时候会有闪屏现象,解决方式就是暂时关闭硬件加速
8.webview导致内存溢出的原因,主要是因为内部类持有外部类的引用导致外部类无法释放的问题
9.关于webview.onPause()
通知内核尝试停止所有处理,如动画和地理位置,但是不能停止Js,如果想全局停止Js,可以调用**pauseTimers()**全局停止Js,调用onResume()恢复。
完结~
https://blog.csdn.net/weixin_37418246/article/details/82987594
https://www.jianshu.com/p/3c94ae673e2a
https://www.jianshu.com/p/345f4d8a5cfa
https://www.jianshu.com/p/5e7075f4875f
https://www.jianshu.com/p/3a345d27cd42
https://blog.csdn.net/qq_30379689/article/details/51898640
https://blog.csdn.net/lijizhi19950123/article/details/77914601