一、简介
WebView是Android系统中的原生控件,其主要功能与前端页面进行响应交互。
Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。
WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。
二、WebView相关配置:WebSettings
WebSettings webSettings = webView.getSettings();
//常用操作
webSettings.setJavaScriptEnabled(true); -> 是否开启JS支持
webSettings.setPluginsEnabled(true); -> 是否开启插件支持
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); -> 是否允许JS打开新窗口
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); -> //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); -> 缩放至屏幕大小
//缩放操作
webSettings.setSupportZoom(true); -> 是否支持缩放
webSettings.setBuiltInZoomControls(true); -> 是否支持缩放变焦,前提是支持缩放
webSettings.setDisplayZoomControls(false); -> 是否隐藏缩放控件
webSettings.setAllowFileAccess(true); -> 是否允许访问文件
webSettings.setDomStorageEnabled(true); -> 是否节点缓存
webSettings.setDatabaseEnabled(true); -> 是否数据缓存
webSettings.setAppCacheEnabled(true); -> 是否应用缓存
webSettings.setAppCachePath(uri); -> 设置缓存路径
webSettings.setMediaPlaybackRequiresUserGesture(false); -> 是否要手势触发媒体
webSettings.setTextZoom(100); -> 设置文本缩放的百分比
webSettings.setMinimumFontSize(8); -> 设置文本字体的最小值(1~72)
webSettings.setDefaultFontSize(16); -> 设置文本字体默认的大小
webSettings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); -> 按规则重新布局
webSettings.setLoadsImagesAutomatically(false); -> 是否自动加载图片
webSettings.setDefaultTextEncodingName("UTF-8"); -> 设置编码格式
webSettings.setNeedInitialFocus(true); -> 是否需要获取焦点
webSettings.setGeolocationEnabled(false); -> 设置开启定位功能
webSettings.setBlockNetworkLoads(false); -> 是否从网络获取资源
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
三、WebViewClient类
作用:用于处理各种通知,以及请求事件
WebViewClient webViewClient = new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
}
@Override
public void onPageFinished(WebView view, String url) {
}
@Override
public boolean onLoadResource(WebView view, String url) {
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl){
view.loadUrl("file:///android_assets/error.html"); -> assets目录下放置文件
}
webView.setWebViewClient(webViewClient);
各回调方法作用:
onPageStarted():页面开始加载时调用,这时候可以显示加载进度条
onPageFinished():页面完成加载时调用,这时候可以隐藏加载进度条
onLoadResource():页面每次加载资源时调用。每一个资源(比如图片)的加载都会调用一次。
shouldOverrideUrlLoading():给WebView提供时机,让其选择是否对UrlLoading进行拦截。
onReceivedError():页面加载发生错误时调用,这时候可以跳转到自定义的错误提醒页面,总比系统默认的错误页面美观,优化用户体验。
onReceivedHttpError():页面加载请求时发生错误。
onReceivedSslError():页面加载资源时发生错误。
shouldOverrideKeyEvent():覆盖按键默认的响应事件,这时候可以根据自身的需求在点击某些按键时加入相应的逻辑。
onScaleChanged():页面的缩放比例发生变化时调用,这时候可以根据当前的缩放比例来重新调整WebView中显示的内容,如修改字体大小、图片大小等。
shouldInterceptRequest():可以根据请求携带的内容来判断是否需要拦截请求。
四、WebChromeClient类
作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题,设置加载进度条等等
onProgressChanged():页面加载进度发生变化时调用,可以通过该方法实时向用户反馈加载情况,如显示进度条等。
webview.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress < 100) {
String progress = newProgress + "%";
progress.setText(progress);
} else {
}
});
onReceivedIcon():接收Web页面的图标,可以通过该方法把图标设置在原生的控件上。
前三个比较常用
onProgressChanged():加载进度发生变化时调用,可以通过该方法设置加载进度条.
onReceivedIcon():接收Web页面的图标,可以通过该方法把图标设置在原生的控件上
onReceivedTitle():接收Web页面的标题,可以通过该方法把图标设置在原生的控件上.
onPermissionRequest():Web页面请求Android权限时调用。
onPermissionRequestCanceled():Web页面请求Android权限被取消时调用。
onShowFileChooser():Web页面上传文件时调用。
getVideoLoadingProgressView():自定义媒体文件播放加载时的进度条。
getDefaultVideoPoster():设置媒体文件默认的预览图。
onShowCustomView():媒体文件进入全屏时调用。
onHideCustomView():媒体文件退出全屏时调用。
五、Android原生与H5交互
交互方式:
JAVA调用JS代码
方式一: 使用WebView的loadUrl()方法,以loadUrl(script)的方式调用。
方式二: 使用WebView的evaluateJavascript()方法。
JS调用JAVA代码
方式一: 使用WebView的addJavascriptInterface()方法注入对象。
方式二: 使用WebViewClient 的shouldOverrideUrlLoading()方法回调拦截请求。
方式三: 重写 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息。
Java调用Js:
假设前端有下面这个方法:
function javaCallJsNoParam(){
document.getElementById("result").innerHTML= 'JAVA调用JS成功!';
}
java这样调用:
webView.loadUrl("javascript:javaCallJsNoParam()");
或者:
webView.evaluateJavascript(javascript, new ValueCallback<String>() {
@Override
public void onReceiveValue(String response) {
//response为响应数据
}
}
});
JS调用Java:
主要方法:
public void addJavascriptInterface (Object object, String name)
Object: 要注入到WebView的JavaScript上下文中的Java对象。不能为空。
String: 该名称用于在JS中表示注入对象,不能为空。
作用:将提供的Java对象注入到此WebView中。使用提供的名称将对象注入到网页的所有框架中,包括所有iframes。这允许从JavaScript访问Java对象的方法。
示例:
class JsObject {
//1.要被JS调用的方法添加@JavascriptInterface
@JavascriptInterface
public String javaMethod() {
return "injectedObject";
}
}
webview.getSettings().setJavaScriptEnabled(true);
//2、注入java对象,后面的字符串也需与前端约定好
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadUrl(url);
六、常见踩坑经历
6.1.WebView的内存泄露
问题描述:
webview内存泄露的情况还是很严重的,尤其是当你加载的页面比较庞大的时候。
解决方案:
1 单独为WebView所在的activity开一个进程
<---你的WebView所在的activity->
<activity
android:name="com.processkill.B"
android:process="com.xxx.xxx" //为WebView所在的activity单独开一个进程
android:label="@string/app_name" >
</activity>
onDestroy中:
@Override
public void onDestroy() {
android.os.Process.killProcess(android.os.Process.myPid());
super.onDestroy();
}
2.在代码中创建和销毁WebView
1.不要在xml中使用WebView,通过一个ViewGroup,使用代码动态往ViewGroup里addView(webview),这样可以在onDestory()里销毁掉webview及时清理内存。
2.创建webview需要使用applicationContext而不是activity的context,销毁时不再占有activity对象
3.结束时需要及时销毁WebView
创建:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(layoutParams);
mLayout.addView(mWebView);
}
销毁:
@Override
protected void onDestroy() {
if (mWebView != null) {
//移除WebView
mLayout.removeAllViews();
//或者这么移除
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.stopLoading();
// 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
//同时可以避免webView加载的一些html里的js在后台持续耗电问题
mWebView.getSettings().setJavaScriptEnabled(false);
//销毁VebView
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
3.使用腾讯的X5WebView,相关创建销毁问题也需要自己注意。
6.2.WebView shouldOverrideUrlLoading功能误区
网上常见错误解释:阻止WebView调用系统浏览器
若想让WebView在loadUrl时,不调用系统浏览器,设置自定的WebViewClient即可。
webView.setWebViewClient(new WebViewClient());
shouldOverrideUrlLoading接口:
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// WebView不加载该Url
return true;
}
});
```java
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// WebView加载该Url
return false;
}
});
该接口返回值的解释:
If a WebViewClient is provided, returning true causes the current WebView to abort loading the URL,
while returning false causes the WebView to continue loading the URL as usual.
总的来说:
1.若没有设置 WebViewClient 则由系统(Activity Manager)处理该 url,通常是使用浏览器打开或弹出浏览器选择对话框,让用户选择用哪个浏览器打开。
2.若设置 WebViewClient 且该方法返回 true ,则说明由应用的代码处理该 url,WebView 不加载该url,也就是程序员自己做处理。
3.若设置 WebViewClient 且该方法返回 false,则说明由 WebView 处理该 url,即用 WebView 加载该 url。
6.3. WebView监听加载完成
onPageFinished,尽量不要使用这个函数,会多次调用,通过这个函数基本上监听不到加载完成,参考http://stackoverflow.com/questions/3149216/how-to-listen-for-a-webview-finishing-loading-a-url-in-android
使用WebChromeClient里onProgressChanged这个方法来监听,可能都比onPageFinished要好点,用这个方法判断是否加载完成,不过会触发多次,就是会出现多次newProgress==100的情况。可以用一个boolean变量控制一下。
boolean isFirst=true;
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
isFirst=false;
...
}
}
});
6.4. 监听WebView滑动到底端的问题
我之前写过一个:https://blog.csdn.net/qq_34512207/article/details/115760720?spm=1001.2014.3001.5501
6.5.WebView返回上一页
public void myOnclick() {
// 返回按钮,返回到上一网页
llBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(webView.canGoBack()){
webView.goBack();
}else {
finish();
}
}
});
}
/**
* 返回键返回上一网页
*/
@Override
public void onBackPressed() {
if(webView.canGoBack()){
webView.goBack();
}else {
finish();
}
}
6.6.WebView在Android 9.0 以上,多进程报错问题
报错如下:
java.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported.
即:不支持同时多个进程中WebView使用具有相同数据目录的WebView资源。
解决:在application的onCreate初始化中添加以下代码:
/**
* 为webView设置目录后缀
* @param context
*/
public void initWebViewDataDirectory(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
String processName = getProcessName(context);
LogUtils.e(TAG, "processName = " + processName);
LogUtils.e(TAG, "getPackageName = " + context.getPackageName());
if (!context.getPackageName().equals(processName)) {//判断是否是默认进程名称
WebView.setDataDirectorySuffix(processName);
}
}
}
/**
* 得到进程名称
* @param context
* @return
*/
public String getProcessName(Context context) {
try {
if (context == null)
return null;
ActivityManager manager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo :
manager.getRunningAppProcesses()) {
if (processInfo.pid == android.os.Process.myPid()) {
return processInfo.processName;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
建议:初始化放到attachBaseContext方法中。