1.在WebView中使用JavaScript

Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中JavaScript又是一个很举足轻重的脚本。因此,使用WebView时经常会涉及到Android代码和JavaScript代码的交互。

实现Java和js交互通常只需要以下几步:

①WebView开启JavaScript脚本执行。

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

WebSettings webSettings = myWebView.getSettings();

webSettings.setJavaScriptEnabled(true);

②WebView设置供JavaScript调用的交互接口。

③客户端和网页端编写调用对方的代码。

 

2.JavaScript调用Android中的方法

比如,可以用JavaScript代码调用Android代码中的方法,来展现一个对话框,而不是使用JS中的alert()方法。

在JS和Android代码间绑定一个新的接口,需要调用addJavascriptInterface()方法。该方法参数传入一个Java对象实例和一个字符串,该字符串是一个名字(interface name,注意此接口不是通常所说的那个用来实现的接口,而是传入的这个对象在JS中的别名),在JS代码中用此名字调用该Java对象的方法。

addJavascriptInterface()方法可以让JS代码控制宿主程序,这是一个非常有力的特性,但是同时也存在一些安全问题,因为进一步JS代码可以通过反射访问到注入对象的公有域。攻击者可能会在HTML和JavaScript中包含了有威胁性的代码。所以Android 4.1,API 17开始,只有被JavascriptInterface注解标识的公有方法可以被JS代码访问。

另外,因为JS代码和Java对象在这个WebView所私有的后台线程交互,所以还需要注意线程安全性问题。

注意,与JS代码绑定的的这个Java对象运行在另一个线程中,与创建它的线程不是一个线程。而且这个Java对象的域是不可访问的。

//自定义的Android代码和JavaScript代码之间的桥梁类
public class WebAppInterface {
    Context mContext;
    WebAppInterface(Context c) {
        mContext = c;
    }
    // 如果target>=API 17,则需要加上如下注解
   @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_LONG).show();
    }
}

然后将这个类和WebView中的JS代码绑定:

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android"); //给这个对象起的别名叫“Android”

这样就创立了一个接口名,叫“Android”,运行在WebView中的JS代码可以通过这个名字调用WebAppInterface类中的showToast()方法:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

 

3.Android调用JavaScript代码

这个还比较简单,需要调用的时候只需要一行代码:

myWebView.loadUrl("javascript:myFunction()");

其中myFunction()是JS函数。

注意:如果JavaScript函数是带参数的,那么调用时要特别注意。

比如下面这个JS函数,在原来内容上加入一行:

function writeLine(string) {
    console.log("Write a new Line");
    document.getElementById("content").inn erHTML += string + "<br />";//在content标签段落加入新行
}

其中content是自定义的标签,html中有一个段落是:<p id="content"></p>

那么在Android代码中调用这个writeLine()函数时,需要传入一个字符串参数,比如想要传入一个叫name的String:

myWebView.loadUrl("javascript:writeLine('"+name+"')");//JS代码要是带参数

注意:双引号中的函数名一定不要写错。

 

4.使用举例

①Android端代码:

public class MainActivity extends Activity {
  @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(this, 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>

③JS调用Java

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

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

④Java调用JS

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

1)调用js无参无返回值函数:

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

2)调用js有参无返回值函数:

String call = "javascript:alertMessage(\"" + "content" + "\")"; //注意对于字符串作为参数值需要进行转义双引号。
webView.loadUrl(call);

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

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

第一步:Java调用js代码:

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

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

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

第三步:Java在回调方法中获取js函数返回值

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

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也执行在主线程。