技术咨询、项目合作、广告投放、简历咨询、技术文档下载 点击这里 联系博主

# 交互方式总结

Android (opens new window)js (opens new window) 通过 WebView 互相调用方法,实际上是:

  • Android 去调用 JS 的代码

  • JS 去调用 Android 的代码

二者沟通的桥梁是 WebView

对于 android (opens new window) 调用 JS 代码的方法有 2 种:  (必须开启一个线程调用:webview.post(Runnable))

  1. 通过 WebViewloadUrl (),

比如 web.looadUrl("youhtml:onJsMethod(params)");

  1. 通过 WebViewevaluateJavascript ();

比如:webview.evaluateJavascript(" youhtml:onJsMethod(params)",new ValueCallback)

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

  1. 通过 WebView 的 addJavascriptInterface (new 对象,对象别名)进行对象映射  ,

js 端通过传过去的对象别名操作安卓端的映射对象里面的方法

  1. 通过 WebViewClient 的 shouldOverrideUrlLoading ()方法回调拦截 url

  2. 通过 WebChromeClientonJsAlert()、 onJsConfirm()、onJsPrompt()方法回调拦截 JS 对话框 alert()、confirm() 、prompt() 消息

# 具体分析

# Android 通过 WebView 调用 JS 代码

对于 Android 调用 JS 代码的方法有 2 种:

  1. 通过 WebView 的 loadUrl()

  2. 通过 WebView 的 evaluateJavascript()

# 通过 WebView 的 loadUrl()

实例介绍:点击 Android 按钮,即调用 WebView JS(文本名为 javascript)中 callJS()

具体使用:

  • 步骤 1:将需要调用的 JS 代码以.html 格式放到 src/main/assets 文件夹里
  1. 为了方便展示,本文是采用 Andorid 调用本地 JS 代码说明;

  2. 实际情况时, Android 更多的是调用远程 JS 代码,即将加载的 JS 代码路径改成 url 即可

需要加载 JS 代码:JavaScript (opens new window) .html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf\-8" />

    <title>Carson_Ho</title>

    // JS 代码

    <script>
      // Android 需要调用的方法

      function callJS() {
        alert("Android 调用了 JS 的 callJS 方法 ");
      }
    </script>
  </head>
</html>
  • 步骤 2:在 Android 里通过 WebView 设置调用 JS 代码

Android 代码:MainActivity. Java (opens new window)

注释已经非常清楚

public class MainActivity extends AppCompatActivity {

  WebView mWebView;

  Button button;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mWebView = (WebView) findViewById(R.id.webview);

    WebSettings webSettings = mWebView.getSettings();

    // 设置与 Js 交互的权限

    webSettings.setJavaScriptEnabled(true);

    // 设置允许 JS 弹窗

    webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

    // 先载入 JS 代码

    // 格式规定为:file:///android_asset/ 文件名.html

    mWebView.loadUrl("file:///android_asset/javascript.html");

    button = (Button) findViewById(R.id.button);

    button.setOnClickListener(
      new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          // 必须另开线程进行 JS 方法调用(否则无法调用)

          mWebView.post(
            new Runnable() {
              @Override
              public void run() {
                // 注意调用的 JS 方法名要对应上

                // 调用 javascript 的 callJS() 方法

                mWebView.loadUrl("javascript:callJS()");
              }
            }
          );
        }
      }
    );

    // 由于设置了弹窗检验调用结果, 所以需要支持 js 对话框

    // webview 只是载体,内容的渲染需要使用 webviewChromClient 类去实现

    // 通过设置 WebChromeClient 对象处理 JavaScript 的对话框

    //设置响应 js 的 Alert()函数

    mWebView.setWebChromeClient(
      new WebChromeClient() {
        @Override
        public boolean onJsAlert(
          WebView view,
          String url,
          String message,
          final JsResult result
        ) {
          AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);

          b.setTitle("Alert");

          b.setMessage(message);

          b.setPositiveButton(
            android.R.string.ok,
            new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                result.confirm();
              }
            }
          );

          b.setCancelable(false);

          b.create().show();

          return true;
        }
      }
    );
  }
}


特别注意: JS 代码调用一定要在 onPageFinished ()回调之后才能调用,否则不会调用

onPageFinished()属于 WebViewClient 类的方法,主要在页面加载结束时调用

# 通过 WebView 的 evaluateJavascript()

  • 优点:该方法比第一种方法效率更高、使用更简洁。
  1. 因为该方法的执行不会使页面刷新,而第一种方法( loadUrl )的执行则会。

  2. Android 4.4 后才可使用

  • 具体使用
// 只需要将第一种方法的 loadUrl() 换成下面该方法即可

mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
    @Override

    public void onReceiveValue(String value) {

    //此处为 js 返回的结果

    }
    });


上面实现了使用 evaluateJavascript 的方式,得到 js 的返回值。那么使用 loadURL 如何得到 Js 的返回值呢?前面介绍的仅仅只是针对 loadUrl 调用 alert 的情况。如果要使用 loadURL 的话,则要使用对象映射的方式得到返回值:

安卓端:

  1. 网页加载完之后,java 调用 js 方法(注意要在 webview.post(Runnable 中哦)) webView.loadUrl("javascript:shareToFriends()");

  2. 添加对象映射、其中的 androidShare 是对象的别名要传递给 js 的,js 使用它来调用 android 的代码

webView.addJavascriptInterface(new JsToJava(), " androidShare");
private class JsToJava{ //这里需要加@JavascriptInterface,4.2 之后提供给 javascript 调用的函数必须带@JavascriptInterface
    @JavascriptInterface public void jsMethod(String paramFromJS){
        System.out.println("js 返回结果:" + paramFromJS);
    //处理返回的结果
    }
}

网页端:

<script type="text/javascript">
    function shareToFriends() {
      var result =
        "title:'大朴优选:发现光芒之物',desc:'大朴优选:发现光芒之物',link:'http://m.dapu.com/mgallery\-promotion\- 298.html',imgUrl:'http://activity.dapuimg.com/dapu\_20160825000\_r1\_c1.jpg'";
      window.androidShare.jsMethod(result);
  //js 端使用安卓端传递过来的 androidShare 对象,来调用安卓端的方法,并将返回的数据 result 传递给安卓端,从而实现了 JS 和安卓之间的互相的传值。 }
</script>

# load 和 evaluateJavascript 方法对比

方式对比图

# 使用建议

两种方法混合使用,即 Android 4.4 以下使用方法 1,Android 4.4 以上方法 2

// Android 版本变量

final int version = Build.VERSION.SDK_iNT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 18 ) {
	mWebView.loadUrl( "javascript:callJS()");
} else {
	mWebView.evaluateJavascript ("javascript:callJS()", new ValueCallback<String>() {
		@Override
		public void onReceiveValue(String value) {
			//此处为 js 返回的结果
		}
	}
	);
}

# JS 通过 WebView 调用 Android 代码

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

  1. 通过 WebViewaddJavascriptInterface()进行对象映射  ,添加的时候不要在 webview.post 中添加,在主线程中添加

  2. 通过 WebViewClientshouldOverrideUrlLoading 方法回调拦截 url

  3. 通过 WebChromeClientonJsAlert()、onJsConfirm()、 onJsPrompt()方法回调拦截 JS 对话框 alert()、confirm()、 prompt()消息

# 通过 WebView 的 addJavascriptInterface()进行对象映射

定义一个与 JS 对象映射关系的 Android 类:AndroidtoJs

// 继承自 Object 类
public class AndroidtoJs extends Object {
	// 定义 JS 需要调用的方法
	// 被 JS 调用的方法必须加入@JavascriptInterface 注解
	@JavascriptInterface
	public void hello(String msg) {
		Toast.make(MainActivity.this, "JS 调用了 Android 的 hello 方法并且传递过来的数据是 "+msg,Toast.lONG).show();
	}
}

将需要调用的 JS 代码以.html 格式放到 src/main/assets 文件夹里

需要加载 JS 代码:javascript (opens new window) .html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf\-8" />

    <title>Carson</title>

    <script>
      function callAndroid() {
        // 由于对象映射,所以调用 test 对象等于调用 Android 映射的对象

        test.hello("js 调用了 android 中的 hello 方法 ");
      }
    </script>
  </head>

  <body>
    // 点击按钮则调用 callAndroid 函数

    <button type="button" id="button1" onclick="callAndroid()"></button>
  </body>
</html>

在 Android 里通过 WebView 设置 Android 类与 JS 代码的映射


public class MainActivity extends AppCompatActivity {

WebView mWebView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mWebView = (WebView) findViewById(R.id.webview);

WebSettings webSettings = mWebView.getSettings();

// 设置与 Js 交互的权限

webSettings.setJavaScriptEnabled( true);

// 通过 addJavascriptInterface() 将 Java 对象映射到 JS 对象

//参数 1 :Javascript 对象名

//参数 2 :Java 对象名

mWebView.addJavascriptInterface( new AndroidtoJs(), "test"); //AndroidtoJS 类对象映射到 js 的 test 对象

// 加载 JS 代码

// 格式规定为:file:///android_asset/ 文件名.html

mWebView.loadUrl( "file:///android_asset/javascript.html");

效果图

特点

# 通过 WebViewClient 的方法 shouldOverrideUrlLoading ()回调拦截 url

  • 具体原理:
  1. Android 通过 WebViewClient 的回调方法 shouldOverrideUrlLoading () 拦截 url

  2. 解析该 url 的协议

  3. 如果检测到是预先约定好的协议,就调用相应方法

  • 具体使用:

在 JS 约定所需要的 Url 协议

<!DOCTYPE html>

<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>

当该 JS 通过 Android 的 mWebView.loadUrl("file:///android_asset/javascript.html")加载后,就会回调 shouldOverrideUrlLoading (),

接下来继续看步骤 2 :

在 Android 通过 WebViewClient 复写 shouldOverrideUrlLoading ()

//MainActivity.java
public class MainActivity extends AppCompatActivity {
	WebView mWebView;
	//    Button button;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mWebView = (WebView) findViewById(R.id.webview);
		WebSettings webSettings = mWebView.getSettings();
		// 设置与 Js 交互的权限
		webSettings.setJavaScriptEnabled( true);
		// 设置允许 JS 弹窗
		webSettings.setJavaScriptCanOpenWindowsAutomatically( true);
		// 步骤 1 :加载 JS 代码
		// 格式规定为:file:///android_asset/ 文件名.html
		mWebView.loadUrl( "file:///android_asset/javascript.html");
		// 复写 WebViewClient 类的 shouldOverrideUrlLoading 方法
		mWebView.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 所需要调用的逻辑
						System.out.println( "js 调用了 Android 的方法");
						// 可以在协议上带有参数并传递到 Android 上
						HashMap<String, String> params = new HashMap<>();
						Set<String> collection = uri.getQueryParameterNames();
						String value="";
						for (String str:collection){
							value=value+"键:"+str+"值:"+uri.getQueryParameter(str);
							params.put(str,uri.getQueryParameter(str));
						}
					}
					return true ;
				}
				return super .shouldOverrideUrlLoading(view, url);
			}
		}
		);
	}

效果图

特点

  • 优点:不存在方式 1 的漏洞;

  • 缺点: JS 获取 Android 方法的返回值复杂。

如果 JS 想要得到 Android 方法的返回值,只能通过 WebView 的 loadUrl()去执行 JS 方法把返回值传递回去,相关的代码如下:

// Android:MainActivity.java

mWebView.loadUrl("javascript:returnResult(" + result + ")");

// JS:javascript.html

function returnResult(result) {

alert( "result is" + result);

}

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

在 JS 中,有三个常用的对话框方法:

方式 3 的原理:Android 通过 WebChromeClient 的 onJsAlert()、onJsConfirm() 、onJsPrompt()方法回调分别拦截 JS 对话框(即上述三个方法),得到他们的消息内容,然后解析即可

下面的例子将用拦截 JS 的输入框(即 prompt()方法) 说明 :

  1. 常用的拦截是:拦截 JS 的输入框(即 prompt() 方法)

  2. 因为只有 prompt() 可以返回任意类型的值,操作最全面方便、更加灵活;而 alert ()对话框没有返回值;confirm ()对话框只能返回两种状态(确定 / 取消)两个值

步骤 1:加载 JS 代码,如下:

<!DOCTYPE html>

<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>

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

  1. 如果是拦截警告框(即 alert()),则触发回调 onJsAlert() ;

  2. 如果是拦截确认框(即 confirm()),则触发回调 onJsConfirm() ;

在 Android 通过 WebChromeClient 复写 onJsPrompt()

public class MainActivity extends AppCompatActivity {
	WebView mWebView;
	//    Button button;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mWebView = (WebView) findViewById(R.id.webview);
		WebSettings webSettings = mWebView.getSettings();
		// 设置与 Js 交互的权限
		webSettings.setJavaScriptEnabled( true);
		// 设置允许 JS 弹窗
		webSettings.setJavaScriptCanOpenWindowsAutomatically( true);
		// 先加载 JS 代码
		// 格式规定为:file:///android_asset/ 文件名.html
		mWebView.loadUrl( "file:///android_asset/javascript.html");
		mWebView.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);
			}
			// 通过 alert() 和 confirm()拦截的原理相同,此处不作过多讲述
			// 拦截 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);
			}
		}
		);
	}
}

效果图

效果图

4、 三种方式的对比 & 使用场景

对比

【未经作者允许禁止转载】 Last Updated: 2/4/2024, 6:06:40 AM