- 新增native调用JS方法的接口,具体例子在 com.sample.mix.demo.repository.impl.NativeRepository.getUserNameFromJS()
- 优化JSBriage类的内部逻辑
基于WebView(Android下是WebView,iOS下是WKWebView)控件加载H5页面,同时通过框架提供的接口,实现H5使用对设备摄像头、文件系统等设备能力的调用。
概述:对js和java的相互调用方式选型后,根据业务需求设计了一套通信方案
1.Js通过native挂载的homeViewModel(叫什么都行)对象调用native方法。
2.native通过方法返回值返回数据或通过调用JS方法传参达到返回数据的目的。
非耗时操作使用同步调用 AND 耗时操作使用异步调用
基于上文中的:”WebView和Native的基本调用方式“,设计出消息订阅的****通信方案--JSBridge。
JSBridge Demo 项目地址:
Andoird :JSBridgeSample
WEB:预留
观察者模式。
1.JSBridgeConfig为处理订阅配消息,处理器就成为订阅者
2.由webview端或者android本地(电池,网络状态改变)通过NativeRepository发布消息
3.JSBridge分发消息
4.不同类型的订阅者处理消息
5.消息由JSRequest定义,具体参数类型只有订阅者自己知道,JSBridge不关心具体使用的数据类型
6.JSBridge通过JSResponse得到订阅者的处理结果,转成json后回复给webview
比较简单就不做介绍了
├── jsbridge
│ ├── IJSAsyncRequest.kt
│ ├── IJSSub.kt
│ ├── IJSSyncRequest.kt
│ ├── JSBridge.kt
│ ├── JSBridgeConfig.kt
│ └── imp
│ ├── request
│ │ ├── async
│ │ │ └── ToSetNetwork.kt
│ │ └── sync
│ │ └── GetVersionCode.kt
│ └── sub
│ └── ListenNetworkStatus.kt
implementation "com.google.code.gson:gson:2.8.6"
请求协议对应实体类:使用泛型区别不同的业务对应的数据结构
@Keep
data class JSRequest<T>(
/**
* 用于区分前端调用native的方法类型
* ex: request.setDeviceName
*/
val method:String,
/**
* 用于区分请求
*/
val requestId:String,
/**
* 前端带过来的参数
*/
val params:T
)
回复协议对应实体类:使用泛型区别不同的业务对应的数据结构
@Keep
data class JSResponse<T>(
/**
* 用于区分前端调用native的方法类型
*
* 回复前端
* response.setDeviceName
*
* 主动通知前端网络状态
* sub.connectStatus
*/
var method:String,
/**
* 用于区分请求
*/
val requestId:String,
val code:String,
val message:String,
val data: T
)
负责分发前端的请求到不同的处理器,该类不需要改动。
异步请求处理器接口,也叫消息的订阅者,处理前端某个请求的类都实现自该接口,该类型的订阅者通过JSBridge的方法回复JS
interface IJSAsyncRequest {
fun onRequest(params: String)
}
同步步请求处理器接口,也叫消息的订阅者,处理前端某个请求的类都实现自该接口。这类型的订阅者直接通过返回值返回数据给JS
interface IJSSyncRequest {
//通过返回值返回数据给JS
fun onRequest(params: String): JSResponse<Any>
}
推送消息处理器接口,也叫消息的订阅者,主动推送数据给JS,传递数据的类实现都实现自该接口,该类型的订阅者通过JSBridge的方法回复JS
interface IJSSub {
fun onRequest(params: String)
}
处理器与请求的关系表
/**
* 建立method与处理器的关系
*/
object JSBridgeConfig {
//状态码
const val SUCCESS_CODE = "000000"
const val UNKNOWN_ERROR_CODE = "999999"
//js回调名称,由前端定义的,可以直接声明在这,或者使用JSBridge提供的设置接口
var CALL_BACK_NAME = "JSBridgeReceiveMessage"
init {
with(JSBridge) {
//异步
//sub
asyncRequestMap["xxx"] = xxx
//request
asyncRequestMap["$ASYNC_TYPE.xxx"] = xxxx
//同步
syncRequestMap["$SYNC_TYPE.xxxx"] = xxx
}
}
}
简单的介绍使用示例,更为详细的内容请阅读sample的源码
/**
* 初始化
*/
fun initJSBridge(webViewClient: WebView) {
JSBridge.setWebClient(webViewClient)
JSBridge.startHandler()
}
class JSBridgeModel{
@JavascriptInterface
fun requestFromJS(requestJson: String): String {
//具体实现使用JSBridge的方法
return JSBridge.requestFromJS(requestJson)
}
}
//向前端注入对象
webview.addJavascriptInterface(JSBridgeModel(), "JSBridgeModel")
简单的给一个示例,更多示例请详细看sample的代码
该处理器的协议:
请求
{
"method": "async.request.toSetNetwork",
"requestId":"12313",
"params": {
}
}
回复:
{
"method": "response.getIotConnectStatus",
"requestId":"12313",
"code":"12313",
"message":"12313",
"data": {
}
}
处理器的实现
/**
* 打开设置:
* 获取参数 这里的例子是没有参数,如果需要:请给JSRequest或JSResponse传入对应的data class
*/
class ToSetNetwork : IJSAsyncRequest, CoroutineScope by MainScope() {
override fun onRequest(params: String) {
launch(Dispatchers.Main) {
var request: JSRequest<NoneRequest>? = null
try {
val type = object : TypeToken<JSRequest<NoneRequest>>() {}.type
request = JsonUtils.fromJson(params, type)
if (request == null) {
return@launch
}
App.ctx.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
})
val response = JSResponse(
request.method,
request.requestId,
JSBridgeConfig.SUCCESS_CODE,
"",
NoneResponse("")
)
JSBridge.onResponse(response, null)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
/**
* 建立method与处理器的关系
*/
object JSBridgeConfig {
//状态码
const val SUCCESS_CODE = "000000"
const val UNKNOWN_ERROR_CODE = "999999"
//js回调名称,由前端定义的,可以直接声明在这,或者使用JSBridge提供的设置接口
var CALL_BACK_NAME = "JSBridgeReceiveMessage"
init {
with(JSBridge) {
//异步
//request
//给上方的处理器注册
asyncRequestMap["$ASYNC_TYPE.toSetNetwork"] = ToSetNetwork()
}
}
}
如果没有前端,可以用chrome的调试工具模拟前端调用:
- 0.在初始化webview时,打开调试模式:
//打开webview的调试模式,todo 注:线上环境不建议打开
WebView.setWebContentsDebuggingEnabled(true)
- 1在chrome地址栏输入:chrome://inspect 。 注:在edge是edge://inspect
- 2.选择你的设备和应用并使用挂载在window上的java对象
//调到控制台执行如下代码:设备就会打开设置啦!
window.JSBridgeModel.requestFromJS(JSON.stringify(JSON.parse('{"method":"async.request.toSetNetwork","requestId":"12313","params": {}}')))