JS与Native交互

Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本。本文将介绍如何实现Java代码和Javascript代码的相互调用。

JS与Native交互

实现JS和Native有两种方式:

shouldOverrideUrlLoading(WebView view, String url) js与java互相调用

先来说一下第一种方式shouldOverrideUrlLoading(WebView view, String url)

通过给WebView加一个事件监听对象(WebViewClient)并重写shouldOverrideUrlLoading(WebView
view, String
url)方法。当按下某个连接时WebViewClient会调用这个方法,并传递参数view和url

开始第二种.JS和Java互调

如何实现java和js互调

WebView开启JavaScript脚本执行
WebView设置供JavaScript调用的交互接口客户端和网页端编写调用对方的代码

JS调用JAVA
JS : window.jsInterfaceName.methodName(parameterValues)
native: webView.addJavascriptInterface(new JsInteration(),
“androidNative”);

下面给出一个实例,方便理解

webView.addJavascriptInterface(new JsInteration(), “androidNative”);
@JavascriptInterface
public void helloJS(){…}

window.androidNative.helloJS();

Java调用JS

webView调用js的基本格式为webView.loadUrl(“javascript:methodName(parameterValues)”)

调用js无参无返回值函数: String call
=“javascript:sayHello()”;webView.loadUrl(call);
调用js有参无返回值函数:String call = “javascript:alertMessage(”” +
“content” + “”)”; webView.loadUrl(call); 调用js有参数有返回值的函数
Android在4.4之前并没有提供直接调用js函数并获取值的方法,所以在此之前,常用的思路是
java调用js方法,js方法执行完毕,再次调用java代码将值返回。
Android 4.4之后使用evaluateJavascript即可。

private void testEvaluateJavascript(WebView webView) {
  webView.evaluateJavascript("getGreetings()", new ValueCallback() {
  @Override
  public void onReceiveValue(String value) {
      Log.i(LOGTAG, "onReceiveValue value=" + value);
  }});
}

注:

参数类型如果是简单的int或String,可以直接传,对于复杂的数据类型,建议以字符串形式的json返回。
evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。

当native与js交互时存cookie看到很多人遇到过这样一个问题,cookie存不进去,网上有很多解释方案,但是很多没说到重点上,这里直接贴一下代码:

public static void synCookies(Context context, String url, String version) {
        CookieSyncManager.createInstance(context);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        cookieManager.removeAllCookie();
        cookieManager.setCookie(url, "sessionKey=" + UserInfoShareprefrence.getInstance(context).getLocalSessionKey());
        cookieManager.setCookie(url, "productVersion=android-epocket-v" + version);
        CookieSyncManager.getInstance().sync();

    }

存不进去的很大一部分原因是你的url不对,他官方给出的解释是这样的

 /**
     * Sets a cookie for the given URL. Any existing cookie with the same host,
     * path and name will be replaced with the new cookie. The cookie being set
     * will be ignored if it is expired.
     *
     * @param url the URL for which the cookie is to be set
     * @param value the cookie as a string, using the format of the 'Set-Cookie'
     *              HTTP response header
     */
    public void setCookie(String url, String value) {
        throw new MustOverrideException();
    }

其实没说明白url到底是什么,这里的url就是显示的url的域名,这里顺便贴出取域名的方法,给出的是通过正则提取域名

    /**
     * 获得域名
     *
     * @param url
     * @return
     */
    public static String getDomain(String url) {
        Pattern p = Pattern.compile("[^//]*?\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE);
        Matcher matcher = p.matcher(url);
        matcher.find();
        return matcher.group();
    }

澳门新浦京app下载,还有一点就是,如果你想传递多个值给cookie的话,可以多次使用setCookie,不要擅自的自己拼值,因为你拼的字符串中可能存在分号,内部多分号做了特殊处理,截取分号之前的,之后的直接放弃!

实现JS和Native有两种方式:
shouldOverrideUrlLoading(WebView view, String url) js与java互相调用
先来说一下第一种方式shouldOverrideUrlLoadin…

如何实现

实现Java和js交互十分便捷。通常只需要以下几步。

  • WebView开启JavaScript脚本执行
  • WebView设置供JavaScript调用的交互接口。
  • 客户端和网页端编写调用对方的代码。

一、Android调用JS方法

本例代码

为了便于讲解,先贴出全部代码

Java代码

package com.example.javajsinteractiondemo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class MainActivity extends Activity {
  private static final String LOGTAG = "MainActivity";
  @SuppressLint("JavascriptInterface")
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      final WebView myWebView = (WebView) findViewById(R.id.myWebView);
      WebSettings settings = myWebView.getSettings();
      settings.setJavaScriptEnabled(true);
      myWebView.addJavascriptInterface(new JsInteration(), "control");
      myWebView.setWebChromeClient(new WebChromeClient() {});
      myWebView.setWebViewClient(new WebViewClient() {

          @Override
          public void onPageFinished(WebView view, String url) {
              super.onPageFinished(view, url);
              testMethod(myWebView);
          }

      });
      myWebView.loadUrl("file:///android_asset/js_java_interaction.html");
  }

  private void testMethod(WebView webView) {
      String call = "javascript:sayHello()";

      call = "javascript:alertMessage("" + "content" + "")";

      call = "javascript:toastMessage("" + "content" + "")";

      call = "javascript:sumToJava(1,2)";
      webView.loadUrl(call);

  }

  public class JsInteration {

      @JavascriptInterface
      public void toastMessage(String message) {
          Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
      }

      @JavascriptInterface
      public void onSumResult(int result) {
          Log.i(LOGTAG, "onSumResult result=" + result);
      }
  }

}

前端网页代码

<html>
<script type="text/javascript">
    function sayHello() {
        alert("Hello")
    }

    function alertMessage(message) {
        alert(message)
    }

    function toastMessage(message) {
        window.control.toastMessage(message)
    }

    function sumToJava(number1, number2){
       window.control.onSumResult(number1 + number2)
    }
</script>
Java-Javascript Interaction In Android
</html>

1.Java调用js代码(Android4.4以前)

调用示例

mWebView.loadUrl(“javascript:sum(3,8)”);

js调用Java

调用格式为window.jsInterfaceName.methodName(parameterValues)
此例中我们使用的是control作为注入接口名称。

function toastMessage(message) {
  window.control.toastMessage(message)
}

function sumToJava(number1, number2){
   window.control.onSumResult(number1 + number2)
}

注意,这里通过addJavascriptInterface将MyActiviy所对应的对象注入到WebView中了。

Java调用JS

webView调用js的基本格式为webView.loadUrl(“javascript:methodName(parameterValues)”)

js函数处理,并将结果通过调用java方法返回

调用js无参无返回值函数

String call = "javascript:sayHello()";
webView.loadUrl(call);

function sum(i,m){

调用js有参无返回值函数

注意对于字符串作为参数值需要进行转义双引号。

String call = "javascript:alertMessage("" + "content" + "")";
webView.loadUrl(call);

var result = i+m;

调用js有参数有返回值的函数

Android在4.4之前并没有提供直接调用js函数并获取值的方法,所以在此之前,常用的思路是
java调用js方法,js方法执行完毕,再次调用java代码将值返回。

1.Java调用js代码

String call = "javascript:sumToJava(1,2)";
webView.loadUrl(call);

2.js函数处理,并将结果通过调用java方法返回

function sumToJava(number1, number2){
       window.control.onSumResult(number1 + number2)
}

3.Java在回调方法中获取js函数返回值

@JavascriptInterface
public void onSumResult(int result) {
  Log.i(LOGTAG, "onSumResult result=" + result);
}

document.getElementById(“h”).innerHTML= result;

4.4处理

Android 4.4之后使用evaluateJavascript即可。这里展示一个简单的交互示例
具有返回值的js方法

function getGreetings() {
      return 1;
}

java代码时用evaluateJavascript方法调用

private void testEvaluateJavascript(WebView webView) {
  webView.evaluateJavascript("getGreetings()", new ValueCallback<String>() {

  @Override
  public void onReceiveValue(String value) {
      Log.i(LOGTAG, "onReceiveValue value=" + value);
  }});
}

输出结果

I/MainActivity( 1432): onReceiveValue value=1

注意

  • 上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。
  • evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。

}

疑问解答

2.JAVA中如何得到JS中的返回值(Android4.4之后)

Alert无法弹出

你应该是没有设置WebChromeClient,按照以下代码设置

myWebView.setWebChromeClient(new WebChromeClient() {});

Android 4.4之后使用evaluateJavascript即可。这里展示一个简单的交互示例

Uncaught ReferenceError: functionName is not defined

问题出现原因,网页的js代码没有加载完成,就调用了js方法。解决方法是在网页加载完成之后调用js方法

myWebView.setWebViewClient(new WebViewClient() {

  @Override
  public void onPageFinished(WebView view, String url) {
      super.onPageFinished(view, url);
      //在这里执行你想调用的js函数
  }

});

先写一个具有返回值的js方法

Uncaught TypeError: Object [object Object] has no method

function getGreetings(str) {

安全限制问题

如果只在4.2版本以上的机器出问题,那么就是系统处于安全限制的问题了。Android文档这样说的

Caution: If you’ve set your targetSdkVersion to 17 or higher, you must
add the @JavascriptInterface annotation to any method that you want
available your web page code (the method must also be public). If you
do not provide the annotation, then the method will not accessible by
your web page when running on Android 4.2 or higher.

中文大意为

警告:如果你的程序目标平台是17或者是更高,你必须要在暴露给网页可调用的方法(这个方法必须是公开的)加上@JavascriptInterface注释。如果你不这样做的话,在4.2以以后的平台上,网页无法访问到你的方法。

return str;

两种解决方法
  • 将targetSdkVersion设置成17或更高,引入@JavascriptInterface注释
  • 自己创建一个注释接口名字为@JavascriptInterface,然后将其引入。注意这个接口不能混淆。

注,创建@JavascriptInterface代码

public @interface JavascriptInterface {

}

}

代码混淆问题

如果在没有混淆的版本运行正常,在混淆后的版本的代码运行错误,并提示Uncaught
TypeError: Object [object Object] has no
method,那就是你没有做混淆例外处理。 在混淆文件加入类似这样的代码

-keep class com.example.javajsinteractiondemo$JsInteration {
    *;
}

java代码时用evaluateJavascript方法调用:(注意:1.evaluateJavascript执行JS方法时,网页必须加载完毕 
2.网页中的JS方法是全局方法

All WebView methods must be called on the same thread

过滤日志曾发现过这个问题。

E/StrictMode( 1546): java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {528712d4} called on Looper (JavaBridge, tid 121) {52b6678c}, FYI main Looper is Looper (main, tid 1) {528712d4})
E/StrictMode( 1546):   at android.webkit.WebView.checkThread(WebView.java:2063)
E/StrictMode( 1546):   at android.webkit.WebView.loadUrl(WebView.java:794)
E/StrictMode( 1546):   at com.xxx.xxxx.xxxx.xxxx.xxxxxxx$JavaScriptInterface.onCanGoBackResult(xxxx.java:96)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
E/StrictMode( 1546):   at android.os.Handler.dispatchMessage(Handler.java:102)
E/StrictMode( 1546):   at android.os.Looper.loop(Looper.java:136)
E/StrictMode( 1546):   at android.os.HandlerThread.run(HandlerThread.java:61)

在js调用后的Java回调线程并不是主线程。如打印日志可验证

ThreadInfo=Thread[WebViewCoreThread,5,main]

解决上述的异常,将webview操作放在主线程中即可。

webView.post(new Runnable() {
    @Override
    public void run() {
        webView.loadUrl(YOUR_URL).
    }
});

private void testEvaluateJavascript(WebView webView) {

webView.evaluateJavascript(“javascript:getGreetings(‘”+”hello
world!”+”‘)”,newValueCallback() {

@Override

publicvoidonReceiveValue(String value) {

Log.i(LOGTAG,”onReceiveValue value=”+ value);

}

});

}

从上面的用法中很明显看到,通过evaluateJavascript调用JS中的方法,可以向其中添加结果回调,来接收JS的return值。

注意:

上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。

evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。

—————————————————————-分割线————

二、JS调用Android

在WebView中使用JavaScript

如果在你的WebView中你将要加载的网页使用JavaScript,你必须为你的WebView启用JavaScript。一旦Javascript启用,你可以在你的应用代码和你的JavaScript代码之间创建接口。

Enabling JavaScript

启用Javascript

Javascript在WebView中默认是禁用的,你必须为你的WebView启用Javascript。你可以通过getSetting()方法获取WebSettings,然后使用setJavaScriptEnabled())启用Javascript。

例如:

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

WebSettings webSettings=myWebView.getSettings();

webSettings.setJavaScriptEnabled(true);

WebSettings提供了访问各种各样的其他有用的设置。例如,如果你正在开发一款在你的Android应用中指定WebView设计的web应用,然后你可以使用setUserAgentString()定义一个自定义用户代理字符,然后在你的网页中查询自定义用户代理字符来核实客户端请求网页确实来自Android应用。

Binding JavaScript code to Android code

绑定JavaScript代码到Android代码

当正在开发一款在Android应用中指定WebView设计的web应用时,你可以在你的Javascript和客户端Android代码之间创建接口。例如,在你的Android代码中你的Javascript代码可以调用一个方法来显示一个对话框Dialog,而不是使用Javascript的alert()功能。

在你的JavaScript和Android代码之间,要绑定一个新的接口,须调用addJavascriptInterface())方法,给这个方法传进一个绑定到你的JavaScript的类实例和一个(你的JavaScript可以调用来访问这个类)的接口名。

例如,你可以在你的Android应用中包含下面的类:

public class WebAppInterface{

ContextmContext;

/** Instantiate the interface and set the context */

WebAppInterface(Contextc){

mContext=c;

}

/** Show a toast from the web page */

@JavascriptInterface

publicvoidshowToast(Stringtoast){

Toast.makeText(mContext,toast,Toast.LENGTH_SHORT).show();

}

}

注意:如果你已经设置你的 targetSdkVersion
为17或者更高版本,你必须给你的的JavaScript可以获取到的方法(方法必须是Public)添加@JavascriptInterface注释。如果你不提供注释,当你的网页运行在Android
4.2或者更高版本的系统时,这些方法是获取不到的。

在这个例子中,WebAppInterface类允许网页使用showToast()方法创建一个Toast信息。

通过addJavascriptInterface())方法,你可以绑定这个类到你的WebView运行的JavaScript中,并命名Android接口。例如:

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

webView.addJavascriptInterface(newWebAppInterface(this),”Android”);

这给运行在WebView中的JavaScript创建了叫“Android”的接口。在这里,你的网页应用可以访问WebAppInerface类。例如,这有一些HTML和JavaScript(当用户点击按钮时使用新接口创建toast信息的Javascript):

function showAndroidToast(toast){

Android.showToast(toast);

}

从JavaScript初始化Android接口是不需要的。WebView自动使接口可以被你的网页获取。所以,点击按钮时,showAndroidToast()功能使用Android接口调用WebAppInterface.showToast()方法。

注:绑定到你的JavaScript对象运行在另一个线程,而不是在它被创建的线程。

注意:使用addJavascriptInterface())方法JavaScript控制你的Android应用。这是非常有用的特点或者是一个危险的安全问题。当HTML在WebView中是不可信的(例如,部分或全部的HTML是由不明身份的人和进程提供的),攻击者可以在你的HTML中执行你的客户端代码,并且攻击者选择的代码可能是任何代码。这样,除非在你的WebView中的所有的HTML和JavaScript代码都是你写的,否则,你不要使用addJavascriptInterface())方法。在你的WebView中,你也应该不要允许用户去导航到其他的不是你自己的网页(相反,允许用户的默认浏览器去打开不相关的链接——默认情况下,用户的浏览器打开所有的链接,所以在下面小节的描述中如果你处理网页导航应小心)。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图