github:
https://github.com/wendux/DSBridge-Android
1、Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。
2、同时支持同步调用和异步调用
3、支持以类的方式集中统一管理API
4、支持API命名空间
5、支持调试模式
6、支持API存在性检测
7、支持进度回调:一次调用,多次返回
8、支持Javascript关闭页面事件回调
9、支持Javascript 模态/非模态对话框
10、支持腾讯X5内核
1、 Javascript中调用原生
添加API类实例到 DWebView
DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
dwebView.addJavascriptObject(new JsApi(), null);
DWebView 继承WebView
init函数中通过addJavascriptInterface增加原生接口
DWebView .java
@SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"})
private void init() {
APP_CACHE_DIRNAME = getContext().getFilesDir().getAbsolutePath() + "/webcache";
WebSettings settings = getSettings();
settings.setDomStorageEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
settings.setAllowFileAccess(false);
settings.setAppCacheEnabled(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setJavaScriptEnabled(true);
settings.setLoadWithOverviewMode(true);
settings.setAppCachePath(APP_CACHE_DIRNAME);
settings.setUseWideViewPort(true);
super.setWebChromeClient(mWebChromeClient);
addInternalJavascriptObject();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
super.addJavascriptInterface(innerJavascriptInterface, BRIDGE_NAME);
} else {
// add dsbridge tag in lower android version
settings.setUserAgentString(settings.getUserAgentString() + " _dsbridge");
}
}
根据JavascriptInterface传过来的方法名称和参数名称通过反射,调用原生方法
DWebView .java
private class InnerJavascriptInterface {
private void PrintDebugInfo(String error) {
Log.d(LOG_TAG, error);
if (isDebug) {
evaluateJavascript(String.format("alert('%s')", "DEBUG ERR MSG:\\n" + error.replaceAll("\\'", "\\\\'")));
}
}
@Keep
@JavascriptInterface
public String call(String methodName, String argStr) {
String error = "Js bridge called, but can't find a corresponded " +
"JavascriptInterface object , please check your code!";
String[] nameStr = parseNamespace(methodName.trim());
methodName = nameStr[1];
Object jsb = javaScriptNamespaceInterfaces.get(nameStr[0]);
JSONObject ret = new JSONObject();
try {
ret.put("code", -1);
} catch (JSONException e) {
e.printStackTrace();
}
if (jsb == null) {
PrintDebugInfo(error);
return ret.toString();
}
Object arg=null;
Method method = null;
String callback = null;
try {
JSONObject args = new JSONObject(argStr);
if (args.has("_dscbstub")) {
callback = args.getString("_dscbstub");
}
if(args.has("data")) {
arg = args.get("data");
}
} catch (JSONException e) {
error = String.format("The argument of \"%s\" must be a JSON object string!", methodName);
PrintDebugInfo(error);
e.printStackTrace();
return ret.toString();
}
Class<?> cls = jsb.getClass();
boolean asyn = false;
try {
method = cls.getMethod(methodName,
new Class[]{Object.class, CompletionHandler.class});
asyn = true;
} catch (Exception e) {
try {
method = cls.getMethod(methodName, new Class[]{Object.class});
} catch (Exception ex) {
}
}
if (method == null) {
error = "Not find method \"" + methodName + "\" implementation! please check if the signature or namespace of the method is right ";
PrintDebugInfo(error);
return ret.toString();
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class);
if (annotation == null) {
error = "Method " + methodName + " is not invoked, since " +
"it is not declared with JavascriptInterface annotation! ";
PrintDebugInfo(error);
return ret.toString();
}
}
Object retData;
method.setAccessible(true);
try {
if (asyn) {
final String cb = callback;
method.invoke(jsb, arg, new CompletionHandler() {
@Override
public void complete(Object retValue) {
complete(retValue, true);
}
@Override
public void complete() {
complete(null, true);
}
@Override
public void setProgressData(Object value) {
complete(value, false);
}
private void complete(Object retValue, boolean complete) {
try {
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("data", retValue);
//retValue = URLEncoder.encode(ret.toString(), "UTF-8").replaceAll("\\+", "%20");
if (cb != null) {
//String script = String.format("%s(JSON.parse(decodeURIComponent(\"%s\")).data);", cb, retValue);
String script = String.format("%s(%s.data);", cb, ret.toString());
if (complete) {
script += "delete window." + cb;
}
//Log.d(LOG_TAG, "complete " + script);
evaluateJavascript(script);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
} else {
retData = method.invoke(jsb, arg);
ret.put("code", 0);
ret.put("data", retData);
return ret.toString();
}
} catch (Exception e) {
e.printStackTrace();
error = String.format("Call failed:The parameter of \"%s\" in Java is invalid.", methodName);
PrintDebugInfo(error);
return ret.toString();
}
return ret.toString();
}
}
2、 原生中调用 Javascript API
原生中调用 Javascript API
dwebView.callHandler("addValue",new Object[]{3,4},new OnReturnValue<Integer>(){
@Override
public void onValue(Integer retValue) {
Log.d("jsbridge","call succeed,return value is "+retValue);
}
});
DWebView .java
public synchronized <T> void callHandler(String method, Object[] args, final OnReturnValue<T> handler) {
CallInfo callInfo = new CallInfo(method, ++callID, args);
if (handler != null) {
handlerMap.put(callInfo.callbackId, handler);
}
if (callInfoList != null) {
callInfoList.add(callInfo);
} else {
dispatchJavascriptCall(callInfo);
}
}
DWebView .java
在ui线程,执行webview的 evaluateJavascript
public synchronized <T> void callHandler(String method, Object[] args, final OnReturnValue<T> handler) {
CallInfo callInfo = new CallInfo(method, ++callID, args);
if (handler != null) {
handlerMap.put(callInfo.callbackId, handler);
}
if (callInfoList != null) {
callInfoList.add(callInfo);
} else {
dispatchJavascriptCall(callInfo);
}
}
private void dispatchJavascriptCall(CallInfo info) {
evaluateJavascript(String.format("window._handleMessageFromNative(%s)", info.toString()));
}
private void _evaluateJavascript(String script) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
DWebView.super.evaluateJavascript(script, null);
} else {
super.loadUrl("javascript:" + script);
}
}
/**
* This method can be called in any thread, and if it is not called in the main thread,
* it will be automatically distributed to the main thread.
*
* @param script
*/
public void evaluateJavascript(final String script) {
runOnMainThread(new Runnable() {
@Override
public void run() {
_evaluateJavascript(script);
}
});
}
3、 API命名空间
dwebView.addJavascriptObject(new JsEchoApi(),"echo");
HashMap不同名称实现不同的命名空间
DWebView .java
private Map<String, Object> javaScriptNamespaceInterfaces = new HashMap<String, Object>();
public void addJavascriptObject(Object object, String namespace) {
if (namespace == null) {
namespace = "";
}
if (object != null) {
javaScriptNamespaceInterfaces.put(namespace, object);
}
}