WebView学习
一: 概念及作用
WebView是一个基于webkit引擎、展现web页面的控件。
Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome
显示和渲染Web页面
直接使用html文件(网络上或本地assets中)作布局
可和JavaScript交互调用
WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理
二:WebView常用类及常用方法
WebSettings类、WebViewClient类、WebChromeClient类
使用:
<WebView
android:id="@+id/webView1"
android:layout_below="@+id/text_endLoading"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginTop="10dp" />
mwebView = (WebView) findViewById(R.id.webView1);
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true; //不在第三方浏览器打开
}
});
1.webView常用方法
WebView的状态
//激活WebView为活跃状态,能正常执行网页的响应
webView.onResume() ;
//当页面被失去焦点被切换到后台不可见状态,需要执行onPause
//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
webView.onPause();
//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢复pauseTimers状态
webView.resumeTimers();
//销毁Webview
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
rootLayout.removeView(webView);
webView.destroy();
//是否可以后退
Webview.canGoBack()
//后退网页
Webview.goBack()
//是否可以前进
Webview.canGoForward()
//前进网页
Webview.goForward()
//刷新
webView.reload();
//以当前的index为起始点前进或者后退到历史记录中指定的steps
//如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);
//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
2.Back键控制网页后退
问题:在不做任何处理前提下 ,浏览网页时点击系统的“Back”键,整个 Browser 会调用 finish()而结束自身
目标:点击返回后,是网页回退而不是推出浏览器
解决方案:在当前Activity中处理并消费掉该 Back 事件
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
2.WebSetting常用方法
//声明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”);//设置编码格式
3.WebViewClient常用方法
// 在webview中打开网页,不使用手机网址
//由于每次超链接在加载前都会先走shouldOverrideUrlLoading回调,所以我们如果想拦截某个URL,将其转换成其它URL可以在这里做。
// 比如,我们拦截所有包含“blog.csdn.net”的地址,将其替换成”www.baidu.com”:
/public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains(“blog.csdn.net”)){
view.loadUrl(“http://www.baidu.com”);
}else {
view.loadUrl(url);
}
return true;
}/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
/* WebView view:当前的WebView实例
SslErrorHandler handler:当前处理错误的Handler,它只有两个函数SslErrorHandler.proceed()和SslErrorHandler.cancel(),SslErrorHandler.proceed()表示忽略错误继续加载,SslErrorHandler.cancel()表示取消加载。在onReceivedSslError的默认实现中是使用的SslErrorHandler.cancel()来取消加载,所以一旦出来SSL错误,HTTPS网站就会被取消加载了,如果想忽略错误继续加载就只有重写onReceivedSslError,并在其中调用SslErrorHandler.proceed()
SslError error:当前的的错误对象,SslError包含了当前SSL错误的基本所有信息,大家自己去看下它的方法吧,这里就不再展开了。
一定要注释掉!
super.onReceivedSslError(view, handler, error);
handler.proceed();
Log.e(TAG,“sslError:”+error.toString()); 发生ssl错误时不会回调onReceividError方法
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
}
//加载错误的时候会产生这个回调,在其中可做错误处理,比如我们可以加载一个错误提示页面
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
// 在每一次请求资源时,都会通过这个函数来回调,比如超链接、JS文件、CSS文件、图片等,
// 也就是说浏览器中每一次请求资源时,都会回调回来,无论任何资源!
// 但是必须注意的是shouldInterceptRequest函数是在非UI线程中执行的,在其中不能直接做UI操作,
// 如果需要做UI操作,则需要利用Handler来实现
/该函数会在请求资源前调用,我们可以通过返回WebResourceResponse的处理结果来让WebView直接使用我们的处理结果。如果我们不想处理,则直接返回null,系统会继续加载该资源。
利用这个特性,我们可以解决一个需求:假如网页中需要加载本地的图片,我们就可以通过拦截shouldInterceptRequest,并返回结果即可/
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return super.shouldInterceptRequest(view, request);
}
添加开始加载的进度条
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
System.out.println("开始加载了");
beginLoading.setText("开始加载了");
}
//设置结束加载函数,取消进度条
@Override
public void onPageFinished(WebView view, String url) {
endLoading.setText("结束加载了");
}
在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次
@Override
public boolean onLoadResource(WebView view, String url) {
//设定加载资源的操作
}
其余不常用方法
/**
- 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次
/
public void onLoadResource(WebView view, String url)
/* - (WebView发生改变时调用)
public void onScaleChanged(WebView view, float oldScale, float newScale)
*- 重写此方法才能够处理在浏览器中的按键事件。
- 是否让主程序同步处理Key Event事件,如过滤菜单快捷键的Key Event事件。
- 如果返回true,WebView不会处理Key Event,
- 如果返回false,Key Event总是由WebView处理。默认:false
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
/* - 是否重发POST请求数据,默认不重发。
*onFormResubmission(WebView view, Message dontResend, Message resend)
* - 更新访问历史
doUpdateVisitedHistory(WebView view, String url, boolean isReload) /* - 通知主程序输入事件不是由WebView调用。是否让主程序处理WebView未处理的Input Event。
- 除了系统按键,WebView总是消耗掉输入事件或shouldOverrideKeyEvent返回true。
- 该方法由event 分发异步调用。注意:如果事件为MotionEvent,则事件的生命周期只存在方法调用过程中,
- 如果WebViewClient想要使用这个Event,则需要复制Event对象。
*onUnhandledInputEvent(WebView view, InputEvent event) * - 通知主程序执行了自动登录请求。
*onReceivedLoginRequest(WebView view, String realm, String account, String args) - 通知主程序:WebView接收HTTP认证请求,主程序可以使用HttpAuthHandler为请求设置WebView响应。默认取消请求。
*onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) - 通知主程序处理SSL客户端认证请求。如果需要提供密钥,主程序负责显示UI界面。
- 有三个响应方法:proceed(), cancel() 和 ignore()。
- 如果调用proceed()和cancel(),webview将会记住response,
- 对相同的host和port地址不再调用onReceivedClientCertRequest方法。
- 如果调用ignore()方法,webview则不会记住response。该方法在UI线程中执行,
- 在回调期间,连接被挂起。默认cancel(),即无客户端认证
onReceivedClientCertRequest(WebView view, ClientCertRequest request)WebChromeClient
Webview加载H5需要定位时
//检查定位权限是否已经允许
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
//申请定位权限
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 0);
} else {
//定位权限已经被允许
Log.e(“location”,“定位权限已经被允许”);
}
//webview定位相关设置
webSettings.setDatabaseEnabled(true);
webSettings.setGeolocationEnabled(true);
String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
//设置定位的数据库路径
webSettings.setGeolocationDatabasePath(dir);
webSettings.setDomStorageEnabled(true);
webView.setWebChromeClient(new WebChromeClient(){
// onGeolocationPermissionsShowPrompt
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, true);
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
});
权限
// 使用GPS定位的话,需要如下权限:
// 如果使用WIFI接入点定位的话,需要如下权限:
//权限
4.WebChromeClient常用方法
取网站标题
@Override
public void onReceivedTitle(WebView view, String title) {
System.out.println("标题在这里");
mtitle.setText(title);
}
//获取加载进度
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress < 100) {
String progress = newProgress + "%";
loading.setText(progress);
} else if (newProgress == 100) {
String progress = newProgress + "%";
loading.setText(progress);
}
}
5.设置webView缓存
当加载 html 页面时,WebView会在/data/data/包名目录下生成 database 与 cache 两个文件夹
请求的 URL记录保存在 WebViewCache.db,而 URL的内容是保存在 WebViewCache 文件夹下
是否启用缓存:
//优先使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//不使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
结合使用(离线加载)
if (NetStatusUtil.isConnected(getApplicationContext())) {
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}
webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
webSettings.setDatabaseEnabled(true); //开启 database storage API 功能
webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置 Application Caches 缓存目录
注意: 每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()
6.webview滚动监听
我们都知道监听滚动事件一般都是设置setOnScrollChangedListener,可惜的是 WebView并没有给我们提供这样的方法,但是我们可以重写WebView,覆盖里面的一个方法: protected void onScrollChanged(final int l, final int t, final int oldl,final int oldt){} 然后再对外提供一个接口,示例代码如下:
[java] view plain copy
public class MyWebView extends WebView {
private OnScrollChangedCallback mOnScrollChangedCallback;
public MyWebView(Context context) {
super(context);
}
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedCallback != null) {
mOnScrollChangedCallback.onScroll(l,t,oldl,oldt);
}
}
public OnScrollChangedCallback getOnScrollChangedCallback() {
return mOnScrollChangedCallback;
}
public void setOnScrollChangedCallback(
final OnScrollChangedCallback onScrollChangedCallback) {
mOnScrollChangedCallback = onScrollChangedCallback;
}
public static interface OnScrollChangedCallback {
public void onScroll(int left,int top ,int oldLeft,int oldTop);
}
}
三:如何避免WebView内存泄露?
不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 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();
}
四:webView与js交互
Android与JS通过WebView互相调用方法,实际上是:
Android去调用JS的代码
JS去调用Android的代码
二者沟通的桥梁是WebView
对于Android调用JS代码的方法有2种:
- 通过WebView的loadUrl()
- 通过WebView的evaluateJavascript()
对于JS调用Android代码的方法有3种:
- 通过WebView的addJavascriptInterface()进行对象映射
- 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
- 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
Android通过WebView调用 JS 代码
对于Android调用JS代码的方法有2种:
通过WebView的loadUrl()
通过WebView的evaluateJavascript()
方式1:通过WebView的loadUrl()
实例介绍:点击Android按钮,即调用WebView JS(文本名为javascript)中callJS()
具体使用:
步骤1:将需要调用的JS代码以.html格式放到src/main/assets文件夹里
为了方便展示,本文是采用Andorid调用本地JS代码说明;
实际情况时,Android更多的是调用远程JS代码,即将加载的JS代码路径改成url即可
第一种 通过WebView的loadUrl(js的方法名)
js代码,保存在本地main.assest文件夹中
添加权限
// 文本名javascript
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS代码
<script>
// Android需要调用的方法
function callJS(){
alert("Android调用了JS的callJS方法");
return "ss"; //第二种方法调用时返回的结果就是ss
}
</script>
</head>
</html>
布局文件,点击按钮调用js中callJs方法,弹出alert
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:layout_width="match_parent"
android:layout_height="400dp"
android:id="@+id/webview_js"/>
<Button
android:id="@+id/but_webjs_js"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击调用js"/>
</LinearLayout>
WebViewJsActivity的写法
package com.ayk.judicial.htmltext;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
/**
* Created by Administrator on 2017/8/3.
*/
public class WebViewJsActivity extends Activity {
private WebView webView;
private Button but;
private WebSettings webSet;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview_js);
webView= (WebView) findViewById(R.id.webview_js);
but= (Button) findViewById(R.id.but_webjs_js);
webSet=webView.getSettings();
//设置可以与js进行交互权限
webSet.setJavaScriptEnabled(true);
//设置可以显示js弹窗权限
webSet.setJavaScriptCanOpenWindowsAutomatically(true);
webView.loadUrl("file:///android_asset/javascript.html");
//点击按钮调用js中方法
but.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 必须另开线程进行JS方法调用(否则无法调用)
webView.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
webView.loadUrl("javascript:callJS()");
}
});
}
});
// 由于设置了弹窗检验调用结果,所以需要支持js对话框
// webview只是载体,内容的渲染需要使用webviewChromClient类去实现
// 通过设置WebChromeClient对象处理JavaScript的对话框
//设置响应js 的Alert()函数
webView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(WebViewJsActivity.this);
b.setTitle("Alert js中的弹框");
b.setMessage(message); //js中弹框要显示的内容
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;
}
});
}
}
JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。
运行结果:
第二种:通过WebView的evaluateJavascript()
优点:该方法比第一种方法效率更高、使用更简洁。
因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。
Android 4.4 后才可使用
具体使用
只需要将第一种方法的loadUrl()换成下面该方法即可
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
实现:
//点击按钮调用js中方法
but.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 必须另开线程进行JS方法调用(否则无法调用)
webView.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
// webView.loadUrl("javascript:callJS()"); //这是第一种方法
//这是第二种方法
webView.evaluateJavascript("javascript:callJS()",new ValueCallback<String>(){
@Override
public void onReceiveValue(String s) {
Log.e("javascript","--onReceiveValue 返回的参数 ,此处为js返回的结果--"+s);
//如果需要调用原生activity或是其他操作,可在这里进行
// Intent intent=new //Intent(WebViewJsActivity.this,MainActivity.class);
// startActivity(intent);
}
});
}
});
}
});
JS通过WebView调用 Android 代码
对于JS调用Android代码的方法有3种:
- 通过WebView的addJavascriptInterface()进行对象映射
- 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
- 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
第一种:通过 WebView的addJavascriptInterface()进行对象映射
步骤1:定义一个与JS对象映射关系的Android类:AndroidtoJs
package com.ayk.judicial.htmltext;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.webkit.JavascriptInterface;
/**
- Created by Administrator on 2017/8/4.
- 映射js中方法的android中创建的类
*/
public class AndroidtoJs {
private Context context;
//通过把context传入过来 就可以通过点击js中的按钮跳转到android原生页面
public AndroidtoJs(Context context){
this.context=context;
}
//js调用的方法,js调用的方法必须加@JavascriptInterface注解
@JavascriptInterface
public void showToast(String s){
Log.e("js调用android------",s);
//通过把context传入过来 就可以通过点击js中的按钮跳转到android原生页面
Intent intent=new Intent(context,MainActivity.class);
context.startActivity(intent);
}
}
步骤2:将需要调用的JS代码以.html格式放到src/main/assets文件夹里
<html>
<script type="text/javascript">
function showToast() {
android.showToast("哈哈啊哈 ");
}
</script>
</head>
<body>
<input type="button" value="调用"
onClick="showToast()"/>
</body>
</html>
步骤3:在Android里通过WebView设置Android类与JS代码的映射
package com.ayk.judicial.htmltext;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* Created by Administrator on 2017/8/4.
* js调用android方法
*/
public class WebViewJstoAndroid extends Activity{
private WebView webView;
private WebSettings webSettings;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jstoandroid);
webView= (WebView) findViewById(R.id.webview_JstoAndroid);
webSettings=webView.getSettings();
webSettings.setJavaScriptEnabled(true); //容许js交互
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:android中需要映射js中方法创建的类
//参数2:js中的方法对象名称
webView.addJavascriptInterface(new AndroidtoJs(WebViewJstoAndroid.this), "android");//AndroidtoJs类对象映射到js的test对象
webView.loadUrl("file:///android_asset/javascript_js.html");
webView.setWebViewClient(new WebViewClient());
}
}
优点:使用简单
仅将Android对象和JS对象映射即可
缺点:存在严重的漏洞问题
第二种:通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url
具体原理:
Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
解析该 url 的协议
如果检测到是预先约定好的协议,就调用相应方法
javascript代码
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function callAndroid(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
</script>
</head>
<!-- 点击按钮则调用callAndroid()方法 -->
<body>
<button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button>
</body>
</html>
activity代码
package com.ayk.judicial.htmltext;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.HashMap;
import java.util.Set;
/**
* Created by Administrator on 2017/8/4.
* js调用android方法 并把方法的返回值返回给js
*/
public class WebViewJstoAndroid02 extends Activity{
private WebView webView;
private WebSettings webSettings;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jstoandroid);
webView= (WebView) findViewById(R.id.webview_JstoAndroid);
webSettings=webView.getSettings();
webSettings.setJavaScriptEnabled(true); //容许js交互
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //容许弹窗
webView.loadUrl("file:///android_asset/javascript_js02.html");
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所需要调用的android代码 方法 或跳转到的页面
final String result=jsToAndroid(); //js调用android的方法
webView.post(new Runnable() {
@Override
public void run() {
webView.loadUrl("javascript:returnResult('" + result + "')"); //"javascript:returnResult('" + result + "')" "javascript:callJS()"
}
});
// 可以在协议上带有参数并传递到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
Log.e("js调用android方法","----js带来的参数---"+collection.toString());
}
return true;
}
return true;
}
});
webView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(WebViewJstoAndroid02.this);
b.setTitle("Alert js中的弹框");
b.setMessage(message); //js中弹框要显示的内容
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;
}
});
}
public String jsToAndroid(){
return "js调用android方法的返回值";
}
}
第三种:通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
js代码
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function clickprompt(){
// 调用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<!-- 点击按钮则调用clickprompt() -->
<body>
<button type="button" id="button1" onclick="clickprompt()">点击调用Android代码</button>
</body>
</html>
activity代码
package com.ayk.judicial.htmltext;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.HashMap;
import java.util.Set;
/**
* Created by Administrator on 2017/8/4.
* js调用android方法 并把方法的返回值返回给js
*/
public class WebViewJstoAndroid03 extends Activity{
private WebView webView;
private WebSettings webSettings;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jstoandroid);
webView= (WebView) findViewById(R.id.webview_JstoAndroid);
webSettings=webView.getSettings();
webSettings.setJavaScriptEnabled(true); //容许js交互
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //容许弹窗
webView.loadUrl("file:///android_asset/javascript_js03.html");
webView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 根据协议的参数,判断是否是所需要的url
// 一般根据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("demo")) {
//
// 执行JS所需要调用的逻辑
Log.e("js调用了Android的方法","003");
// 可以在协议上带有参数并传递到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
//参数result:代表消息框的返回值(输入值)
String res=jsToAndroid();
result.confirm(res); //可以把方法的返回值传回去
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(WebViewJstoAndroid03.this);
b.setTitle("Alert js中的弹框");
b.setMessage(message); //js中弹框要显示的内容
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 super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
});
}
public String jsToAndroid(){
return "js调用android方法的返回值003";
}
}
文章内容参考:http://blog.csdn.net/carson_ho/article/details/64904691
http://blog.csdn.net/carson_ho/article/details/52693322