Android之 WebView的使用

article/2025/9/29 6:29:28

一 简介

1.1 WebView是用来展示网页的控件,底层是google的WebKit的引擎。

比起苹果的WebView,webkit一些不足地方:

  • 不能支持word等文件的预览
  • 纯标签加载,并不支持所有标签的加载
  • 不支持文件的下载,图片的放大,都要单独处理

1.2 其它Web引擎,腾讯的webx5,其功能比WebKit要强大些,支持常见文件格式的预览(word文档,excel表格等),还支持文件的下载。

官方地址: https://x5.tencent.com/tbs/

1.3 WebView大部分场合还是能满足需求的,用官方Webkit就可以了

二 WebView的使用

2.1 初始化webview组件

xml里面初始化

<WebViewandroid:id="@+id/webview"android:layout_width="match_parent"android:layout_height="match_parent" />

获取动态初始化

WebView webView = new WebView(this);
llWebview.addView(webView);

2.2 配置WebSettings

//声明WebSettings子类
WebSettings webSettings = webView.getSettings();//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);  //支持插件
webSettings.setPluginsEnabled(true); //设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小//缩放操作
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.setDefaultTextEncodingName("utf-8");//设置编码格式

2.3 加载网页链接或加载标签

加载网页链接

//加载http链接:http://www.google.com/
//加载assets链接:file:///android_asset/test.html
//加载本地存储链接:http://www.google.com/
webView.loadUrl("http://www.google.com/");

加载标签

String goods_content="<p>我的第一个段落。</p>";
webView.loadDataWithBaseURL(null, WebUtil.getHtmlData(goods_content), "text/html", "utf-8", null);public static String getHtmlData(String bodyHTML) {String head = "<head>" +"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"> " +"<style>div,p,img{max-width: 100%; width: 100% !important; height: auto !important;}" +"body {" +"margin-right:8px;" +//限定网页中的文字右边距为15px(可根据实际需要进行行管屏幕适配操作)"margin-left:8px;" +//限定网页中的文字左边距为15px(可根据实际需要进行行管屏幕适配操作)"margin-top:8px;" +//限定网页中的文字上边距为15px(可根据实际需要进行行管屏幕适配操作)"font-size:16px;" +//限定网页中文字的大小为40px,请务必根据各种屏幕分辨率进行适配更改"word-wrap:break-word;" +//允许自动换行(汉字网页应该不需要这一属性,这个用来强制英文单词换行,类似于word/wps中的西文换行)"}" +"p { margin: 0; }" +"</style>" +"</head>";return "<html>" + head + "<body>" + bodyHTML + "</body><ml>";
}

2.4 设置WebViewClient,来处理通知和请求事件

常规用法,复写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示

webView.setWebViewClient(new WebViewClient(){@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {view.loadUrl(url);return true;}
});

完整用法

webViewClient = new WebViewClient() {/*** shouldOverrideUrlLoading* <p>* 当加载的网页需要重定向的时候就会回调这个函数告知我们应用程序是否需要接管控制网页加载,如果应用程序接管,*并且return true意味着主程序接管网页加载,如果返回false让webview自己处理。* </p>* 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            即将要被加载的url* @return true 当前应用程序要自己处理这个url, 返回false则不处理。 注:"post"请求方式不会调用这个回调函数*/@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {if (Uri.parse(url).getHost().equals("www.baidu.com")) {Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);return true;}return false;}/*** onPageStarted 当内核开始加载访问的url时,会通知应用程序,对每个main frame* 这个函数只会被调用一次,页面包含iframe或者framesets 不会另外调用一次onPageStarted,* 当网页内内嵌的frame 发生改变时也不会调用onPageStarted。* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            即将要被加载的url* @param favicon*            如果这个favicon已经存储在本地数据库中,则会返回这个网页的favicon,否则返回为null。*/@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon) {// TODO Auto-generated method stubsuper.onPageStarted(view, url, favicon);Log.i(TAG, "onPageStarted:页面开始加载");}/*** onPageFinished 当内核加载完当前页面时会通知我们的应用程序,这个函数只有在main* frame情况下才会被调用,当调用这个函数之后,渲染的图片不会被更新,如果需要获得新图片的通知可以使用@link* WebView.PictureListener#onNewPicture。 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            即将要被加载的url*/@Overridepublic void onPageFinished(WebView view, String url) {// TODO Auto-generated method stubsuper.onPageFinished(view, url);Log.i(TAG, "onPageStarted:页面加载结束");}/*** onLoadResource 通知应用程序WebView即将加载url 制定的资源* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            即将加载的url 资源*/@Overridepublic void onLoadResource(WebView view, String url) {// TODO Auto-generated method stubsuper.onLoadResource(view, url);Log.i(TAG, "onLoadResource:加载资源指定的网址");}/*** shouldInterceptRequest* 通知应用程序内核即将加载url制定的资源,应用程序可以返回本地的资源提供给内核,若本地处理返回数据,内核不从网络上获取数据。* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            raw url 制定的资源* @return 返回WebResourceResponse包含数据对象,或者返回null*/@Overridepublic WebResourceResponse shouldInterceptRequest(WebView view, String url) {// TODO Auto-generated method stubLog.i(TAG, "shouldInterceptRequest");return super.shouldInterceptRequest(view, url);}/*** onReceivedError* <p>* 当浏览器访问制定的网址发生错误时会通知我们应用程序 参数说明:* </p>* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param errorCode*            错误号可以在WebViewClient.ERROR_* 里面找到对应的错误名称。* @param description*            描述错误的信息* @param failingUrl*            当前访问失败的url,注意并不一定是我们主url*/@Overridepublic void onReceivedError(WebView view, int errorCode,String description, String failingUrl) {// TODO Auto-generated method stubsuper.onReceivedError(view, errorCode, description, failingUrl);view.loadUrl("file:///android_asset/error.html");Log.i(TAG, "onReceivedError");}/*** 如果浏览器需要重新发送POST请求,可以通过这个时机来处理。默认是不重新发送数据。 参数说明* * @param view*            接收WebViewClient的webview* @param dontResend*            浏览器不需要重新发送的参数* @param resend*            浏览器需要重新发送的参数*/@Overridepublic void onFormResubmission(WebView view, Message dontResend,Message resend) {// TODO Auto-generated method stubsuper.onFormResubmission(view, dontResend, resend);Log.i(TAG, "onFormResubmission");}/*** doUpdateVisitedHistory* 通知应用程序可以将当前的url存储在数据库中,意味着当前的访问url已经生效并被记录在内核当中。这个函数在网页加载过程中只会被调用一次。* 注意网页前进后退并不会回调这个函数。* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            当前正在访问的url* @param isReload*            如果是true 这个是正在被reload的url*/@Overridepublic void doUpdateVisitedHistory(WebView view, String url,boolean isReload) {// TODO Auto-generated method stubsuper.doUpdateVisitedHistory(view, url, isReload);Log.i(TAG, "doUpdateVisitedHistory");}/*** 当网页加载资源过程中发现SSL错误会调用此方法。我们应用程序必须做出响应,是取消请求handler.cancel(),还是继续请求handler.* proceed();内核的默认行为是handler.cancel();* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param handler*            处理用户请求的对象。* @param error*            SSL错误对象* */@Overridepublic void onReceivedSslError(WebView view, SslErrorHandler handler,SslError error) {// view.loadUrl("file:///android_asset/error.html");// TODO Auto-generated method stubsuper.onReceivedSslError(view, handler, error);Log.i(TAG, "onReceivedSslError");}/*** onReceivedHttpAuthRequest 通知应用程序WebView接收到了一个Http* auth的请求,应用程序可以使用supplied 设置webview的响应请求。默认行为是cancel 本次请求。* * * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param handler*            用来响应WebView请求的对象* @param host*            请求认证的host* @param realm*            认真请求所在的域*/@Overridepublic void onReceivedHttpAuthRequest(WebView view,HttpAuthHandler handler, String host, String realm) {// TODO Auto-generated method stubsuper.onReceivedHttpAuthRequest(view, handler, host, realm);Log.i(TAG, "onReceivedHttpAuthRequest");}/*** shouldOverrideKeyEvent* 提供应用程序同步一个处理按键事件的机会,菜单快捷键需要被过滤掉。如果返回true,webview不处理该事件,如果返回false,* webview会一直处理这个事件,因此在view 链上没有一个父类可以响应到这个事件。默认行为是return false;* * * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param event*            键盘事件名* @return 如果返回true,应用程序处理该时间,返回false 交有webview处理。*/@Overridepublic boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {Log.i(TAG, "shouldOverrideKeyEvent");// TODO Auto-generated method stubreturn super.shouldOverrideKeyEvent(view, event);}/*** 通知应用程序webview 要被scale。应用程序可以处理改事件,比如调整适配屏幕。*/@Overridepublic void onScaleChanged(WebView view, float oldScale, float newScale) {// TODO Auto-generated method stubsuper.onScaleChanged(view, oldScale, newScale);Log.i(TAG, "onScaleChanged");}/*** onReceivedLoginRequest 通知应用程序有个自动登录的帐号过程* * 参数说明:* * @param view*            请求登陆的webview* @param realm*            账户的域名,用来查找账户。* @param account*            一个可选的账户,如果是null 需要和本地的账户进行check, 如果是一个可用的账户,则提供登录。* @param args*            验证制定参数的登录用户*/@Overridepublic void onReceivedLoginRequest(WebView view, String realm,String account, String args) {// TODO Auto-generated method stubsuper.onReceivedLoginRequest(view, realm, account, args);Log.i(TAG, "onReceivedLoginRequest");}
});

2.5 设置WebChromeClient,辅助WebVlew处理/avascrlpt的对话框,网站图标,网站tltle,加载进度等

常规用法,加载页面缓冲进度

webView.setWebChromeClient(new WebChromeClient() {@Overridepublic void onProgressChanged(WebView view, int newProgress) {if (newProgress >=80) {llView.setVisibility(View.GONE);} else {llView.setVisibility(View.VISIBLE);}super.onProgressChanged(view, newProgress);}
});

完整用法

webView.setWebChromeClient(new WebChromeClient() {/*** onProgressChanged 通知应用程序当前网页加载的进度。* * 参数说明:* * @param view*            接收WebChromeClient的的webview实例* @param newProgress*            webview接受的进度*/@Overridepublic void onProgressChanged(WebView view, int newProgress) {// TODO Auto-generated method stubsuper.onProgressChanged(view, newProgress);if (newProgress <= 100) {Log.i(TAG, newProgress + "===onProgressChanged===");}}/*** 当document 的title变化时,会通知应用程序* * * 参数说明:* * @param view*            接收WebViewClient的webview实例* @param title*            document新的title*/@Overridepublic void onReceivedTitle(WebView view, String title) {// TODO Auto-generated method stubsuper.onReceivedTitle(view, title);Message message = new Message();message.what = 100;message.obj = title;handler.sendMessage(message);}/*** 当前页面有个新的favicon时候,会回调这个函数。 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param icon*            当前页面的favicon 注:很多时间不会跳转到此回调函数,因为很多网站设置了icon,没有设置favicon,*/@Overridepublic void onReceivedIcon(WebView view, Bitmap icon) {// TODO Auto-generated method stubsuper.onReceivedIcon(view, icon);Message message = new Message();message.what = 200;message.obj = icon;handler.sendMessage(message);}/*** 通知应用程序 apple-touch-icon的 url* * 参数说明:* * @param view*            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new*            MyAndroidWebViewClient()),即是这个webview。* @param url*            apple-touch-icon 的服务端地址* @param precomposed*            如果precomposed 是true 则touch-icon是预先创建的* *            Tips* *            如果应用程序需要这个icon的话, 可以通过这个url获取得到 icon。*/@Overridepublic void onReceivedTouchIconUrl(WebView view, String url,boolean precomposed) {// TODO Auto-generated method stubsuper.onReceivedTouchIconUrl(view, url, precomposed);Log.i(TAG, "====onReceivedTouchIconUrl====");}/*** webview请求得到focus,发生这个主要是当前webview不是前台状态,是后台webview。*/@Overridepublic void onRequestFocus(WebView view) {// TODO Auto-generated method stubsuper.onRequestFocus(view);Log.i(TAG, "====onRequestFocus====");}/*** 覆盖默认的window.alert展示界面,*/@Overridepublic boolean onJsAlert(final WebView view, String url, String message,JsResult result) {final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());builder.setTitle("对话框").setMessage(message).setPositiveButton("确定", null);builder.setOnKeyListener(new OnKeyListener() {public boolean onKey(DialogInterface dialog, int keyCode,KeyEvent event) {Log.v("onJsAlert", "keyCode==" + keyCode + "event=" + event);return true;}});// 禁止响应按back键的事件builder.setCancelable(false);AlertDialog dialog = builder.create();dialog.show();result.confirm();// 因为没有绑定事件,需要强行confirm,否则页面会变黑显示不了内容。return true;// return super.onJsAlert(view, url, message, result);}/*** 覆盖默认的window.confirm展示界面,*/@Overridepublic boolean onJsConfirm(final WebView view, String url, String message,final JsResult result) {final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());builder.setTitle("对话框").setMessage(message).setPositiveButton("确定", new OnClickListener() {public void onClick(DialogInterface dialog, int which) {result.confirm();}}).setNeutralButton("取消", new OnClickListener() {public void onClick(DialogInterface dialog, int which) {result.cancel();}});builder.setOnCancelListener(new OnCancelListener() {@Overridepublic void onCancel(DialogInterface dialog) {result.cancel();}});// 屏蔽keycode等于84之类的按键,避免按键后导致对话框消息而页面无法再弹出对话框的问题builder.setOnKeyListener(new OnKeyListener() {@Overridepublic boolean onKey(DialogInterface dialog, int keyCode,KeyEvent event) {Log.v("onJsConfirm", "keyCode==" + keyCode + "event=" + event);return true;}});// 禁止响应按back键的事件// builder.setCancelable(false);AlertDialog dialog = builder.create();dialog.show();return true;}/*** 覆盖默认的window.prompt展示界面,*/@Overridepublic boolean onJsPrompt(WebView view, String url, String message,String defaultValue, final JsPromptResult result) {final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());builder.setTitle("对话框").setMessage(message);final EditText et = new EditText(view.getContext());et.setSingleLine();et.setText(defaultValue);builder.setView(et).setPositiveButton("确定", new OnClickListener() {public void onClick(DialogInterface dialog, int which) {result.confirm(et.getText().toString());}}).setNeutralButton("取消", new OnClickListener() {public void onClick(DialogInterface dialog, int which) {result.cancel();}});// 屏蔽keycode等于84之类的按键,避免按键后导致对话框消息而页面无法再弹出对话框的问题builder.setOnKeyListener(new OnKeyListener() {public boolean onKey(DialogInterface dialog, int keyCode,KeyEvent event) {Log.v("onJsPrompt", "keyCode==" + keyCode + "event=" + event);return true;}});// 禁止响应按back键的事件// builder.setCancelable(false);AlertDialog dialog = builder.create();dialog.show();return true;// return super.onJsPrompt(view, url, message, defaultValue,// result);}
});

2.6 Android调用JS

添加网络权限

<uses-permission android:name="android.permission.INTERNET" />

编写html文件,放到assets文件里面

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body><div>function say(value){</br>&nbsp;&nbsp;callJS(value);</br>}</div></body><script>function callJS(value){alert(value);return value;}</script>
</html>

java文件,android调用js

public class MainActivity extends AppCompatActivity {private WebView webview;private TextView tvAndroid;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);webview = (WebView) findViewById(R.id.webview);tvAndroid = (TextView) findViewById(R.id.tv_android);tvAndroid.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {//Android调用js方法//Android 4.4以下使用loadUrl,Android 4.4以上evaluateJavascriptif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {webview.loadUrl("javascript:callJS('aaa')");} else {webview.evaluateJavascript("javascript:callJS('aaa')", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此处为 js 返回的结果Toast.makeText(MainActivity.this,value,Toast.LENGTH_SHORT).show();}});}}});initWebView();}public void initWebView() {//启用JS脚本webview.getSettings().setJavaScriptEnabled(true);// 设置允许JS弹窗webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);//加载网页webview.loadUrl("file:///android_asset/index.html");// 由于设置了弹窗检验调用结果,所以需要支持js对话框// webview只是载体,内容的渲染需要使用webviewChromClient类去实现// 通过设置WebChromeClient对象处理JavaScript的对话框//设置响应js 的Alert()函数webview.setWebChromeClient(new WebChromeClient(){@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult jsResult) {new AlertDialog.Builder(view.getContext()).setMessage(message).setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {jsResult.confirm();}}).setCancelable(false).create().show();return true;}});//覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开webview.setWebViewClient(new WebViewClient() {//overridepublic void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {handler.proceed("admin", "sunlight");int d = Log.d("MyWebViewClient", "onReceivedHttpAuthRequest");}@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String uri) {// TODO Auto-generated method stub//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器view.loadUrl(uri);return true;}});}
}

效果

注意:

  • 启用JS脚本 webview.getSettings().setJavaScriptEnabled(true);
  • 设置允许JS弹窗webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
  • 4.4以下通过​​WebView​​​的​​loadUrl()​,4.4以上通过​​WebView​​​的​​evaluateJavascript()​​
  • JS代码调用一定要在 ​​onPageFinished()​​ 回调之后才能调用,否则不会调用 

2.7 js调用android方法 

方法一:通过​​WebView​​​的​​addJavascriptInterface()​​进行对象映射

html文件

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body><button style="width:100%;height:50px; margin-top: 100px;" onclick="aa.showToast('哈哈哈')">js调用Android方法</button></body></html>

java文件

package com.serial.jsweview;import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;public class MainActivity2 extends AppCompatActivity {private WebView webview;private TextView tvAndroid;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);webview = (WebView) findViewById(R.id.webview);tvAndroid = (TextView) findViewById(R.id.tv_android);tvAndroid.setText("//继承自Object类,别名是aa,即在html可以直接用aa.showToast(\"哈哈哈\")来调用android方法\n" +"public class MyObject extends Object {\n" +"    @JavascriptInterface\n" +"    public void showToast(String name){\n" +"         Toast.makeText(MainActivity2.this, \"您好!\"+name, Toast.LENGTH_SHORT).show();\n" +"    }\n" +"}");initWebView();}public void initWebView() {// 设置与Js交互的权限webview.getSettings().setJavaScriptEnabled(true);//将java对象暴露给JavaScript脚本//参数1:java对象,里面定义了java方法//参数2:Java对象在js里的对象名,可以看作第一个参数的别名,可以随便取,即在html可以直接用aa.showToast("哈哈哈")来调用android方法webview.addJavascriptInterface(new MyObject(), "aa");//AndroidtoJS类对象映射到js的test对象//加载网页webview.loadUrl("file:///android_asset/index2.html");}//继承自Object类,别名是aa,即在html可以直接用aa.showToast("哈哈哈")来调用android方法public class MyObject extends Object {// 定义JS需要调用的方法// 被JS调用的方法必须加入@JavascriptInterface注解@JavascriptInterfacepublic void showToast(String name){Toast.makeText(MainActivity2.this, "您好!"+name, Toast.LENGTH_SHORT).show();}}
}

 

 方法二:通过 ​​WebViewClient​​​ 的​​shouldOverrideUrlLoading ()​​方法回调拦截 url

原理:

  • Android通过 ​​WebViewClient​​​ 的回调方法​​shouldOverrideUrlLoading ()​​拦截 url
  • 解析该 url 的协议
  • 如果检测到是预先约定好的协议,就调用相应方法

html文件

<!DOCTYPE html>
<html><head><meta charset="utf-8"><script>function callAndroid(){/*约定的url协议*/document.location = "js://webview?arg1=111&arg2=222";}</script></head><body><button style="width:100%;height:50px; margin-top: 100px;" onclick="callAndroid()">点击调用Android代码</button></body>
</html>

 java文件

public class MainActivity3 extends AppCompatActivity {private WebView webview;private TextView tvAndroid;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);webview = (WebView) findViewById(R.id.webview);tvAndroid = (TextView) findViewById(R.id.tv_android);initWebView();}public void initWebView() {// 设置与Js交互的权限webview.getSettings().setJavaScriptEnabled(true);// 设置允许JS弹窗webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);//步骤1:加载网页webview.loadUrl("file:///android_asset/index3.html");// 复写WebViewClient类的shouldOverrideUrlLoading方法webview.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {// 步骤2:根据协议的参数,判断是否是所需要的url// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)//假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)Uri uri = Uri.parse(url);// 如果url的协议 = 预先约定的 js 协议// 就解析往下解析参数if ( uri.getScheme().equals("js")) {// 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议// 所以拦截url,下面JS开始调用Android需要的方法if (uri.getAuthority().equals("webview")) {// 步骤3:// 执行JS所需要调用的逻辑Toast.makeText(MainActivity3.this, "您好!js调用了Android的方法", Toast.LENGTH_SHORT).show();// 可以在协议上带有参数并传递到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();}return true;}return super.shouldOverrideUrlLoading(view, url);}});}
}

效果

 

方法三:通过 ​​WebChromeClient​​​ 的​​onJsAlert()​​​、​​onJsConfirm()​​​、​​onJsPrompt()​​​方法回调拦截JS对话框​​alert()​​​、​​confirm()​​​、​​prompt()消息

原理:

  • Android通过 ​​WebChromeClient​​​ 的​​onJsAlert()​​​、​​onJsConfirm()​​​、​​onJsPrompt()​​​方法回调分别拦截JS对话框,得到他们的消息内容,然后解析即可。比如拦截 JS的输入框prompt()​​方法
  • 因为只有​​prompt()​​可以返回任意类型的值,操作最全面方便、更加灵活;
  • 而alert()对话框没有返回值;confirm()对话框只能返回两种状态(确定 / 取消)两个值

html文件

<!DOCTYPE html>
<html><head><meta charset="utf-8"><script>function clickprompt(){var result=prompt("js://demo?arg1=111&arg2=222");alert("demo " + result);}</script></head><body><button   style="width:100%;height:50px; margin-top: 100px;" onclick="clickprompt()">点击调用Android代码</button></body>
</html>

当使用​​mWebView.loadUrl("file:///android_asset/javascript.html")​​​加载了上述JS代码后,就会触发回调​​onJsPrompt()​​,具体如下:

如果是拦截警告框,即​​alert()​​​,则触发回调​​onJsAlert();
如果是拦截确认框,即​​confirm(),则触发回调​​onJsConfirm();

java文件

package com.serial.jsweview;import android.net.Uri;
import android.os.Bundle;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import java.util.HashMap;
import java.util.Set;public class MainActivity4 extends AppCompatActivity {private WebView webview;private TextView tvAndroid;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main4);webview = (WebView) findViewById(R.id.webview);tvAndroid = (TextView) findViewById(R.id.tv_android);initWebView();}public void initWebView() {// 设置与Js交互的权限webview.getSettings().setJavaScriptEnabled(true);// 设置允许JS弹窗webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);webview.loadUrl("file:///android_asset/index4.html");webview.setWebChromeClient(new WebChromeClient() {// 拦截输入框(原理同方式2)// 参数message:代表promt()的内容(不是url)// 参数result:代表输入框的返回值@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {// 根据协议的参数,判断是否是所需要的url(原理同方式2)// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)//假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)Uri uri = Uri.parse(message);// 如果url的协议 = 预先约定的 js 协议// 就解析往下解析参数if (uri.getScheme().equals("js")) {// 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议// 所以拦截url,下面JS开始调用Android需要的方法if (uri.getAuthority().equals("webview")) {//// 执行JS所需要调用的逻辑System.out.println("js调用了Android的方法");// 可以在协议上带有参数并传递到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();//参数result:代表消息框的返回值(输入值)result.confirm("js调用了Android的方法成功啦");}return true;}return super.onJsPrompt(view, url, message, defaultValue, result);}// 拦截JS的警告框@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {return super.onJsAlert(view, url, message, result);}// 拦截JS的确认框@Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) {return super.onJsConfirm(view, url, message, result);}});}
}

效果

总结:

JS调用Android代码的方法有3种:

  1. 通过​​WebView​​​的​​addJavascriptInterface()​​进行对象映射,存在安全漏洞
  2. 通过 ​​WebViewClient​​​ 的​​shouldOverrideUrlLoading ()​​方法回调拦截 url,不存在1的漏洞,但JS获取Android方法的返回值复杂
  3. 通过 ​​WebChromeClient​​​ 的​​onJsAlert()​​​、​​onJsConfirm()​​​、​​onJsPrompt()​​​方法回调拦截JS对话框​​alert()​​​、​​confirm()​​​、​​prompt()​​ 消息。不存在漏洞,需要协议的约定

2.8  页面返回

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {myWebView.goBack();return true;}return super.onKeyDown(keyCode, event);
}

2.9 缓存的配置

WebSettings webSettings = mWebView.getSettings();
//优先使用缓存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 
//只在缓存中读取
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
/不使用缓存
WwebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);

2.10 清除缓存

//清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
webview.clearCache(true);
//清除当前webview访问的历史记录,只会webview访问历史记录里的所有记录除了当前访问记录.
webview.clearHistory (); 
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据。
webview.clearFormData ();

2.10 webview生命周期

onResume()
WebView为活跃状态时回调,可以正常执行网页的响应。

onPause()
WebView被切换到后台时回调, 页面被失去焦点, 变成不可见状态,onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。

pauseTimers()
当应用程序被切换到后台时回调,该方法针对全应用程序的WebView,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。

resumeTimers()
恢复pauseTimers时的动作。

destroy()
关闭了Activity时回调, WebView调用destory时, WebView仍绑定在Activity上.这是由于自定义WebView构建时传入了该Activity的context对象, 因此需要先从父
容器中移除WebView, 然后再销毁webview。
mRootLayout.removeView(webView);  
mWebView.destroy();

跟随Activity的生命周期

@Override
protected void onResume() {super.onResume();//恢复webview的状态(不靠谱)webView.resumeTimers();//激活webView的状态,能正常加载网页webView.onResume();
}@Override
protected void onPause() {super.onPause();//当页面被失去焦点被切换到后台不可见状态,需要执行onPause//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。webView.onPause();//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。(不靠谱)webView.pauseTimers();
}@Override
protected void onDestroy() {super.onDestroy();//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview//但是注意:webview调用destory时,webview仍绑定在Activity上//这是由于自定义webview构建时传入了该Activity的context对象//因此需要先从父容器中移除webview,然后再销毁webview:ViewGroup parent = findViewById(R.id.container);parent.removeView(webView);webView.destroy();
}

三 防止内存泄漏的注意点

3.1 在需要的时候在 Activity 中创建,并且使用 getApplicationgContext(),而不要在xml注册。

//mWebView=new WebView(this);
mWebView=new WebView(getApplicationContext());
LinearLayout linearLayout  = findViewById(R.id.xxx);
linearLayout.addView(mWebView);

3.2 并且Activity退出的时候,需要手动释放webview内存

@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();
}

3.3 尽量不要在webview里面开启新进程 

比如下面这种

<activity android:name=".WebviewActivity"android:process=":webview"/>

四 其它web框架的选择

4.1 webview大部分场合是没问题的,但涉及到文件下载,文档浏览等只能自己写js交互,处理相对麻烦。所以也有其它web框架供我们选择

4.2 第一个就是开头提过的腾讯webx5,相对来说比较强大,但可能个别手机会出现兼容问题

官方网址:https://x5.tencent.com/tbs/

4.3 还有github常用的webview库,内置文件的处理功能,和加载缓冲样式,也可以直接代替webview

项目地址:https://github.com/Justson/AgentWeb

4.4 还有专门处理JS交互的一个库DSBridge,分为webx5内核版本和webkit内核版本

项目地址:https://github.com/wendux/DSBridge-Android

使用中webx5内核版本由于规则定义不符合我们要求,而且不能改,就换用了webkit内核版本,目前没出现什么问题

五  手机加载网页空白问题解决方案

5.1 网页加载空白原因有很多,遇到的可以尝试下面方案,第一个就是联网权限没配置,配置下权限

<uses-permission android:name="android.permission.INTERNET" />

5.2 https的ssl证书问题

WebView().setWebViewClient(new WebViewClient(){@Overridepublic void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {handler.proceed();}
});

5.3  尝试加载DOM缓存

webView.getSettings().setDomStorageEnabled(true);

5.4  Android5.0以上允许加载http和https混合的页面

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

5.5 如果上面尝试都不行,那就有可能不是Webview配置问题,可能是内核版本较低,对于高版本的Web或者JS语法不支持。

如ES6经过尝试在Androd6.0系统上会空白,而且切换腾讯Webx5内核也不行。这时候只能让Web前端es6转为es5了

即可以尝试h5做低版本适配,ES6转化为ES5的方案


http://chatgpt.dhexx.cn/article/UE1uCMtU.shtml

相关文章

Android:WebView 使用漏洞详解与解决方案

目录 1. 类型 WebView中&#xff0c;主要漏洞有三类&#xff1a; 任意代码执行漏洞密码明文存储漏洞域控制不严格漏洞 2. 具体分析 2.1 WebView 任意代码执行漏洞 出现该漏洞的原因有三个&#xff1a; WebView 中 addJavascriptInterface&#xff08;&#xff09; 接口WebVie…

android webview页面下方有input输入框,键盘弹起上滑无法拖动,导致输入内容也无法看到...

在手机端页面中有input输入框&#xff0c;输入框聚焦的时候会弹出键盘&#xff0c;如果input在页面的中下部&#xff0c;弹出的键盘会覆盖住input输入框&#xff0c;这个时候输入内容通常都是看不见的&#xff0c;用户看不到自己输入自己已经输入的内容这种体验非常的不好&…

Google上架因为WebView被拒

原因&#xff1a;在WebViewClient的回调方法onReceivedSslError里只单纯的写了handler.proceed();没给出明确的原因 两种解决方法&#xff1a; 1、去掉WebViewClient的回调方法onReceivedSslError 2、修改onReceivedSslError如下所示 Override public void onReceivedSslEr…

Android 使用Webview加载PDF文件

一、核心代码 protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pdf_renderer);WebSettings webSettings weContainer.getSettings();webSettings.setJavaScriptEnabled(true);// 设置WebView是否允…

Android中解决ScrollView嵌套WebView底部留白太多和高度问题

前言&#xff1a; Android中WebView的坑很多&#xff0c;比如低版本内核不支持&#xff0c;加载速度慢&#xff0c;重定向等等&#xff0c;当使用ScrollView嵌套WebView时坑更多&#xff0c;有人说为啥要嵌套&#xff1f;单独使用WebView或者ScrollView不行吗&#xff1f;答案…

webview适配(一):文件选择,相机拍照,相册选择

说起来android适配webview就是比较烦人&#xff0c;本人遇到过很多坑&#xff0c;但是之前从来没有过记录&#xff0c;例如&#xff1a;文件选择框&#xff0c;toast弹框&#xff0c;视频不正常播放&#xff0c;视频各种不全屏&#xff08;网上说的方法都不行时候就崩溃了&…

Android跟web哪个好,比系统自带的WebView更好用 | AgentWeb

名称 AgentWeb 语言 Android 平台 GitHub 作者 Justson 在混合化开发大行其道的今天&#xff0c;安卓开发经常会用到WebView&#xff0c;用于加载网页。系统自带的WebView性能和流畅度都一般&#xff0c;今天给大家推荐一款第三方WebView&#xff0c;性能比系统自带的要好&…

X5 浏览器内核调研报告

关于这份调研报告&#xff0c;不是从技术角度深入探索&#xff0c;重点是从产品本身分析&#xff0c;通俗易懂才是重点。主要是为了锻炼平时做技术调研和竞品分析的能力&#xff0c;以及业务拓展的技术储备。内容有点多&#xff0c;下面 **X5 **内核调研报告将分为三个环节&…

南京理工大学计算机考研资料汇总

南京理工大学研究生院 http://gs.njust.edu.cn/ 南京理工大学计算机科学与工程学院 http://cs.njust.edu.cn/ 南京理工大学计算机科学与工程学院始建于1953年创办的哈尔滨军事工程学院模拟计算机研究组&#xff0c;先后经历了炮兵工程学院计算机教研室&#xff08;1960年&a…

上海海洋大学计算机考研资料汇总

上海海洋大学研招网 上海海洋大学信息学院 上海海洋大学&#xff08;Shanghai Ocean University&#xff09;是上海市人民政府与国家海洋局、农业农村部共建高校&#xff0c;国家“双一流”世界一流学科建设高校&#xff0c;入选国家卓越农林人才教育培养计划、国家建设高水平…

2024东华大学计算机考研信息汇总

最新数据见&#xff1a;东华大学_信息汇总_N诺计算机考研 东华大学计算机科学与技术学院官网&#xff1a;http://cst.dhu.edu.cn/ 东华大学&#xff08;Donghua University&#xff09;&#xff0c;简称“东华”&#xff0c;地处中国上海&#xff0c;是教育部直属、国家“211工…

西南石油大学计算机考研资料汇总

西南石油大学&#xff08;Southwest Petroleum University&#xff09;&#xff0c;简称“西南石大”&#xff0c;坐落于四川省成都市&#xff0c;是经中华人民共和国教育部备案的一所中央与地方共建、以四川省人民政府管理为主的高等院校&#xff0c;是世界一流学科建设高校、…

2023东北大学计算机考研信息汇总

完整内容见&#xff1a;东北大学 东北大学计算机科学与工程学院 东北大学软件学院 东北大学秦皇岛分校 东北大学&#xff08;Northeastern University&#xff09;&#xff0c;简称东大&#xff08;NEU&#xff09;&#xff0c;中华人民共和国教育部直属的高水平研究型全国…

2023上海海洋大学计算机考研信息汇总

上海海洋大学研招网 上海海洋大学信息学院 上海海洋大学&#xff08;Shanghai Ocean University&#xff09;是上海市人民政府与国家海洋局、农业农村部共建高校&#xff0c;国家“双一流”世界一流学科建设高校&#xff0c;入选国家卓越农林人才教育培养计划、国家建设高水平…

东北大学计算机考研资料汇总

东北大学计算机科学与工程学院 东北大学软件学院 东北大学秦皇岛分校 东北大学&#xff08;Northeastern University&#xff09;&#xff0c;简称东大&#xff08;NEU&#xff09;&#xff0c;中华人民共和国教育部直属的高水平研究型全国重点大学&#xff0c;坐落于东北…

2023北京大学计算机考研信息汇总

原文转载于&#xff1a;北京大学 N诺小程序 - 计算机学习考研必备神器 北京大学信息科学技术学院 北京大学软件与微电子学院 北京大学前沿交叉学科研究院 北京大学信息工程学院 北大研究生院 北京大学&#xff08;Peking University&#xff09;简称“北大”&#…

北京大学计算机考研资料汇总

北京大学信息科学技术学院 北京大学软件与微电子学院 北京大学前沿交叉学科研究院 北京大学信息工程学院 北大研究生院 北京大学&#xff08;Peking University&#xff09;简称“北大”&#xff0c;诞生于1898年&#xff0c;初名京师大学堂&#xff0c;是中国近代第…

2023西南石油大学计算机考研信息汇总

西南石油大学&#xff08;Southwest Petroleum University&#xff09;&#xff0c;简称“西南石大”&#xff0c;坐落于四川省成都市&#xff0c;是经中华人民共和国教育部备案的一所中央与地方共建、以四川省人民政府管理为主的高等院校&#xff0c;是世界一流学科建设高校、…

2023南京理工大学计算机考研信息汇总

南京理工大学研究生院 http://gs.njust.edu.cn/ 南京理工大学计算机科学与工程学院 http://cs.njust.edu.cn/ 南京理工大学计算机科学与工程学院始建于1953年创办的哈尔滨军事工程学院模拟计算机研究组&#xff0c;先后经历了炮兵工程学院计算机教研室&#xff08;1960年&am…

2023中国科学院大学计算机考研信息汇总

原文转载于&#xff1a;中国科学院大学 N诺小程序 - 计算机学习考研必备神器 中国科学院大学&#xff08;University of Chinese Academy of Sciences&#xff09;&#xff0c;简称“国科大”&#xff0c;是国家教育部正式批准成立的一所以研究生教育为主的科教融合、独具特色…