# JSBridge小记
# 原理图
# web调用native
# JSBridge :拦截 URL Schema
- 原理图
- http请求格式
<protocol>://<domain>/<path>?<query>
1
- jsbridge请求格式
jsbridge://<method>?<params>
1
2
2
- jsbridge://showName?type=husky&age=2
- 优缺点
- 兼容性好
- 不直观,URL长度有限制
# show me the code
- 这里我们通过
window.alert()
将我们约定的协议(这里我们用jsbridge)发送给Native
- Native可以拿到我们弹窗中的信息,可以决定收到信息后,在执行其他逻辑
function showNativeDialog(){
window.alert('jsbridge://showNativeDialog?text=huskyAreYouScared')
}
1
2
3
2
3
- Native部分代码
- 我们重写onJsAlert来实现信息的接受
// MainActivity.java
webView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if(!message.startsWith("jsbridge://")){ // 这里判断是不是以我们约定好的协议进行调用的
return super.onJsAlert(view, url, message, result);
}
String str = message.substring(message.indexOf("=")+1);// 这里拿到参数,简单实现
new AlertDialog.Builder(this).setMessage(str).create().show(); // 调用原生的dialog
result.confirm();
return true;
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 拿到信息之后,其他逻辑就被native端接管了
# JSBridge :注入API
- 原理图
- 优缺点
- 优点:简单直观
- 缺点:有兼容性问题(Android 4.2+)
# show me the code
- 首先我们准备需要注入的方法
// MainActivity.java
class NativeBridge{
private Context ctx;
NativeBridge(Context ctx){
this.ctx = ctx;
}
@JavascriptInterface // 这个注解一定要加
public void showNativeDialog(String text){
new AlertDialog.Builder(ctx).setMessage(text).create().show();
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 向window对象注入JS方法
// MainActivity.java
webView.addJavascriptInterface(new NativeBridge(this),"NativeBridge");
1
2
2
- 这里定义完成后,在
web
端就可以在window
上找到NativeBridge
的对象 - 接下来我们写一下web端的代码
function showNativeDialog () {
window.NativeBridge.showNativeDialog('husky')
},
1
2
3
2
3
# native调用web
- 直接上代码
# show me the code
js
代码准备,将showWebDialog
挂载到window
上
window.showWebDialog = text => window.alert(text)
1
- 我们起一个本地服务,我这里端口为
8080
,vue
创建的项目启动默认端口 - native端
TIP
以Android为例,这里只写了主要逻辑,声明变量和视图绑定代码忽略
- webview加载上面启动的本地服务
// MainActivity.java
webView.loadUrl("http://192.168.1.104:8080/?timestamp"+new Date().getTime()); // 加时间戳防止缓存
webView.getSettings().setJavaScriptEnabled(true); // 可以执行JS脚本
webView.setWebChromeClient(new WebChromeClient()); // WebChromeClient是辅助WebView处理Javascript的对话框
1
2
3
4
2
3
4
- 通过button来触发事件,通过webview来执行JS代码
// MainActivity.java
showBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
String inputValue = 'husky are yu scared';
String jsCode = String.format("window.showWebDialog('%s')", inputValue);
webView.evaluateJavascript(jsCode,null);
}
});
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 通过以上代码,就可以简单的
# 两次单项调用实现回调
# 实现web端调用native的回调
- 两次单项调用,指的是
web
调用native
,然后native
在调用web
- 原理图
# show me the code
- 为了更好的统一管理我们先定义一个JSSDK
window.callbackMap = {}
window.callbackId = 1
window.JSSDK = {
getNativeMessage (callback) {
const callbackId = window.callbackId++
window.callbackMap[callbackId] = callback
window.NativeBridge.getNativeMessage(callbackId)
},
showMessage (callbackId, value) {
if (window.callbackMap[callbackId]) {
window.callbackMap[callbackId](value)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- web端执行刚才定义的方法,并注册回调
window.JSSDK.getNativeMessage(function (info) {
window.alert(info)
})
1
2
3
2
3
- 接下来写
native
端,注入getNativeMessage
方法到NativeBridge
对象中
// MainActivity.java
webView.addJavascriptInterface(new NativeBridge(this),"NativeBridge");
class NativeBridge{
private Context ctx;
NativeBridge(Context ctx){
this.ctx = ctx;
}
@JavascriptInterface
public void getNativeMessage(int callbackId){
final MainActivity mainActivity = (MainActivity) ctx;
final String jsCode = String.format("window.JSSDK.showMessage(%s, '%s')", callbackId,"husky callback");
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mainActivity.webView.evaluateJavascript(jsCode,null);
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 实现native端调用web的回调
- 其实和实现
web
端调用native
的回调思想一致,只是执行的地方对调了 - 两次单项调用,指的是
native
调用web
,然后web
在调用native
- 原理图
TIP
注意这里和上面的原理图,webview和native换了位置
# show me the code
- 因为这里是native调用web,所以,保存回调和接受回调,应该在native来定义
- 首先我们在native定义一个NativeSDK
// MainActivity.java
// 定义回调接口
interface Callback{
void invoke(String value);
}
class NativeSDK{
private Context ctx;
// 回调函数的Key
private int id = 1;
// 用来存储回调
private Map<Integer,Callback> callbackMap = new HashMap();
NativeSDK(Context ctx){
this.ctx = ctx;
}
// 定义执行web端的方法
void getWebMessageInfo(Callback callback){
int callbackId = id++;
callbackMap.put(callbackId,callback);
final String jsCode = String.format("window.JSSDK.getWebMessageInfo(%s)", callbackId);
self.runOnUiThread(new Runnable() {
@Override
public void run() {
self.webView.evaluateJavascript(jsCode,null);
}
});
}
// 定义回调函数
void showMessage(int callbackId,String value){
if(callbackMap.containsKey(callbackId)){
callbackMap.get(callbackId).invoke(value);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- 定义调用web端的bridge构造函数
// MainActivity.java
webView.addJavascriptInterface(new NativeBridge(this),"NativeBridge");
class NativeBridge{
private Context ctx;
NativeBridge(Context ctx){
this.ctx = ctx;
}
@JavascriptInterface
public void showMessage(int callbackId,String text){
nativeSDK.showMessage(callbackId,text);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 定义好
nativeSDK
和bridge
,点击事件执行getWebMessageInfo
showBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
// 执行nativeSDK中定义好的getWebMessageInfo,并设置回调函数
nativeSDK.getWebMessageInfo(new Callback() {
@Override
public void invoke(String value) {
new AlertDialog.Builder(self).setMessage("接受web的回调"+value).create().show();
}
});
}
});
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 接下来在
web
端定义getWebMessageInfo
方法
window.JSSDK = {
getWebMessageInfo (callbackId) {
// 调用native端注入的showMessage方法
window.NativeBridge.showMessage(callbackId, 'husky web info')
}
}
1
2
3
4
5
6
2
3
4
5
6
# DSBridge-android
- GitHub仓库地址传送门
# web调用native
- 首先需要在web端安装dsbridge插件
npm install dsbridge@3.1.3
1
- 在需要用的地方引入该插件
import dsBridge from 'dsbridge'
1
- 在web端的点击事件中触发调用native的方法
// 点击事件调用send触发该事件,调用原生getNativeMessage方法,并提供回调函数
function send () {
dsBridge.call('getNativeMessage', '', value => {
window.alert(value)
})
}
1
2
3
4
5
6
2
3
4
5
6
- 在native端需要几点需要改变
- 1.在build.gradle(Project:xxx)中的allprojects加入以下内容
allprojects {
repositories {
// ...
+ maven { url 'https://jitpack.io' }
}
}
1
2
3
4
5
6
2
3
4
5
6
- 2.在build.gradle(Module:xxx)dependencies加入以下内容
dependencies {
// ...
implementation 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'
}
1
2
3
4
2
3
4
- 3.将activity_xml文件中的webview换成下面这个方法
<wendu.dsbridge.DWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
1
2
3
4
5
2
3
4
5
- 4.将声明webview的类型换成DWebView,并导入相应的包
- private WebView webView;
+ private DWebView webView;
1
2
2
- 5.通过addJavascriptObject来注册JSAPI
dWebView.addJavascriptObject(new JSApi(this),null);
class JSApi{
private Context context;
JSApi(Context context){
this.context = context;
}
@JavascriptInterface
public void getNativeMessage (Object msg, CompletionHandler<String> handler){
handler.complete("husky native message"); // 回调
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# native调用web
- 1.在web端注册方法
dsBridge.register('getWebMessage', () => {
// other logic
return 'husky web info' // 返回值
})
1
2
3
4
2
3
4
- 2.在相应的点击事件中写入一下代码
showBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
// 调用web端注册的方法
webView.callHandler("getWebMessage", null, new OnReturnValue<String>() {
@Override
public void onValue(String retValue) {
new AlertDialog.Builder(self)
.setMessage(retValue)
.create()
.show();
}
});
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 调试webview
- 首先需要给
webview
开启debugger
webView.setWebContentsDebuggingEnabled(true);
1
- 然后在用
debugger
模式启动程序
- 打开
chrome
历览器的chrome://inspect/#devices
页面,如下图
- 点击
inspect
会弹出一个页面,就和我们平时开发web
应用调试器一样,然后就可以分别在web
端和Android studio
打断点进行调试了
← 正则小记 手机端H5展示页的制作 →