Webview 中 js 和 java 交互方式
传统通信
1、Java 调用 JS
- loadUrl
Java 调用 JS,只有一种 WebView.loadUrl("javascript:function()")
, - evaluateJavascript
Android8.0+,onAlert 只回调一次?
loadUrl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS代码
<script>
// Android需要调用的方法
function callJS() {
alert("Android调用了JS的callJS方法");
}
</script>
</head>
</html>
|
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
| class Java调用Js代码 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_java_call_js)
var webSettings = webview.settings
// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true)
// 设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true)
webview.webChromeClient = object : WebChromeClient() {
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
ToastUtils.showShort(message)
LogUtil.i("url:$url,message:$message")
return true
}
}
webview.loadUrl("file:///android_asset/js/javacalljs.html")
btn_loadurl.setOnClickListener {
webview.post {
LogUtil.i("javascript:callJS()")
webview.loadUrl("javascript:callJS()")
}
}
}
}
|
evaluateJavascript
该方法比第一种方法效率更高、使用更简洁。
- 因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。
- Android 4.4 后才可使用
1
2
3
4
5
6
7
8
| // 其他代码同loadUrl
btn_evaluateJavascript.setOnClickListener {
webview.post {
webview.evaluateJavascript("javascript:callJS()") {
LogUtil.i("evaluateJavascript ,value:$it")
}
}
}
|
参数返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS代码
<script>
// Android需要调用的方法
function callJS() {
alert("Android调用了JS的callJS方法");
}
function readyToGo() {
alert("WebView调用JavaScript无参无返回值函数")
}
function alertMessage(message) {
alert(message)
}
function getYourCar(){
return "Car";
}
</script>
</head>
</html>
|
1
2
3
4
5
6
7
8
9
10
| btn_no_param_no_return.setOnClickListener {
webview.loadUrl("javascript:readyToGo()")
}
btn_has_param_no_return.setOnClickListener {
val call = "javascript:alertMessage(\"" + "WebView调用JavScript有参无返回值函数" + "\")"
webview.loadUrl(call)
}
btn_has_param_has_return.setOnClickListener {
evaluateJavaScript(webview)
}
|
对比
两种方法混合使用,即 Android 4.4 以下使用方法 1,Android 4.4 以上方法 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // Android版本变量
final int version = Build.VERSION.SDK_INT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 19) {
mWebView.loadUrl("javascript:callJS()");
} else {
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
|
2、JS 调用 Java
- 通过 WebView 的 addJavascriptInterface() 进行对象映射
Android4.2 及以上版本添加 @JavascriptInterface - WebViewClient 的 shouldOverrideUrlLoading() 方法回调拦截 url
- onConsoleMessage
- 通过 WebChromeClient 的 onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截 JS 对话框 alert()、confirm()、prompt() 消息
alert(),使用频率高
确认框,使用频率一般
提示输入框,使用频率很少
可用 jsPrompt 用来 JS 和 Java 通信的桥梁通道
通过 WebView 的 addJavascriptInterface() 进行对象映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// 由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello("js调用了android中的hello方法");
}
</script>
</head>
<body>
点击按钮则调用callAndroid函数<br/>
<button type="button" id="button1" onclick="callAndroid()">点我通过js调用java</button>
</body>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class js调用Java代码 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_js_call_java)
var webSettings = webview.settings;
// 设置与Js交互的权限
webSettings.javaScriptEnabled = true;
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript对象名
//参数2:Java对象名
webview.addJavascriptInterface(AndroidtoJs(), "test") //AndroidtoJS类对象映射到js的test对象
// 加载JS代码
// 格式规定为:file:///android_asset/文件名.html
webview.loadUrl("file:///android_asset/js/jscalljava.html")
}
}
|
在页面的 window 对象里注入了 test
对象。在 js 里可以直接调用
使用简单,仅将 Android 对象和 JS 对象映射即可
在 Android 通过 WebViewClient 复写 shouldOverrideUrlLoading()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function callAndroid(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
</script>
</head>
<!-- 点击按钮则调用callAndroid()方法 -->
<body>
<button type="button" id="button1" onclick="callAndroid()">点击调用Android代码_shouldOverrideUrlLoading
</button>
</body>
</html>
|
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| class js调用Java代码_shouldOverrideUrlLoading : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_js_call_java_should_override_url_loading)
var webSettings = webview.settings
// 设置与Js交互的权限
webSettings.javaScriptEnabled = true
// 设置允许JS弹窗
webSettings.javaScriptCanOpenWindowsAutomatically = true
webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return super.shouldOverrideUrlLoading(view, url)
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
// 步骤2:根据协议的参数,判断是否是所需要的url
// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
//假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
var uri: Uri? = request?.url ?: return super.shouldOverrideUrlLoading(view, request)
if (uri?.scheme.equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri?.authority.equals("webview")) {
// 步骤3:
// 执行JS所需要调用的逻辑
LogUtil.i("js调用了Android的方法")
// 可以在协议上带有参数并传递到Android上
var params = HashMap<String, String>()
var queryParameterNames = uri?.queryParameterNames
queryParameterNames?.forEach {
var name = it
var value = uri?.getQueryParameter(it)
LogUtil.i("name:$name,value:$value")
}
}
return true
}
return super.shouldOverrideUrlLoading(view, request)
}
}
// 步骤1:加载JS代码
// 格式规定为:file:///android_asset/文件名.html
webview.loadUrl("file:///android_asset/js/jscalljava_shouldoverrideurlloading.html")
}
}
|
不存在方式 1WebView 的 addJavascriptInterface 的漏洞
如果 JS 想要得到 Android 方法的返回值,只能通过 WebView 的 loadUrl() 去执行 JS 方法把返回值传递回去,相关的代码如下:
1
2
3
4
5
6
7
| // Android:MainActivity.java
mWebView.loadUrl("javascript:returnResult(" + result + ")");
// JS:javascript.html
function returnResult(result){
alert("result is" + result);
}
|
通过 WebChromeClient 的 onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截 JS 对话框 alert()、confirm()、prompt() 消息
在 JS 中,有三个常用的对话框方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function clickprompt(){
// 调用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<!-- 点击按钮则调用clickprompt() -->
<body>
<button type="button" id="button1" onclick="clickprompt()">点击调用Android代码通过onJsPrompt()</button>
</body>
</html>
|
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| class js调用Java代码_onJsPrompt : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_js_call_java_onjsprompt)
var webSettings = webview.settings
// 设置与Js交互的权限
webSettings.javaScriptEnabled = true
// 设置允许JS弹窗
webSettings.javaScriptCanOpenWindowsAutomatically = true
webview.webChromeClient = object : WebChromeClient() {
// 拦截输入框(原理同方式2)
// 参数message:代表promt()的内容(不是url)
// 参数result:代表输入框的返回值
override fun onJsPrompt(view: WebView?, url: String?, message: String?,
defaultValue: String?, result: JsPromptResult?): Boolean {
// 根据协议的参数,判断是否是所需要的url(原理同方式2)
// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
//假定传入进来的 url = "js://demo?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
var uri: Uri? = Uri.parse(message)
LogUtil.w("uri:${uri.toString()}")
if (uri?.scheme.equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri?.authority.equals("demo")) {
// 步骤3:
// 执行JS所需要调用的逻辑
LogUtil.i("js调用了Android的方法前,睡眠4000ms")
SystemClock.sleep(4000)
// 可以在协议上带有参数并传递到Android上
var params = HashMap<String, String>()
var queryParameterNames = uri?.queryParameterNames
queryParameterNames?.forEach {
var name = it
var value = uri?.getQueryParameter(it)
LogUtil.i("name:$name,value:$value")
}
LogUtil.i("js调用了Android的方法后")
//参数result:代表消息框的返回值(输入值)
result?.confirm("js调用了Android的方法成功啦")
}
return true
}
return super.onJsPrompt(view, url, message, defaultValue, result)
}
// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述
// 拦截JS的警告框
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
return super.onJsAlert(view, url, message, result)
}
// 拦截JS的确认框
override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
return super.onJsConfirm(view, url, message, result)
}
}
// 步骤1:加载JS代码
// 格式规定为:file:///android_asset/文件名.html
webview.loadUrl("file:///android_asset/js/jscalljava_onjsprompt.html")
}
}
|
三种方式对比
JSBridge
定制 js 调用 native 的 jsbridge 协议:
1
2
3
4
5
6
7
8
9
10
| // jsbridge://className:port/methodName?jsonObj
1. className:类名
2. port:port用来干嘛,其实js层调用native层方法后,native需要将执行结果返回给js层,WebChromeClient的onJsPrompt只能是同步的,异步的返回就不行了。这时候port就发挥了它应有的作用,我们在js中调用native方法的时候,在js中注册一个callback,然后将该callback在指定的位置上缓存起来,然后native层执行完毕对应方法后通过WebView.loadUrl调用js中的方法,回调对应的callback。那么js怎么知道调用哪个callback呢?于是我们需要将callback的一个存储位置传递过去,那么就需要native层调用js中的方法的时候将存储位置回传给js,js再调用对应存储位置上的callback,进行回调。于是,完整的协议定义如下:
jsbridge://className:callbackAddress/methodName?jsonObj
3. methodName:方法名
4. jsonObj:方法要用的参数
|
例如:
1
| jsbridge://Logger:callbackAddress/log?{"msg":"native log"}
|
jsbrige 原理
- 通过 onJsPromt,js 通过 prompt,Java 层拦截该 onJsPrompt
- js 和 Java 层定义好协议如
JSBridge://obj:port/method?params
- Java 层处理完毕,通过 js 中缓存的 callback 回调过去
jsbridge 库
三端易用的现代跨平台 Javascript bridge, 通过它,你可以在 Javascript 和原生之间同步或异步的调用彼此的函数.
基于 github.com/lzyzsd/JsBridge 优化改进而来的 JsBridge 1.基于系统的 JsBridge; 2.基于 Tbs X5 内核的 JsBridge;
https://github.com/marcuswestin/WebViewJavascriptBridge,只有 iOS 平台>
Android 通过 WebView 与 JS 的交互方式总结
Android 和 H5 交互有大图
- SVGA 时,需要加个头:
data:application/octet-stream;base64,base64字符串
- 出现
uncaught SyntaxError: Unexpected token
- 图片过大,对图片进行压缩再 base64
- Base64.DEFAULT 修改为 NO_WRAP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // JsCallCommontMethodForSalam
@CommonMethodForjs
fun bundleFile(map: HashMap<String, Any>, webView: WeakReference<WebView>) {
try {
// val s = SystemClock.uptimeMillis()
GlobalScope.launch(Dispatchers.Main) {
val name = map["name"]
val data = withContext(Dispatchers.IO) {
val input = GlobalContext.getAppContext().assets.open("web/$name")
val bytes = StreamTool.read(input)
Base64.encodeToString(bytes, Base64.NO_WRAP)
}
val base64SVGA = "\"data:application/octet-stream;base64,$data\""
callJsMethod(webView, base64SVGA, map)
// val cost = SystemClock.uptimeMillis() - s
LogUtils.i(TAG, "bundleFile cost=ms,name=$name,base64SVGA=$base64SVGA,map=$map")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
|
Ref
自己实现的简单的 JSBridge 支持异步
确认通过 onJsPrompt 来实现
1
2
3
4
5
6
7
| public class MyJSBridgeWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
result.confirm(MyJSBridge.callJava(view, message));
return true;
}
}
|
定义 MyJSBridge
存储暴露给 js 的 native 方法;拦截 onJsPrompt 给 WebChromeClient,实现调用本地 native 方法
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
public interface IBridge {
}
public final class MyJSBridge {
private static final String JSBREIDGE_NAME = "JSBridge";
/**
* key:暴露的对象名,提供给js调用的对象
*
*
<pre>
* value: { HashMap<String, Method>
*
* name: 方法名
* value: Method
* }
*
* </pre>
*/
private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>();
/**
* 注册IBridge,将该Bridge中的所有方法注册保存
*/
public static void register(String exposedName, Class<? extends IBridge> clazz) {
if (!exposedMethods.containsKey(exposedName)) {
try {
exposedMethods.put(exposedName, getAllMethod(clazz));
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取IBridge中的所有方法
*/
private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception {
HashMap<String, Method> mMethodsMap = new HashMap<>();
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 // 参数1为webview
&& parameters[1] == JSONObject.class // 参数2为:JSONObject
&& parameters[2] == JsCallback.class) // 参数3为:JsCallback
{
mMethodsMap.put(name, method);
}
}
}
return mMethodsMap;
}
/**
* 将js传来的uri进行解析,然后根据调用的类名别名从刚刚的map中查找是不是存在,存在的话拿到该类所有方法的methodMap,然后根据方法名从methodMap拿到方法,反射调用,并将参数传进去,参数就是前文说的满足条件的三个参数,即WebView,JSONObject,Callback
* @param webView webview
* @param uriString uri
* @return
*/
public static String callJava(WebView webView, String uriString) {
String methodName = "";
String className = "";
String param = "{}";
String port = "";
if (!TextUtils.isEmpty(uriString) && uriString.startsWith(JSBREIDGE_NAME)) {
Uri uri = Uri.parse(uriString);
className = uri.getHost();
param = uri.getQuery();
port = uri.getPort() + "";
String path = uri.getPath();
if (!TextUtils.isEmpty(path)) {
methodName = path.replace("/", "");
}
}
if (exposedMethods.containsKey(className)) {
HashMap<String, Method> methodHashMap = exposedMethods.get(className);
if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) {
Method method = methodHashMap.get(methodName);
if (method != null) {
try {
method.invoke(null, webView, new JSONObject(param), new JsCallback(webView, port));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
|
定义 JsCallback
JsCallback 用于 js 调用 native 后,native 可以异步执行后然后告知 js 完成了操作
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
36
37
| public class JsCallback {
private static Handler mHandler = new Handler(Looper.getMainLooper());
/**
* js中定义的callback格式
*/
private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge.onFinish('%s', %s);";
/**
* js中随机生成的port,js中有个保存callback的数组callbacks,port就是callbacks的位置,理解为数组的位置
*/
private String mPort;
/**
* 虚引用保存webview,用于java回调js方法时用,webview.loadUrl("javascript:xxx");
*/
private WeakReference<WebView> mWebViewRef;
public JsCallback(WebView view, String port) {
mWebViewRef = new WeakReference<>(view);
mPort = port;
}
public void apply(JSONObject jsonObject) {
final String execJs = String.format(CALLBACK_JS_FORMAT, mPort, String.valueOf(jsonObject));
if (mWebViewRef != null && mWebViewRef.get() != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
mWebViewRef.get().loadUrl(execJs);
}
});
}
}
}
|
编写 jsbridge.js
实现传递给 native 的协议定制;callback 的保存
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
36
37
38
39
40
41
42
43
44
45
46
| (function (win) {
var hasOwnProperty = Object.prototype.hasOwnProperty;
var JSBridge = win.JSBridge || (win.JSBridge = {});
var JSBRIDGE_PROTOCOL = 'JSBridge';
// 直接使用{}定义属性和值的键值对方式。键值键通过:连接,键与键之间用逗号隔开。
var Inner = {
callbacks: {},
call: function (obj, method, params, callback) {
console.log(obj + " " + method + " " + params + " " + callback);
var port = Util.getPort();
console.log(port);
this.callbacks[port] = callback;
var uri = Util.getUri(obj, method, params, port);
console.log(uri);
window.prompt(uri, "");
},
onFinish: function (port, jsonObj) {
var callback = this.callbacks[port];
callback && callback(jsonObj);
delete this.callbacks[port];
},
};
var Util = {
getPort: function () {
return Math.floor(Math.random() * (1 << 30));
},
getUri: function (obj, method, params, port) {
params = this.getParam(params);
var uri = JSBRIDGE_PROTOCOL + '://' + obj + ':' + port + '/' + method + '?' + params;
return uri; // JSBridge://obj:port/method?params
},
getParam: function (obj) {
if (obj && typeof obj === 'object') {
return JSON.stringify(obj);
} else {
return obj || '';
}
}
};
for (var key in Inner) {
if (!hasOwnProperty.call(JSBridge, key)) {
JSBridge[key] = Inner[key];
}
}
})(window);
|
测试
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
36
37
38
39
40
| <!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>JSBridge</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1, user-scalable=no"/>
<script src="file:///android_asset/js/MyJSBridge.js" type="text/javascript"></script>
<script type="text/javascript">
function rquestSuccess(res) {
alert(JSON.stringify(res))
}
</script>
<style>
</style>
</head>
<body>
<div>
<h3>MyJSBridge 测试</h3>
</div>
<ul class="list">
<li>
<div>
<button onclick="JSBridge.call('bridge','showToast',{'msg':'Hello JSBridge'},function(res){alert(JSON.stringify(res))})">
测试showToast
</button>
</div>
<div>
<button onclick="JSBridge.call('bridge','requestNet',{'url':'https://blog.csdn.net/sbsujjbcy/article/details/50752595'},rquestSuccess)">
网络请求
</button>
</div>
</li>
<br/>
</ul>
</body>
</html>
|
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| public final class MyBridgeImpl implements IBridge {
/**
* 定义一个展示toast的native方法给js,参数定死,具体参考的定义 {@link me.hacket.assistant.samples.ui.webview.jsbridge.myjsbridge.lib.MyJSBridge#callJava(WebView, String)}
*
* @param webView webview
* @param param js传递过来的json格式的参数
* @param callback js传递过来的callback,可以用于异步回调,java完成后回调给js
*/
public static void showToast(WebView webView, JSONObject param, final JsCallback callback) {
String message = param.optString("msg");
Toast.makeText(webView.getContext(), message, Toast.LENGTH_SHORT).show();
if (null != callback) {
try {
JSONObject object = new JSONObject();
object.put("key", "value");
object.put("key1", "value1");
callback.apply(getJSONObject(0, "ok", object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void requestNet(WebView webView, JSONObject param, final JsCallback callback) {
String url = param.optString("url");
LogUtil.i("获取到了js传递过来的url,模拟网络请求,睡眠5000ms:" + url);
SystemClock.sleep(5000);
LogUtil.i("模拟网络请求结束");
try {
if (callback != null) {
JSONObject object = new JSONObject();
object.put("name", "hacket");
object.put("age", 26);
callback.apply(getJSONObject(200, "获取网络数据成功", object));
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static JSONObject getJSONObject(int code, String msg, JSONObject result) {
JSONObject object = new JSONObject();
try {
object.put("code", code);
object.put("msg", msg);
object.putOpt("result", result);
return object;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| class MyJSBridgeTestAct : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var webview = WebView(this)
setContentView(webview)
var webSettings = webview.settings
// 设置与Js交互的权限
webSettings.javaScriptEnabled = true
// 设置允许JS弹窗
webSettings.javaScriptCanOpenWindowsAutomatically = true
MyJSBridge.register("bridge", MyBridgeImpl::class.java)
webview.webChromeClient = TestMyJSBridgeWebChromeClient()
webview.loadUrl("file:///android_asset/js/MyJSBridge_test.html")
}
inner class TestMyJSBridgeWebChromeClient : MyJSBridgeWebChromeClient()
}
|
Ref