android的WebView是一个非常强大的控件,本文主要针对其简单使用和笔者在使用时所遇到的问题做一些总结。
本文参考了该博文:http://blog.csdn.net/zgjxwl/article/details/9627685
一、WebView中Java与javascript交互
1.这是要和js交互的注入接口类:
public final class JavascriptInteerface{ @JavascriptInterface public void test(String teststr) { Toast.makeText(getApplicationContext(), teststr, Toast.LENGTH_LONG).show(); } @JavascriptInterface public void test1() { // 调用js代码 webView.loadUrl("javascript:testJavaCall(‘这是java调js的方法‘)"); } }
2.这是交互的代码,其中data里的window.testObject.test(‘这是js掉java的Test方法‘)是调用java的代码
webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavascriptInteerface(), "testObject"); // 测试的HTML代码 String data ="<HTML><HEAD><script type=\"text/javascript\">function testJavaCall(str){alert(str);};</script></HEAD><BODY><H1>TEST1</H1></BODY><BR /><INPUT type=\"button\" value=\"js调用java\" onclick=\"window.testObject.test(‘这是js掉java的Test方法‘)\" /><INPUT type=\"button\" value=\"java调js\" onclick=\"window.testObject.test1()\" /></HTML>"; // 如果是网页,直接用loadUrl(url)方法 webView.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
二、使用时遇见android版本兼容的问题
WebView存在android系统版本升级导致的兼容性问题。本文主要讨论的是java和js的交互问题。4.2版本之前和4.2版本(包含)之后接入js是有所区别的。
在之前,笔者使用的注入交互接口类如下:
public final class JavascriptInteerface{ public void test() { Toast.makeText(getApplicationContext(), "这是java代码的Test方法", Toast.LENGTH_LONG).show(); } }
调用为:
webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavascriptInteerface(), "testObject");
按照上面的方法,在我的手机(android系统版本为4.4.2)上运行没有反应,查看log时显示如下错误:
E/Web Console﹕ Uncaught TypeError: Object [object Object] has no method ‘test‘ at about:blank:1
原因:因为这个接口允许JavaScript 控制宿主应用程序,这是个很强大的特性,但同时,在4.2的版本前存在重大安全隐患,因为JavaScript 可以使用反射访问注入webview的java对象的public fields,在一个包含不信任内容的WebView中使用这个方法,会允许攻击者去篡改宿主应用程序,使用宿主应用程序的权限执行java代码。因此4.2以后,任何为JS暴露的接口,都需要加@JavascriptInterface注释,这样,这个Java对象的fields 将不允许被JS访问。
官网对于该问题大概描述如下:
public void addJavascriptInterface (Object object, String name)
Added in API level 1
Injects the supplied Java object into this WebView. The object is injected into the JavaScript context of the main frame, using the supplied name. This allows the Java object‘s methods to be accessed from JavaScript. For applications targeted to API level JELLY_BEAN_MR1
and above, only public methods that are annotated with JavascriptInterface
can be accessed from JavaScript. For applications targeted to API level JELLY_BEAN
or below, all public methods (including the inherited ones) can be accessed, see the important security note below for implications.
Note that injected objects will not appear in JavaScript until the page is next (re)loaded. For example:
class JsObject { @JavascriptInterface public String toString() { return "injectedObject"; } } webView.addJavascriptInterface(new JsObject(), "injectedObject"); webView.loadData("", "text/html", null); webView.loadUrl("javascript:alert(injectedObject.toString())");
IMPORTANT:
- This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for apps targeting
JELLY_BEAN
or earlier. Apps that target a version later thanJELLY_BEAN
are still vulnerable if the app runs on a device running Android earlier than 4.2. The most secure way to use this method is to targetJELLY_BEAN_MR1
and to ensure the method is called only when running on Android 4.2 or later. With these older versions, JavaScript could use reflection to access an injected object‘s public fields. Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content. - JavaScript interacts with Java object on a private, background thread of this WebView. Care is therefore required to maintain thread safety.
- The Java object‘s fields are not accessible.
- For applications targeted to API level
LOLLIPOP
and above, methods of injected Java objects are enumerable from JavaScript.
所以,要解决上述问题:只需要在每个Javascript要调用的Java接口中得每个方法前加上这句@JavascriptInterface。例如我之前的代码改为
public final class JavascriptInteerface{ @JavascriptInterface public void test() { Toast.makeText(getApplicationContext(), "这是java代码的Test方法", Toast.LENGTH_LONG).show(); } }
至此,该问题就得到解决。除了该问题外,在做IOS和Android同时跟同一个HTML5中得JS交互时,也会遇到一个问题,主要是向注入js对象不一样导致,这种解决方法最好是在IOS向HTML动态注入js代码;也可以在JS中判断客户端使用的设备,在此就不多做说明。