最近在做hybrid 开发 涉及js调用java的问题,android4.2之下有漏洞 所以4.2之下不能使用官方的addJavascriptInterface 方法实现。查了很多资料 找到一个比较好的解决方案,参考了github上以为大牛下的点击打开链接https://github.com/pedant/safe-java-js-webview-bridge,他是基于webView的WebChromeClient 的监听js.prompt方法实现的,但是他的方法有一点不够灵活,就是只能调用本地的静态方法,然后静态方法的第一个参数必须为WebView 然后反射调用。然而我们很多时候 需要在方法里面使用成员属性,这个时候就不方便 我在他的核心代码上做了一些修改!
<span style="font-size:14px;color:#333333;">public class JsCallJava<T> {
private final static String TAG = "JsCallJava";
private final static String RETURN_RESULT_FORMAT = "{\"code\": %d, \"result\": %s}";
private HashMap<String, Method> mMethodsMap;
private String mInjectedName;
private String mPreloadInterfaceJS;
private Gson mGson;
//当前对象
private T mInstance;
public JsCallJava(String injectedName, T instance) {
try {
if (TextUtils.isEmpty(injectedName)) {
throw new Exception("injected name can not be null");
}
mInstance = instance;
mInjectedName = injectedName;
mMethodsMap = new HashMap<String, Method>();
//获取自身声明的所有方法(包括public private protected), getMethods会获得所有继承与非继承的方法
Method[] methods = instance.getClass().getDeclaredMethods();
StringBuilder sb = new StringBuilder("javascript:(function(b){console.log(\"");
sb.append(mInjectedName);
sb.append(" initialization begin\");var a={queue:[],callback:function(){var d=Array.prototype.slice.call(arguments,0);var c=d.shift();var e=d.shift();this.queue[c].apply(this,d);if(!e){delete this.queue[c]}}};");
for (Method method : methods) {
String sign;
// Log.e("print",method.getName()+"==isPublic:"+);
if (!Modifier.isPublic(method.getModifiers()) || (sign = genJavaMethodSign(method)) == null) {
continue;
}
mMethodsMap.put(sign, method);
sb.append(String.format("a.%s=", method.getName()));
}
sb.append("function(){var f=Array.prototype.slice.call(arguments,0);if(f.length<1){throw\"");
sb.append(mInjectedName);
sb.append(" call error, message:miss method name\"}var e=[];for(var h=1;h<f.length;h++){var c=f[h];var j=typeof c;e[e.length]=j;if(j==\"function\"){var d=a.queue.length;a.queue[d]=c;f[h]=d}}var g=JSON.parse(prompt(JSON.stringify({method:f.shift(),types:e,args:f})));if(g.code!=200){throw\"");
sb.append(mInjectedName);
sb.append(" call error, code:\"+g.code+\", message:\"+g.result}return g.result};Object.getOwnPropertyNames(a).forEach(function(d){var c=a[d];if(typeof c===\"function\"&&d!==\"callback\"){a[d]=function(){return c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))}}});b.");
sb.append(mInjectedName);
sb.append("=a;console.log(\"");
sb.append(mInjectedName);
sb.append(" initialization end\")})(window);");
mPreloadInterfaceJS = sb.toString();
} catch (Exception e) {
Log.e(TAG, "init js error:" + e.getMessage());
}
}</span>
上面的构造函数中我把 当前的对象以泛型的方式传入了进来供反射调用的使用,注册方法的时候 我是使用了自定义的注解JsInterface,判断有此注解的方法才能被注入js回调
<strong><span style="font-size:14px;">private String genJavaMethodSign(Method method) {
String sign = method.getName();
Class[] argsTypes = method.getParameterTypes();
int len = argsTypes.length;
Log.e("print", len + "===" + method.toString());
//包含该注解的方法才可以使用的
</span><span style="font-size:18px;color:#ff0000;"> JsInterface jsMeta = method.getAnnotation(JsInterface.class);
if (jsMeta == null) {
Log.w(TAG, "method(" + sign + ") must have annotation @JsInterface ,or will be pass");
return null;
}</span><span style="font-size:14px;">
//Log.e("print","meta:"+meta);
// Log.e("print","isSameClass"+(argsTypes[0]==mInstance.getClass().getSuperclass()));
// if (len < 1 || argsTypes[0] != mInstance.getClass()) {
// Log.w(TAG, "method(" + sign + ") must use webview to be first parameter, will be pass");
// // return null;
// }
// if (len < 1 || argsTypes[0] != mInstance.getClass()) {
// Log.w(TAG, "method(" + sign + ") must use webview to be first parameter, will be pass");
// // return null;
// }
for (int k = 0; k < len; k++) {
Class cls = argsTypes[k];
if (cls == String.class) {
sign += "_S";
} else if (cls == int.class ||
cls == long.class ||
cls == float.class ||
cls == double.class) {
sign += "_N";
} else if (cls == boolean.class) {
sign += "_B";
} else if (cls == JSONObject.class) {
sign += "_O";
} else if (cls == JsCallback.class) {
sign += "_F";
} else {
sign += "_P";
}
}
return sign;
}</span></strong>
在反射调用的时候把当前对象传入,就可以调用成员方法了,这样的话整个架构就比较灵活,感谢safe-java-js-webview-bridge的作者提供了这么好的解决方案!<span style="font-size:14px;">public String call(WebView webView, String jsonStr) { if (!TextUtils.isEmpty(jsonStr)) { try { JSONObject callJson = new JSONObject(jsonStr); String methodName = callJson.getString("method"); JSONArray argsTypes = callJson.getJSONArray("types"); JSONArray argsVals = callJson.getJSONArray("args"); //Log.e("print",methodName+"="+argsTypes+"="+argsVals); String sign = methodName; int len = argsTypes.length(); Object[] values = new Object[len]; int numIndex = 0; String currType; for (int k = 0; k < len; k++) { currType = argsTypes.optString(k); if ("string".equals(currType)) { sign += "_S"; values[k] = argsVals.isNull(k) ? null : argsVals.getString(k); } else if ("number".equals(currType)) { sign += "_N"; //为了标记for循环的判断 如果第一位就是number类型排除0 numIndex = numIndex * 10 + k + 1; Log.e("print", "numIndex:" + numIndex); } else if ("boolean".equals(currType)) { sign += "_B"; values[k] = argsVals.getBoolean(k); } else if ("object".equals(currType)) { sign += "_O"; values[k] = argsVals.isNull(k) ? null : argsVals.getJSONObject(k); } else if ("function".equals(currType)) { sign += "_F"; values[k] = new JsCallback(webView, mInjectedName, argsVals.getInt(k)); } else { sign += "_P"; } } Method currMethod = mMethodsMap.get(sign); // 方法匹配失败 if (currMethod == null) { return getReturn(jsonStr, 500, "not found method(" + sign + ") with valid parameters"); } // 数字类型细分匹配 if (numIndex > 0) { Class[] methodTypes = currMethod.getParameterTypes(); int currIndex; Class currCls; while (numIndex > 0) { currIndex = (numIndex - numIndex / 10 * 10) - 1; currCls = methodTypes[currIndex]; if (currCls == int.class) { values[currIndex] = argsVals.getInt(currIndex); } else if (currCls == long.class) { //WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number values[currIndex] = Long.parseLong(argsVals.getString(currIndex)); } else { values[currIndex] = argsVals.getDouble(currIndex); } // Log.e("print",currIndex+"=numIndex="+numIndex+" value:"+ values[currIndex]); numIndex /= 10; } } </span><span style="font-size:24px;color:#ff0000;">return getReturn(jsonStr, 200, currMethod.invoke(mInstance, values));</span><span style="font-size:14px;"> } catch (Exception e) { //优先返回详细的错误信息 if (e.getCause() != null) { return getReturn(jsonStr, 500, "method execute error:" + e.getCause().getMessage()); } return getReturn(jsonStr, 500, "method execute error:" + e.getMessage()); } } else { return getReturn(jsonStr, 500, "call data empty"); } }</span>
最后上两张图片
最后资源地址:http://download.csdn.net/detail/issingleman/9540271