Android native和h5混合开发几种常见的hybrid通信方式
Posted 唐大麦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android native和h5混合开发几种常见的hybrid通信方式相关的知识,希望对你有一定的参考价值。
传统的JSInterface
首先先介绍一下最普通的一种通信方式,就是使用android原生的javascriptInterface来进行js和Java的通信。具体方式如下:
首先先看一段html代码
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript">
function showToast(toast)
javascript:control.showToast(toast);
function log(msg)
console.log(msg);
</script>
</head>
<body>
<input type="button" value="toast"
onClick="showToast('Hello world')" />
</body>
</html>
很简单,一个button,点击这个button就执行js脚本中的showToast方法。
而这个showToast方法做了什么呢?
function showToast(toast)
javascript:control.showToast(toast);
可以看到control.showToast,这个是什么我们等下再说,下面看我们java的代码。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="zjutkz.com.tranditionaljsdemo.MainActivity">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
</LinearLayout>
public class MainActivity extends AppCompatActivity
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView)findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsInterface(), "control");
webView.loadUrl("file:///android_asset/interact.html");
public class JsInterface
@JavascriptInterface
public void showToast(String toast)
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
log("show toast success");
public void log(final String msg)
webView.post(new Runnable()
@Override
public void run()
webView.loadUrl("javascript: log(" + "'" + msg + "'" + ")");
);
首先界面很简单,一个WebView。在对应的activity中做的事也就几件,首先打开js通道。
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
然后通过WebView的addJavascriptInterface方法去注入一个我们自己写的interface。
webView.addJavascriptInterface(new JsInterface(), "control");
public class JsInterface
@JavascriptInterface
public void showToast(String toast)
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
log("show toast success");
public void log(final String msg)
webView.post(new Runnable()
@Override
public void run()
webView.loadUrl("javascript: log(" + "'" + msg + "'" + ")");
);
可以看到这个interface我们给它取名叫control。
最后loadUrl。
webView.loadUrl("file:///android_asset/interact.html");
好了,让我们再看看js脚本中的那个showToast()方法。
function showToast(toast)
javascript:control.showToast(toast);
这里的control就是我们的那个interface,调用了interface的showToast方法
@JavascriptInterface
public void showToast(String toast)
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
log("show toast success");
可以看到先显示一个toast,然后调用log()方法,log()方法里调用了js脚本的log()方法。
function log(msg)
console.log(msg);
js的log()方法做的事就是在控制台输出msg。
这样我们就完成了js和java的互调,是不是很简单。但是大家想过这样有什么问题吗?如果你使用的是AndroidStudio,在你的webSettings.setJavaScriptEnabled(true);
这句函数中,AndroidStudio会给你一个warning。
这个提示的意思呢,就是如果你使用了这种方式去开启js通道,你就要小心XSS攻击了,具体的大家可以参考wooyun上的这篇文章。
虽然这个漏洞已经在Android 4.2上修复了,就是使用@JavascriptInterface
这个注解。但是你得考虑兼容性啊,你不能保证,尤其在中国这样碎片化严重的地方,每个用户使用的都是4.2+的系统。所以基本上我们不会再利用Android系统为我们提供的addJavascriptInterface方法或者@JavascriptInterface
注解来实现js和java的通信了。那怎么办呢?方法都是人想出来的嘛,下面让我们看解决方案。
JSBridge
JSBridge,顾名思义,就是和js沟通的桥梁。其实这个技术在Android中已经不算新了,相信有些同学也看到过不少实现方案,这里说一种我的想法吧。其实说是我的想法,实际是公司里的大牛实现的,我现在做的就是维护并且扩展,不过这里还是拿出来和大家分享一下。
思路
首先先说思路,有经验的同学可能都知道Android的WebView中有一个WebChromeClient类,这个类其实就是用来监听一些WebView中的事件的,我们发现其中有三个这样的方法。
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
return super.onJsPrompt(view, url, message, defaultValue, result);
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
return super.onJsAlert(view, url, message, result);
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
return super.onJsConfirm(view, url, message, result);
这三个方法其实就对应于js中的alert(警告框),comfirm(确认框)和prompt(提示框)方法,那这三个方法有什么用呢?
前面我们说了JSBridge的作用是提供一种js和java通信的框架,其实我们可以利用这三个方法去完成这样的事。比如我们可以在js脚本中调用alert方法,这样对应的就会走到WebChromeClient类的onJsAlert()方法中,我们就可以拿到其中的信息去解析,并且做java层的事情。那是不是这三个方法随便选一个就可以呢?其实不是的,因为我们知道,在js中,alert和confirm的使用概率还是很高的,特别是alert,所以我们最好不要使用这两个通道,以免出现不必要的问题。
好了,说到这里我们前期的准备工作也就做好了,其实就是通过重写WebView中WebChromeClient类的onJsPrompt()方法来进行js和java的通信。
有了实现方案,下面就是一些具体的细节了,大家有没有想过,怎么样才能让java层知道js脚本需要调用的哪一个方法呢?怎么把js脚本的参数传递进来呢?同步异步的方式又该怎么实现呢?下面提供一种我的思路。
首先大家都知道http是什么,其实我们的JSBridge也可以效仿一下http,定义一个自己的协议。比如规定sheme,path等等。下面来看一下一些的具体内容:
hybrid://JSBridge:1538351/method?“message”:”msg”
是不是和http协议有一点像,其实我们可以通过js脚本把这段协议文本传递到onPropmt()方法中并且进行解析。比如,sheme是hyrid://开头的就表示是一个hybrid方法,需要进行解析。后面的method表示方法名,message表示传递的参数等等。
有了这样一套协议,我们就可以去进行我们的通信了。
先看一下我们html和js的代码
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="file:///android_asset/jsBridge.js" type="text/javascript"></script>
</head>
<body>
<div class="blog-header">
<h3>JSBridge</h3>
</div>
<ul class="entry">
<br/>
<li>
toast展示<br/>
<button onclick="JsBridge.call('JSBridge','toast','message':'我是气泡','isShowLong':0,function(res));">toast</button>
</li>
<br/>
<li>
异步任务<br/>
<button onclick="JsBridge.call('JSBridge','plus','data':1,function(res)console.log(JSON.stringify(res)));">plus</button>
</li>
<br/>
<br/>
</ul>
</body>
</html>
(function (win, lib)
var doc = win.document;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var JsBridge = win.JsBridge || (win.JsBridge = );
var inc = 1;
var LOCAL_PROTOCOL = 'hybrid';
var CB_PROTOCOL = 'cb_hybrid';
var CALLBACK_PREFIX = 'callback_';
//核心功能,对外暴露
var Core =
call: function (obj, method, params, callback, timeout)
var sid;
if (typeof callback !== 'function')
callback = null;
sid = Private.getSid();
Private.registerCall(sid, callback);
Private.callMethod(obj, method, params, sid);
,
//native代码处理 成功/失败 后,调用该方法来通知js
onComplete: function (sid, data)
Private.onComplete(sid, data);
;
//私有功能集合
var Private =
params: ,
chunks: ,
calls: ,
getSid: function ()
return Math.floor(Math.random() * (1 << 50)) + '' + inc++;
,
buildParam: function (obj)
if (obj && typeof obj === 'object')
return JSON.stringify(obj);
else
return obj || '';
,
parseData: function (str)
var rst;
if (str && typeof str === 'string')
try
rst = JSON.parse(str);
catch (e)
rst =
status:
code: 1,
msg: 'PARAM_PARSE_ERROR'
;
else
rst = str || ;
return rst;
,
//根据sid注册calls的回调函数
registerCall: function (sid, callback)
if (callback)
this.calls[CALLBACK_PREFIX + sid] = callback;
,
//根据sid删除calls对应的回调函数,并返回call对象
unregisterCall: function (sid)
var callbackId = CALLBACK_PREFIX + sid;
var call = ;
if (this.calls[callbackId])
call.callback = this.calls[callbackId];
delete this.calls[callbackId];
return call;
,
//生成URI,调用native功能
callMethod: function (obj, method, params, sid)
// hybrid://objectName:sid/methodName?params
params = Private.buildParam(params);
var uri = LOCAL_PROTOCOL + '://' + obj + ':' + sid + '/' + method + '?' + params;
var value = CB_PROTOCOL + ':';
window.prompt(uri, value);
,
onComplete: function (sid, data)
var callObj = this.unregisterCall(sid);
var callback = callObj.callback;
data = this.parseData(data);
callback && callback(data);
;
for (var key in Core)
if (!hasOwnProperty.call(JsBridge, key))
JsBridge[key] = Core[key];
)(window);
有前端经验的同学应该能很轻松的看懂这样的代码,对于看不懂的同学我来解释一下,首先看界面。
可以看到有两个按钮,对应着html的这段代码
<br/>
<li>
toast展示<br/>
<button onclick="JsBridge.call('JSBridge','toast','message':'我是气泡','isShowLong':0,function(res));">toast</button>
</li>
<br/>
<li>
异步任务<br/>
<button onclick="JsBridge.call('JSBridge','plus','data':1,function(res)console.log(JSON.stringify(res)));">toast</button>
</li>
<br/>
点击按钮会执行js脚本的这段代码
call: function (obj, method, params, callback, timeout)
var sid;
if (typeof callback !== 'function')
callback = null;
sid = Private.getSid();
Private.registerCall(sid, callback);
Private.callMethod(obj, method, params, sid);
它其实就是一个函数,名字叫call,括号里的是它的参数(obj, method, params, callback, timeout)。那这几个参数是怎么传递的呢?回过头看我们的html代码,点击第一个按钮,会执行这个语句
<button onclick="JsBridge.call('JSBridge','toast','message':'我是气泡','isShowLong':0,function(res));">toast</button>
其中括号(‘JSBridge’,’toast’,‘message’:’我是气泡’,’isShowLong’:0,function(res))
里的第一个参数’JSBridge’对应着前面的obj,’toast’对应着method,以此类推。第二个按钮也是一样。
然后在call这个方法内,会执行Private类的registerCall和callMethod,我们来看callMehod()。
//生成URI,调用native功能
callMethod: function (obj, method, params, sid)
// hybrid://objectName:sid/methodName?params
params = Private.buildParam(params);
var uri = LOCAL_PROTOCOL + '://' + obj + ':' + sid + '/' + method + '?' + params;
var value = CB_PROTOCOL + ':';
window.prompt(uri, value);
注释说的很清楚了,就是通过传递进来的参数生成uri,并且调用window.prompt()方法,这个方法大家应该很眼熟吧,没错,在调用这个方法之后,程序就会相应的走到java代码的onJsPrompt()方法中。而生成的uri则是我们上面说过的那个我们自己定义的协议格式。
好了,我们总结一下这两个前端的代码。其实很简单,以界面的第一个按钮toast为例,点击这个按钮,它会执行相应的js脚本代码,然后就会像我们前面所讲的那样,走到onJsPrompt()方法中,下面让我们看看对应的java代码。
public class InjectedChromeClient extends WebChromeClient
private final String TAG = "InjectedChromeClient";
private JsCallJava mJsCallJava;
public InjectedChromeClient()
mJsCallJava = new JsCallJava();
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
result.confirm(mJsCallJava.call(view, message));
return true;
这是对应的WebChromeClient类,可以看到在onJsPrompt()方法中我们只做了一件事,就是丢给JsCallJava类去解析,再看JsCallJava类之前,我们可以先看看onJsPrompt()这个方法到底传进来了什么。
可以看到,我们传给JsCallJava类的那个message,就像我们前面定义的协议一样。sheme是hybrid://,表示这是一个hybrid方法,host是JSBridge,方法名字是toast,传递的参数是以json格式传递的,具体内容如图。不知道大家有没有发现,这里我有一个东西没有讲,就是JSBridge:后面的那串数字,这串数字是干什么用的呢?大家应该知道,现在我们整个调用过程都是同步的,这意味着我们没有办法在里面做一些异步的操作,为了满足异步的需求,我们就需要定义这样的port,有了这串数字,我们在java层就可以做异步的操作,等操作完成以后回调给js脚本,js脚本就通过这串数字去得到对应的callback,有点像startActivity中的那个requestCode。大家没听懂也没关系,后面我会在代码中具体讲解。
好了,下面我们可以来看JsCallJava这个类的具体代码了。
public class JsCallJava
private final static String TAG = "JsCallJava";
private static final String BRIDGE_NAME = "JSBridge";
private static final String SCHEME="hybrid";
private static final int RESULT_SUCCESS=200;
private static final int RESULT_FAIL=500;
private ArrayMap<String, ArrayMap<String, Method>> mInjectNameMethods = new ArrayMap<>();
private JSBridge mWDJSBridge = JSBridge.getInstance();
public JsCallJava()
try
ArrayMap<String, Class<? extends IInject>> externals = mWDJSBridge.getInjectPair();
if (externals.size() > 0)
Iterator<String> iterator = externals.keySet().iterator();
while (iterator.hasNext())
String key = iterator.next();
Class clazz = externals.get(key);
if (!mInjectNameMethods.containsKey(key))
mInjectNameMethods.put(key, getAllMethod(clazz));
catch (Exception e)
Log.e(TAG, "init js error:" + e.getMessage());
private ArrayMap<String, Method> getAllMethod(Class injectedCls) throws Exception
ArrayMap<String, Method> mMethodsMap = new ArrayMap<>();
//获取自身声明的所有方法(包括public private protected), getMethods会获得所有继承与非继承的方法
Method[] methods = injectedCls.getDeclaredMethods();
for (Method method : methods)
String name;
if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null)
continue;
Class[] parameters=method.getParameterTypes();
if(null!=parameters && parameters.length==3)
if(parameters[0]==WebView.class && parameters[1]==JSONObject.class && parameters[2]==JsCallback.class)
mMethodsMap.put(name, method);
return mMethodsMap;
public String call(WebView webView, String jsonStr)
String methodName = "";
String name = BRIDGE_NAME;
String param = "";
String result = "";
String sid="";
if (!TextUtils.isEmpty(jsonStr) && jsonStr.startsWith(SCHEME))
Uri uri = Uri.parse(jsonStr);
name = uri.getHost();
param = uri.getQuery();
sid = getPort(jsonStr);
String path = uri.getPath();
if (!TextUtils.isEmpty(path))
methodName = path.replace("/", "");
if (!TextUtils.isEmpty(jsonStr))
try
ArrayMap<String, Method> methodMap = mInjectNameMethods.get(name);
Object[] values = new Object[3];
values[0] = webView;
values[1] = new JSONObject(param);
values[2]=new JsCallback(webView,sid);
Method currMethod = null;
if (null != methodMap && !TextUtils.isEmpty(methodName))
currMethod = methodMap.get(methodName);
// 方法匹配失败
if (currMethod == null)
result = getReturn(jsonStr, RESULT_FAIL, "not found method(" + methodName + ") with valid parameters");
else
result = getReturn(jsonStr, RESULT_SUCCESS, currMethod.invoke(null, values));
catch (Exception e)
e.printStackTrace();
else
result = getReturn(jsonStr, RESULT_FAIL, "call data empty");
return result;
private String getPort(String url)
if (!TextUtils.isEmpty(url))
String[] arrays = url.split(":");
if (null != arrays && arrays.length >= 3)
String portWithQuery = arrays[2];
arrays = portWithQuery.split("/");
if (null != arrays && arrays.length > 1)
return arrays[0];
return null;
private String getReturn(String reqJson, int stateCode, Object result)
String insertRes;
if (result == null)
insertRes = "null";
else if (result instanceof String)
//result = ((String) result).replace("\\"", "\\\\\\"");
insertRes = String.valueOf(result);
else if (!(result instanceof Integer)
&& !(result instanceof Long)
&& !(result instanceof Boolean)
&& !(result instanceof Float)
&& !(result instanceof Double)
&& !(result instanceof JSONObject)) // 非数字或者非字符串的构造对象类型都要序列化后再拼接
insertRes = result.toString();//mGson.toJson(result);
else //数字直接转化
insertRes = String.valueOf(result);
//String resStr = String.format(RETURN_RESULT_FORMAT, stateCode, insertRes);
Log.d(TAG, " call json: " + reqJson + " result:" + insertRes);
return insertRes;
有点长,不过其实逻辑很好理解。首先我们调用的是call这个方法。它里面做了什么呢
public String call(WebView webView, String jsonStr)
String methodName = "";
String name = BRIDGE_NAME;
String param = "";
String result = "";
String sid="";
if (!TextUtils.isEmpty(jsonStr) && jsonStr.startsWith(SCHEME))
Uri uri = Uri.parse(jsonStr);
name = uri.getHost();
param = uri.getQuery();
sid = getPort(jsonStr);
String path = uri.getPath();
if (!TextUtils.isEmpty(path))
methodName = path.replace("/", "");
if (!TextUtils.isEmpty(jsonStr))
try
ArrayMap<String, Method> methodMap = mInjectNameMethods.get(name);
Object[] values = new Object[3];
values[0] = webView;
values[1] = new JSONObject(param);
values[2]=new JsCallback(webView,sid);
Method currMethod = null;
if (null != methodMap && !TextUtils.isEmpty(methodName))
currMethod = methodMap.get(methodName);
// 方法匹配失败
if (currMethod == null)
result = getReturn(jsonStr, RESULT_FAIL, "not found method(" + methodName + ") with valid parameters");
else
result = getReturn(jsonStr, RESULT_SUCCESS, currMethod.invoke(null, values));
catch (Exception e)
e.printStackTrace();
else
result = getReturn(jsonStr, RESULT_FAIL, "call data empty");
return result;
可以看到其实就是通过js脚本传递过来的参数得到了方法名字,sid(前面说的那串数字)等等内容。下面看这段代码
ArrayMap<String, Method> methodMap = mInjectNameMethods.get(name);
通过name去得到一个map,这里的name是我们刚刚解析得到了,对应实际情况就是JSBridge,那这个mInjectNameMethods又是什么呢?
private ArrayMap<String, ArrayMap<String, Method>> mInjectNameMethods = new ArrayMap<>();
private JSBridge mJSBridge = JSBridge.getInstance();
public JsCallJava()
try
ArrayMap<String, Class<? extends IInject>> externals = mJSBridge.getInjectPair();
if (externals.size() > 0)
Iterator<String> iterator = externals.keySet().iterator();
while (iterator.hasNext())
String key = iterator.next();
Class clazz = externals.get(key);
if (!mInjectNameMethods.containsKey(key))
mInjectNameMethods.put(key, getAllMethod(clazz));
catch (Exception e)
Log.e(TAG, "init js error:" + e.getMessage());
private ArrayMap<String, Method> getAllMethod(Class injectedCls) throws Exception
ArrayMap<String, Method> mMethodsMap = new ArrayMap<>();
//获取自身声明的所有方法(包括public private protected), getMethods会获得所有继承与非继承的方法
Method[] methods = injectedCls.getDeclaredMethods();
for (Method method : methods)
String name;
if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null)
continue;
Class[] parameters=method.getParameterTypes();
if(null!=parameters && parameters.length==3)
if(parameters[0]==WebView.class && parameters[1]==JSONObject.class && parameters[2]==JsCallback.class)
mMethodsMap.put(name, method);
return mMethodsMap;
可以看到我们有一个JSBridge类,在JsCallJava的构造函数中,我们通过JSBridge这个类的getInjectPair()方法得到了一个String和class的映射关系,并且把class中符合标准的方法拿出来存放到mInjectNameMethods中,以便我们在call方法中调用。下面来看看JSBridge类。
public class JSBridge
public static final String BRIDGE_NAME = "JSBridge";
private static JSBridge INSTANCE = new JSBridge();
private boolean isEnable=true;
private ArrayMap<String, Class<? extends IInject>> mClassMap = new ArrayMap<>();
private JSBridge()
mClassMap.put(BRIDGE_NAME, JSLogical.class);
public static JSBridge getInstance()
return INSTANCE;
public boolean addInjectPair(String name, Class<? extends IInject> clazz)
if (!mClassMap.containsKey(name))
mClassMap.put(name, clazz);
return true;
return false;
public boolean removeInjectPair(String name,Class<? extends IInject> clazz)
if (TextUtils.equals(name,BRIDGE_NAME))
return false;
Class clazzValue=mClassMap.get(name);
if(null!=clazzValue && (clazzValue == clazz))
mClassMap.remove(name);
return true;
return false;
public ArrayMap<String, Class<? extends IInject>> getInjectPair()
return mClassMap;
它的getInjectPair方法其实就是得到了mClassMap,这个map在JSBridge类初始化的时候就有一个默认的值了。
public static final String BRIDGE_NAME = "JSBridge";
private JSBridge()
mClassMap.put(BRIDGE_NAME, JSLogical.class);
key是”JSBridge”,value是我们的JSLogincal类。
public class JSLogical implements IInject
/**
* toast
*
* @param webView 浏览器
* @param param 提示信息
*/
public static void toast(WebView webView, JSONObject param, final JsCallback callback)
String message = param.optString("message");
int isShowLong = param.optInt("isShowLong");
Toast.makeText(webView.getContext(), message, isShowLong == 0 ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
if (null != callback)
try
JSONObject object = new JSONObject();
object.put("result", true);
invokeJSCallback(callback, object);
catch (Exception e)
e.printStackTrace();
/**
* 加一
*
* @param webView
* @param param
* @param callback
*/
public static void plus(WebView webView, final JSONObject param, final JsCallback callback)
new Thread(new Runnable()
@Override
public void run()
try
Thread.sleep(2000);
int original = param.optInt("data");
original = original + 1;
if (null != callback)
JSONObject object = new JSONObject();
object.put("after plussing", original);
invokeJSCallback(callback, object);
catch (Exception e)
e.printStackTrace();
).start();
private static void invokeJSCallback(JsCallback callback, JSONObject objects)
invokeJSCallback(callback, true, null, objects);
public static void invokeJSCallback(JsCallback callback, boolean isSuccess, String message, JSONObject objects)
try
callback.apply(isSuccess, message, objects);
catch (JsCallback.JsCallbackException e)
e.printStackTrace();
对这个类上面的两个方法有没有很眼熟?名字和js脚本中的那两个方法一样有木有。我们调用链最后就会走到相应的同名方法中!
上面就是js调js的整个过程了,其实吧,不应该放这么多的代码的,搞得像是源码分析一样,不过我觉得这样还是有一定好处的,至少跟着代码走一遍能加深印象嘛。
我们还是来捋一捋整个过程。
在js脚本中把对应的方法名,参数等写成一个符合协议的uri,并且通过window.prompt方法发送给java层。
在java层的onJsPrompt方法中接受到对应的message之后,通过JsCallJava类进行具体的解析。
在JsCallJava类中,我们解析得到对应的方法名,参数等信息,并且在map中查找出对应的类的方法。
这里多说一句,还记得我们定义的协议中的host是什么吗?
hybrid://JSBridge:875725/toast?“message”:”我是气泡”,”isShowLong”:0
是JSBridge,而我们在JsCallJava类中是通过这个host去查找对应的类的,我们可以看到在JSBridge类中
public static final String BRIDGE_NAME = "JSBridge";
private JSBridge()
mClassMap.put(BRIDGE_NAME, JSLogical.class);
这意味着,如果你可以更换你的host,叫aaa都没关系,只要你在对应的map中的key也是aaa就可以了。
可能有的同学会说何必这么麻烦,直接在JsCallJava类中定义方法不就好了,这样还省的去写那么多的逻辑。可是大家有想过如果你把所有js脚本想要调用的方法都写在JsCallJava类中,这个类会有多难扩展和维护吗?而像我这样,如果你的js脚本处理的是登录相关逻辑,你可以写一个LoginLogical.class,如果是业务相关,你可以写一个BizLogical.class,这样不仅清晰,而且解耦。
当然,如果你仔细的看过代码,会发现其实在native层的那些同名函数其实是有规范的。
首先必须要是public static的,因为这样调用会更方便。
其次参数也有要求,有且仅有三个参数,WebView,JsonObject和一个Callback。WebView用来提供可能需要的context,另外java执行js方法也需要WebView对象。JsonObject是js脚本传递过来的参数。而Callback则是java用于回调js脚本的。
可能你会发现JSBridge里处处都是规范,协议需要规范,参数需要规范。这些其实都是合理的,因为规范所以安全。
在得到对应的方法之后,就去调用它,以我们的toast为
/**
* toast
*
* @param webView 浏览器
* @param param 提示信息
*/
public static void toast(WebView webView, JSONObject param, final JsCallback callback)
String message = param.optString("message");
int isShowLong = param.optInt("isShowLong");
Toast.makeText(webView.getContext(), message, isShowLong == 0 ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
if (null != callback)
try
JSONObject object = new JSONObject();
object.put("result", true);
invokeJSCallback(callback, object);
catch (Exception e)
e.printStackTrace();
拿到对应的信息,直接makeToast就好了。
以上就是全部js调用java的过程,那我们java执行完逻辑以后,怎么回调js呢?这里我们以另外一个按钮的例子来说。
原生项目如何从零开始集成 React Native
H5混合开发中android终端和ios终端常见的兼容问题1
混合开发之DSBridge(同时支持Android和iOS)