Android之 WebView的使用

news2024/10/6 20:37:06

一 简介

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里面初始化

<WebView
   android: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(){
      @Override
      public 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"请求方式不会调用这个回调函数
	 */
	@Override
	public 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。
	 */
	@Override
	public void onPageStarted(WebView view, String url, Bitmap favicon) {
		// TODO Auto-generated method stub
		super.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
	 */
	@Override
	public void onPageFinished(WebView view, String url) {
		// TODO Auto-generated method stub
		super.onPageFinished(view, url);
		Log.i(TAG, "onPageStarted:页面加载结束");
	}
	
	/**
	 * onLoadResource 通知应用程序WebView即将加载url 制定的资源
	 * 
	 * 参数说明:
	 * 
	 * @param view
	 *            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new
	 *            MyAndroidWebViewClient()),即是这个webview。
	 * @param url
	 *            即将加载的url 资源
	 */
	@Override
	public void onLoadResource(WebView view, String url) {
		// TODO Auto-generated method stub
		super.onLoadResource(view, url);
		Log.i(TAG, "onLoadResource:加载资源指定的网址");
	}
	
	/**
	 * shouldInterceptRequest
	 * 通知应用程序内核即将加载url制定的资源,应用程序可以返回本地的资源提供给内核,若本地处理返回数据,内核不从网络上获取数据。
	 * 
	 * 参数说明:
	 * 
	 * @param view
	 *            接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new
	 *            MyAndroidWebViewClient()),即是这个webview。
	 * @param url
	 *            raw url 制定的资源
	 * @return 返回WebResourceResponse包含数据对象,或者返回null
	 */
	@Override
	public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
		// TODO Auto-generated method stub
		Log.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
	 */
	@Override
	public void onReceivedError(WebView view, int errorCode,
			String description, String failingUrl) {
		// TODO Auto-generated method stub
		super.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
	 *            浏览器需要重新发送的参数
	 */
	@Override
	public void onFormResubmission(WebView view, Message dontResend,
			Message resend) {
		// TODO Auto-generated method stub
		super.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
	 */
	@Override
	public void doUpdateVisitedHistory(WebView view, String url,
			boolean isReload) {
		// TODO Auto-generated method stub
		super.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错误对象
	 * 
	 */
	@Override
	public void onReceivedSslError(WebView view, SslErrorHandler handler,
			SslError error) {
		// view.loadUrl("file:///android_asset/error.html");
		// TODO Auto-generated method stub
		super.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
	 *            认真请求所在的域
	 */
	@Override
	public void onReceivedHttpAuthRequest(WebView view,
			HttpAuthHandler handler, String host, String realm) {
		// TODO Auto-generated method stub
		super.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处理。
	 */
	@Override
	public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
		Log.i(TAG, "shouldOverrideKeyEvent");
		// TODO Auto-generated method stub
		return super.shouldOverrideKeyEvent(view, event);
	}
	
	/**
	 * 通知应用程序webview 要被scale。应用程序可以处理改事件,比如调整适配屏幕。
	 */
	@Override
	public void onScaleChanged(WebView view, float oldScale, float newScale) {
		// TODO Auto-generated method stub
		super.onScaleChanged(view, oldScale, newScale);
		Log.i(TAG, "onScaleChanged");
	}
	
	/**
	 * onReceivedLoginRequest 通知应用程序有个自动登录的帐号过程
	 * 
	 * 参数说明:
	 * 
	 * @param view
	 *            请求登陆的webview
	 * @param realm
	 *            账户的域名,用来查找账户。
	 * @param account
	 *            一个可选的账户,如果是null 需要和本地的账户进行check, 如果是一个可用的账户,则提供登录。
	 * @param args
	 *            验证制定参数的登录用户
	 */
	@Override
	public void onReceivedLoginRequest(WebView view, String realm,
			String account, String args) {
		// TODO Auto-generated method stub
		super.onReceivedLoginRequest(view, realm, account, args);
		Log.i(TAG, "onReceivedLoginRequest");
 
	}
});

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

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

webView.setWebChromeClient(new WebChromeClient() {
	@Override
	public 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接受的进度
	 */
	@Override
	public void onProgressChanged(WebView view, int newProgress) {
		// TODO Auto-generated method stub
		super.onProgressChanged(view, newProgress);
		if (newProgress <= 100) {
			Log.i(TAG, newProgress + "===onProgressChanged===");
		}
	}
	
	/**
	 * 当document 的title变化时,会通知应用程序
	 * 
	 * 
	 * 参数说明:
	 * 
	 * @param view
	 *            接收WebViewClient的webview实例
	 * @param title
	 *            document新的title
	 */
	@Override
	public void onReceivedTitle(WebView view, String title) {
		// TODO Auto-generated method stub
		super.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,
	 */
	@Override
	public void onReceivedIcon(WebView view, Bitmap icon) {
		// TODO Auto-generated method stub
		super.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。
	 */
	@Override
	public void onReceivedTouchIconUrl(WebView view, String url,
			boolean precomposed) {
		// TODO Auto-generated method stub
		super.onReceivedTouchIconUrl(view, url, precomposed);
		Log.i(TAG, "====onReceivedTouchIconUrl====");
	}
	
	 
	/**
	 * webview请求得到focus,发生这个主要是当前webview不是前台状态,是后台webview。
	 */
	@Override
	public void onRequestFocus(WebView view) {
		// TODO Auto-generated method stub
		super.onRequestFocus(view);
		Log.i(TAG, "====onRequestFocus====");
	}
	
	/**
	 * 覆盖默认的window.alert展示界面,
	 */
	@Override
	public 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展示界面,
	 */
	@Override
	public 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() {
			@Override
			public void onCancel(DialogInterface dialog) {
				result.cancel();
			}
		});
 
		// 屏蔽keycode等于84之类的按键,避免按键后导致对话框消息而页面无法再弹出对话框的问题
		builder.setOnKeyListener(new OnKeyListener() {
			@Override
			public 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展示界面,
	 */
	@Override
	public 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;

    @Override
    protected 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() {
            @Override
            public void onClick(View view) {
                //Android调用js方法
                //Android 4.4以下使用loadUrl,Android 4.4以上evaluateJavascript
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    webview.loadUrl("javascript:callJS('aaa')");
                } else {
                    webview.evaluateJavascript("javascript:callJS('aaa')", new ValueCallback<String>() {
                        @Override
                        public 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(){
            @Override
            public 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() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        jsResult.confirm();
                    }
                }).setCancelable(false).create().show();
                return true;
            }
        });

        //覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开
        webview.setWebViewClient(new WebViewClient() {
            //override
            public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
                handler.proceed("admin", "sunlight");
                int d = Log.d("MyWebViewClient", "onReceivedHttpAuthRequest");
            }
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String uri) {
                // TODO Auto-generated method stub
                //返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
                view.loadUrl(uri);
                return true;
            }
        });
    }
}

效果

 

 

注意:

1,  //启用JS脚本
        webview.getSettings().setJavaScriptEnabled(true);
        // 设置允许JS弹窗
        webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);

2,4.4以下通过​​WebView​​​的​​loadUrl()​​
     4.4以上通过​​WebView​​​的​​evaluateJavascript()​​

3,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;

    @Override
    protected 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注解
        @JavascriptInterface
        public 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;

    @Override
    protected 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() {
			  @Override
			  public 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;

    @Override
    protected 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:代表输入框的返回值
                @Override
                public 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的警告框
                @Override
                public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                    return super.onJsAlert(view, url, message, result);
                }

                // 拦截JS的确认框
                @Override
                public 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()​​ 消息。不存在漏洞,需要协议的约定

三 总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/461469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB连续时间信号的实现和时域基本运算(八)更新中...

1、实验目的&#xff1a; 1&#xff09;熟悉常用连续时间信号的实现方法&#xff1b; 2&#xff09;掌握连续时间信号的时域基本运算&#xff1b; 3&#xff09;掌握实现基本函数及其运算的函数的使用方法&#xff1b; 4&#xff09;加深对信号基本运算的理解。 2、实验内容&am…

Python Selenium 关键字驱动

目录 项目目录结构 action目录 config目录 exceptionpictures目录 log目录 testCases目录 testData目录 util目录 总结 之前写过一篇Java版的关键字驱动&#xff0c;现在来写一篇Python版本的&#xff0c;网上好多教程都是虎头蛇尾的不完整~ 说下思路&#xff0c;这边没…

十、ElasticSearch 实战 - 源码运行

一、概述 想深入理解 Elasticsearch&#xff0c;了解其报错机制&#xff0c;并有针对性的调整参数&#xff0c;阅读其源码是很有必要的。此外&#xff0c;了解优秀开源项目的代码架构&#xff0c;能够提高个人的代码架构能力 阅读 Elasticsearch 源码的第一步是搭建调试环境&…

C++的左值引用和右值引用

引用和指针的区别&#xff1f; 引用必须初始化&#xff0c;指针可以不初始化 定义一个指针和引用汇编指令上一样的&#xff0c;引用底层还是指针 引用只有一级引用&#xff0c;没有多级引用&#xff0c;而指针可以有多级指针 定义一个引用变量和指针变量&#xff0c;它们汇…

【c语言】函数指针详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

Android Framework—Service

介绍 Service是一种可以在后台执行长时间运行操作而不提供界面的应用组件。服务可以由其他应用组件启动&#xff0c;而且即使用户切换到其他应用&#xff0c;服务仍将在后台继续运行。此外&#xff0c;组件可以通过绑定到服务与之进行交互&#xff0c;甚至是执行进程之间的通信…

Unreal5 第三人称射击游戏 角色基础制作

快捷键 快捷键 ctrlE 可以快速打开相关蓝图类&#xff0c;直接在场景选中添加到场景中的对象即可。 调试蓝图 选中调试对象&#xff0c;选中需要调试的实例&#xff0c;即可查看当前角色的状态。 动画实例在运行的时候&#xff0c;也可以在右侧箭头获取当前场景已经生成的实…

Linux socket编程(二):多进程服务器

一、多进程服务器 最简单的的服务器程序执行流程为&#xff1a;创建socket → \rightarrow → 绑定监听的IP地址和端口 → \rightarrow → 监听客户端连接 → \rightarrow → 接受/发送数据。当服务端调用read阻塞等待一个客户端发来数据时&#xff0c;无法同时响应其它客户…

七、CANdelaStudio入门-Variant概念与应用

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的Variant概念与应用,欢迎各位朋友订阅、…

Redis实现互相关注功能

Redis实现互相关注功能 我们要实现关注功能&#xff0c;首先&#xff0c;我们需要得到关注的目标ID&#xff1a; PutMapping("/{id}/{isFollow}")public Result followUser(PathVariable("id") Long id,PathVariable("isFollow") Boolean isFol…

下一代大数据分布式存储技术Apache Ozone初步研究

文章目录 概述定义特性架构总体架构写数据读数据 部署安装方式安装Docker启动Docker-compose启动企业预置型(On Premise)安装 实践命令行接口Ofs (Hadoop兼容)Recon API 概述 定义 Apache Ozone 官网地址 https://ozone.apache.org/ 最新版本1.3.0 Apache Ozone 官网最新文档地…

Java线程间通信方式(2)

前文了解了线程通信方式中的Object.wait/Object.notify以及Semaphore,接下来我们继续了解其他的线程间通信方式。 CountDownLatch CountDownLatch利用一个指定的计数初始化&#xff0c;由于调用了countDown方法&#xff0c;await方法会阻塞直到当前技术为0&#xff0c;之后所…

PHP-8.2.5+IIS10 php-cgi.exe - FastCGI 进程意外退出

服务器信息&#xff1a; Windows Server 2019 Standard. Internet Information Services(Version 10.0.17763.1) PHP版本&#xff1a; PHP Version 8.2.5 php-8.2.5-nts-Win32-vs16-x64 下载地址&#xff1a;https://windows.php.net/download#php-8.2 错误信息&#xff1a; H…

【网络】-- UDP协议

目录 传输层 再谈端口号 端口号范围划分 认识知名端口号&#xff08;Well-Know Port Number&#xff09; 两个问题 netstat pidof UDP协议 UDP的特点 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 传输层 负责数据能够从发送端传输接收端。 再谈端口号 端…

【ROS】ubuntu18.04安装ROS(ROS1 Melodic)

1、添加中科大ROS源 1.1、添加源 sudo sh -c . /etc/lsb-release && echo "deb http://mirrors.ustc.edu.cn/ros/ubuntu/ lsb_release -cs main" > /etc/apt/sources.list.d/ros-latest.list1. 2、添加公钥 sudo apt-key adv --keyserver hkp://keyser…

输入捕获实验

实验内容 用TIM5 的通道 1&#xff08;PA0&#xff09;来做输入捕获&#xff0c;捕获 PA0 上高电平的脉宽&#xff08;用 WK_UP 按键输入高电平&#xff09;&#xff0c;通过串口打印高电平脉宽时间。 输入捕获简介 输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的…

新库上线 | 全国工艺美术大师信息数据

全国工艺美术大师信息数据 一、数据简介 作为物质产品&#xff0c;工艺美术反映着一定时代、一定社会的物质的和文化的生产水平&#xff1b;作为精神产品&#xff0c;它的视觉形象&#xff08;造型、色彩、装饰&#xff09;又体现了一定时代的审美观。我国工艺美术品的制作较早…

java基础+注解笔记【狂神说java】

基础部分–总结 基础包的命名 //一般为域名倒置 page com.yang.base基础语法 类和方法 根据不同的写法–大小写的差异可以判别是类对象还是方法名 类的首字母都要大写&#xff0c;方法首字母小写可以理解为&#xff0c;类对象是class&#xff0c;方法是函数 类对象 方法 使…

浅谈在 Vue2 和 Vue3 中计算属性和侦听器的一些变化

文章目录 &#x1f4cb;前言&#x1f3af;计算属性&#x1f3af;侦听器&#x1f4dd;最后 &#x1f4cb;前言 计算属性 computed 和侦听器 watch 都是 Vue.js 框架中用来响应式更新视图的重要概念。因此无论是在哪个版本&#xff0c;它们都是不可缺少的概念&#xff0c;这篇文…

音视频八股文(5)--SDL音视频渲染实战。会使用就行,不需要深究。

01-SDL子系统 SDL将功能分成下列数个子系统&#xff08;subsystem&#xff09;&#xff1a; ◼ SDL_INIT_TIMER&#xff1a;定时器 ◼ SDL_INIT_AUDIO&#xff1a;音频 ◼ SDL_INIT_VIDEO&#xff1a;视频 ◼ SDL_INIT_JOYSTICK&#xff1a;摇杆 ◼ SDL_INIT_HAPTIC&#xff1…