diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 0000000..2884129
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,17 @@
+name: Android CI
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Gradle
+ run: ./gradlew build
diff --git a/JsBridge.zip b/JsBridge.zip
new file mode 100644
index 0000000..d2c837b
Binary files /dev/null and b/JsBridge.zip differ
diff --git a/README.md b/README.md
index 8d57fa8..d58fde3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-#JsBridge
+# JsBridge
-----
@@ -129,8 +129,7 @@ will print 'JS got a message hello' and 'JS responding with' in webview console.
## Notice
This lib will inject a WebViewJavascriptBridge Object to window object.
-So in your js, before use WebViewJavascriptBridge, you must detect if WebViewJavascriptBridge exist.
-If WebViewJavascriptBridge does not exit, you can listen to WebViewJavascriptBridgeReady event, as the blow code shows:
+You can listen to `WebViewJavascriptBridgeReady` event to ensure `window.WebViewJavascriptBridge` is exist, as the blow code shows:
```javascript
@@ -148,6 +147,38 @@ If WebViewJavascriptBridge does not exit, you can listen to WebViewJavascriptBri
```
+Or put all JsBridge function call into `window.WVJBCallbacks` array if `window.WebViewJavascriptBridge` is undefined, this taks queue will be flushed when `WebViewJavascriptBridgeReady` event triggered.
+
+Copy and paste setupWebViewJavascriptBridge into your JS:
+
+```javascript
+function setupWebViewJavascriptBridge(callback) {
+ if (window.WebViewJavascriptBridge) {
+ return callback(WebViewJavascriptBridge);
+ }
+ if (window.WVJBCallbacks) {
+ return window.WVJBCallbacks.push(callback);
+ }
+ window.WVJBCallbacks = [callback];
+}
+```
+
+Call `setupWebViewJavascriptBridge` and then use the bridge to register handlers or call Java handlers:
+
+```javascript
+setupWebViewJavascriptBridge(function(bridge) {
+ bridge.registerHandler('JS Echo', function(data, responseCallback) {
+ console.log("JS Echo called with:", data);
+ responseCallback(data);
+ });
+ bridge.callHandler('ObjC Echo', {'key':'value'}, function(responseData) {
+ console.log("JS received response:", responseData);
+ });
+});
+```
+
+It same with https://github.com/marcuswestin/WebViewJavascriptBridge, that would be easier for you to define same behavior in different platform between Android and iOS. Meanwhile, writing concise code.
+
## License
This project is licensed under the terms of the MIT license.
diff --git a/build.gradle b/build.gradle
index a4648d2..ba3db91 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -23,7 +23,7 @@ allprojects {
}
google()
}
- tasks.withType(Javadoc) { // 这一段是为了消除gbk的错误
+ tasks.withType(Javadoc) { // 这一段是为了消除gbk的错�?
options{
encoding "UTF-8"
charSet 'UTF-8'
diff --git a/example/build.gradle b/example/build.gradle
index 8d69880..c6950fb 100644
--- a/example/build.gradle
+++ b/example/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 25
+ compileSdkVersion 28
// buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.github.lzyzsd.jsbridge.example"
- minSdkVersion 9
- targetSdkVersion 25
+ minSdkVersion 17
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
}
@@ -20,8 +20,8 @@ android {
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile project(':library')
- compile 'com.android.support:appcompat-v7:25.3.1'
- compile 'com.google.code.gson:gson:2.3.1'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(':library')
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'com.google.code.gson:gson:2.8.5'
}
diff --git a/example/src/main/assets/demo.html b/example/src/main/assets/demo.html
index dc358fc..38d106b 100644
--- a/example/src/main/assets/demo.html
+++ b/example/src/main/assets/demo.html
@@ -1,42 +1,42 @@
-
-
-
- js调用java
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/example/src/main/java/com/github/lzyzsd/jsbridge/example/CustomWebView.java b/example/src/main/java/com/github/lzyzsd/jsbridge/example/CustomWebView.java
new file mode 100644
index 0000000..1b5935c
--- /dev/null
+++ b/example/src/main/java/com/github/lzyzsd/jsbridge/example/CustomWebView.java
@@ -0,0 +1,115 @@
+package com.github.lzyzsd.jsbridge.example;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.github.lzyzsd.jsbridge.BridgeHandler;
+import com.github.lzyzsd.jsbridge.BridgeHelper;
+import com.github.lzyzsd.jsbridge.CallBackFunction;
+import com.github.lzyzsd.jsbridge.IWebView;
+import com.github.lzyzsd.jsbridge.WebViewJavascriptBridge;
+
+/**
+ * 采用BridgeHelper集成JsBridge功能示例.定制WebView,可只添加实际需要的JsBridge接口.
+ *
+ * @author ZhengAn
+ * @date 2019-07-07
+ */
+@SuppressLint("SetJavaScriptEnabled")
+public class CustomWebView extends WebView implements WebViewJavascriptBridge, IWebView {
+
+ private BridgeHelper bridgeHelper;
+
+ public CustomWebView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ public CustomWebView(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ this.setVerticalScrollBarEnabled(false);
+ this.setHorizontalScrollBarEnabled(false);
+ this.getSettings().setJavaScriptEnabled(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ WebView.setWebContentsDebuggingEnabled(true);
+ }
+
+ bridgeHelper = new BridgeHelper(this);
+ this.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView webView, String s) {
+ bridgeHelper.onPageFinished();
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView webView, String s) {
+ return bridgeHelper.shouldOverrideUrlLoading(s);
+ }
+ });
+ }
+
+ /**
+ * @param handler default handler,handle messages send by js without assigned handler name,
+ * if js message has handler name, it will be handled by named handlers registered by native
+ */
+ public void setDefaultHandler(BridgeHandler handler) {
+ bridgeHelper.setDefaultHandler(handler);
+ }
+
+ @Override
+ public void send(String data) {
+ send(data, null);
+ }
+
+ @Override
+ public void send(String data, CallBackFunction responseCallback) {
+ bridgeHelper.send(data, responseCallback);
+ }
+
+
+ /**
+ * register handler,so that javascript can call it
+ * 注册处理程序,以便javascript调用它
+ *
+ * @param handlerName handlerName
+ * @param handler BridgeHandler
+ */
+ public void registerHandler(String handlerName, BridgeHandler handler) {
+ bridgeHelper.registerHandler(handlerName, handler);
+ }
+
+ /**
+ * unregister handler
+ *
+ * @param handlerName
+ */
+ public void unregisterHandler(String handlerName) {
+ bridgeHelper.unregisterHandler(handlerName);
+ }
+
+ /**
+ * call javascript registered handler
+ * 调用javascript处理程序注册
+ *
+ * @param handlerName handlerName
+ * @param data data
+ * @param callBack CallBackFunction
+ */
+ public void callHandler(String handlerName, String data, CallBackFunction callBack) {
+ bridgeHelper.callHandler(handlerName, data, callBack);
+ }
+
+}
diff --git a/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainActivity.java b/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainActivity.java
index aedb2b4..dced3b6 100644
--- a/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainActivity.java
+++ b/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainActivity.java
@@ -12,10 +12,8 @@
import android.webkit.WebView;
import android.widget.Button;
-import com.github.lzyzsd.jsbridge.BridgeHandler;
import com.github.lzyzsd.jsbridge.BridgeWebView;
-import com.github.lzyzsd.jsbridge.CallBackFunction;
-import com.github.lzyzsd.jsbridge.DefaultHandler;
+import com.github.lzyzsd.jsbridge.OnBridgeCallback;
import com.google.gson.Gson;
public class MainActivity extends Activity implements OnClickListener {
@@ -53,7 +51,6 @@ protected void onCreate(Bundle savedInstanceState) {
button.setOnClickListener(this);
- webView.setDefaultHandler(new DefaultHandler());
webView.setWebChromeClient(new WebChromeClient() {
@@ -80,32 +77,24 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC
}
});
+ webView.addJavascriptInterface(new MainJavascrotInterface(webView.getCallbacks(), webView), "android");
+ webView.setGson(new Gson());
webView.loadUrl("file:///android_asset/demo.html");
- webView.registerHandler("submitFromWeb", new BridgeHandler() {
-
- @Override
- public void handler(String data, CallBackFunction function) {
- Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
- function.onCallBack("submitFromWeb exe, response data 中文 from Java");
- }
-
- });
-
User user = new User();
Location location = new Location();
location.address = "SDU";
user.location = location;
user.name = "大头鬼";
- webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
+ webView.callHandler("functionInJs", new Gson().toJson(user), new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
-
+ Log.d(TAG, "onCallBack: " + data);
}
});
- webView.send("hello");
+ webView.sendToWeb("hello");
}
@@ -129,7 +118,9 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
if(null == mUploadMessage && null != mUploadMessageArray){
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
- mUploadMessageArray.onReceiveValue(new Uri[]{result});
+ if (result != null) {
+ mUploadMessageArray.onReceiveValue(new Uri[]{result});
+ }
mUploadMessageArray = null;
}
@@ -139,7 +130,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
@Override
public void onClick(View v) {
if (button.equals(v)) {
- webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {
+ webView.callHandler("functionInJs", "data from Java", new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
diff --git a/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainJavascrotInterface.java b/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainJavascrotInterface.java
new file mode 100644
index 0000000..293a7e5
--- /dev/null
+++ b/example/src/main/java/com/github/lzyzsd/jsbridge/example/MainJavascrotInterface.java
@@ -0,0 +1,36 @@
+package com.github.lzyzsd.jsbridge.example;
+
+import android.util.Log;
+import android.webkit.JavascriptInterface;
+
+import com.github.lzyzsd.jsbridge.BridgeWebView;
+import com.github.lzyzsd.jsbridge.OnBridgeCallback;
+
+import java.util.Map;
+
+/**
+ * Created on 2019/7/10.
+ * Author: bigwang
+ * Description:
+ */
+public class MainJavascrotInterface extends BridgeWebView.BaseJavascriptInterface {
+
+ private BridgeWebView mWebView;
+
+ public MainJavascrotInterface(Map callbacks, BridgeWebView webView) {
+ super(callbacks);
+ mWebView = webView;
+ }
+
+ @Override
+ public String send(String data) {
+ return "it is default response";
+ }
+
+
+ @JavascriptInterface
+ public void submitFromWeb(String data, String callbackId) {
+ Log.d("chromium data", data + ", callbackId: " + callbackId + " " + Thread.currentThread().getName());
+ mWebView.sendResponse("submitFromWeb response", callbackId);
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 4e669b2..7293145 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,3 +17,5 @@
#systemProp.http.proxyHost=127.0.0.1
#systemProp.https.proxyHost=127.0.0.1
#systemProp.http.proxyPort=1080
+android.enableJetifier=true
+android.useAndroidX=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 34495fa..85cbcc2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat Mar 31 22:44:06 CST 2018
+#Wed Mar 24 16:24:00 CST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/library/build.gradle b/library/build.gradle
index 1eba597..500ecf2 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -3,12 +3,12 @@ apply plugin: 'com.android.library'
version = "1.0.0"
android {
- compileSdkVersion 25
+ compileSdkVersion 28
// buildToolsVersion "25.0.3"
defaultConfig {
- minSdkVersion 9
- targetSdkVersion 25
+ minSdkVersion 17
+ targetSdkVersion 28
versionCode 1
versionName version
}
@@ -25,28 +25,30 @@ android {
}
dependencies {
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'com.google.code.gson:gson:2.8.5'
}
-//def siteUrl = 'https://github.com/lzyzsd/JsBridge'
-//def gitUrl = 'https://github.com/lzyzsd/JsBridge.git'
-//apply plugin: 'com.github.dcendents.android-maven'
-//group = "com.github.lzyzsd.jsbridge"
-//task sourcesJar(type: Jar) {
-// from android.sourceSets.main.java.srcDirs
-// classifier = 'sources'
-//}
-//task javadoc(type: Javadoc) {
-// source = android.sourceSets.main.java.srcDirs
-// classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-//}
-//task javadocJar(type: Jar, dependsOn: javadoc) {
-// classifier = 'javadoc'
-// from javadoc.destinationDir
-//}
-//artifacts {
-// archives javadocJar
-// archives sourcesJar
-//}
-//Properties properties = new Properties()
-//properties.load(project.rootProject.file('local.properties').newDataInputStream())
+def siteUrl = 'https://github.com/lzyzsd/JsBridge'
+def gitUrl = 'https://github.com/lzyzsd/JsBridge.git'
+// apply plugin: 'com.github.dcendents.android-maven'
+group = "com.github.lzyzsd.jsbridge"
+task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier = 'sources'
+}
+task javadoc(type: Javadoc) {
+ source = android.sourceSets.main.java.srcDirs
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+}
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+}
+artifacts {
+ archives javadocJar
+ archives sourcesJar
+}
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
diff --git a/library/src/main/assets/WebViewJavascriptBridge.js b/library/src/main/assets/WebViewJavascriptBridge.js
index 27743b9..bf43554 100644
--- a/library/src/main/assets/WebViewJavascriptBridge.js
+++ b/library/src/main/assets/WebViewJavascriptBridge.js
@@ -6,15 +6,9 @@
return;
}
- var messagingIframe;
- var bizMessagingIframe;
- var sendMessageQueue = [];
var receiveMessageQueue = [];
var messageHandlers = {};
- var CUSTOM_PROTOCOL_SCHEME = 'yy';
- var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
-
var responseCallbacks = {};
var uniqueId = 1;
@@ -49,9 +43,7 @@
// 发送
function send(data, responseCallback) {
- _doSend({
- data: data
- }, responseCallback);
+ _doSend('send', data, responseCallback);
}
// 注册线程 往数组里面添加值
@@ -60,19 +52,43 @@
}
// 调用线程
function callHandler(handlerName, data, responseCallback) {
- _doSend({
- handlerName: handlerName,
- data: data
- }, responseCallback);
+ // 如果方法不需要参数,只有回调函数,简化JS中的调用
+ if (arguments.length == 2 && typeof data == 'function') {
+ responseCallback = data;
+ data = null;
+ }
+ _doSend(handlerName, data, responseCallback);
}
//sendMessage add message, 触发native处理 sendMessage
- function _doSend(message, responseCallback) {
- if (responseCallback) {
- var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
+ function _doSend(handlerName, message, responseCallback) {
+ var callbackId;
+ if(typeof responseCallback === 'string'){
+ callbackId = responseCallback;
+ } else if (responseCallback) {
+ callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
+ }else{
+ callbackId = '';
}
+ try {
+ var fn = eval('window.android.' + handlerName);
+ } catch(e) {
+ console.log(e);
+ }
+ if (typeof fn === 'function'){
+ var responseData = fn.call(this, JSON.stringify(message), callbackId);
+ if(responseData){
+ console.log('response message: '+ responseData);
+ responseCallback = responseCallbacks[callbackId];
+ if (!responseCallback) {
+ return;
+ }
+ responseCallback(responseData);
+ delete responseCallbacks[callbackId];
+ }
+ }
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
@@ -99,7 +115,6 @@
sendMessageQueue = [];
//android can't read directly the return data, so we can reload iframe src to communicate with java
bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
-
}
//提供给native使用,
@@ -120,10 +135,7 @@
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
- _doSend({
- responseId: callbackResponseId,
- responseData: responseData
- });
+ _doSend('response', responseData, callbackResponseId);
};
}
@@ -145,12 +157,12 @@
//提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
function _handleMessageFromNative(messageJSON) {
- console.log(messageJSON);
+ console.log('handle message: '+ messageJSON);
if (receiveMessageQueue) {
receiveMessageQueue.push(messageJSON);
}
_dispatchMessageFromNative(messageJSON);
-
+
}
var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
@@ -158,15 +170,17 @@
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
- _fetchQueue: _fetchQueue,
_handleMessageFromNative: _handleMessageFromNative
};
var doc = document;
- _createQueueReadyIframe(doc);
- _createQueueReadyIframe4biz(doc);
var readyEvent = doc.createEvent('Events');
+ var jobs = window.WVJBCallbacks || [];
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
+ window.WVJBCallbacks = []
+ jobs.forEach(function (job) {
+ job(WebViewJavascriptBridge)
+ })
doc.dispatchEvent(readyEvent);
})();
diff --git a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHandler.java b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHandler.java
deleted file mode 100644
index 2d745c1..0000000
--- a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHandler.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.github.lzyzsd.jsbridge;
-
-public interface BridgeHandler {
-
- void handler(String data, CallBackFunction function);
-
-}
diff --git a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHelper.java b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHelper.java
new file mode 100644
index 0000000..d4e4bfe
--- /dev/null
+++ b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeHelper.java
@@ -0,0 +1,288 @@
+package com.github.lzyzsd.jsbridge;
+
+import android.os.Looper;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JsBridge辅助类,帮助集成JsBridge功能.
+ *
+ * @author ZhengAn
+ * @date 2019-06-30
+ */
+public class BridgeHelper implements WebViewJavascriptBridge {
+
+ private static final String TAG = "BridgeHelper";
+
+ private static final String BRIDGE_JS = "WebViewJavascriptBridge.js";
+ private Map responseCallbacks = new HashMap<>();
+ private Map messageHandlers = new HashMap<>();
+ private BridgeHandler defaultHandler = new DefaultHandler();
+
+ private List startupMessage = new ArrayList<>();
+
+ private List getStartupMessage() {
+ return startupMessage;
+ }
+
+ private void setStartupMessage(List startupMessage) {
+ this.startupMessage = startupMessage;
+ }
+
+ private long uniqueId = 0;
+
+ private IWebView webView;
+
+ public BridgeHelper(IWebView webView) {
+ this.webView = webView;
+ }
+
+ /**
+ * @param handler default handler,handle messages send by js without assigned handler name,
+ * if js message has handler name, it will be handled by named handlers registered by native
+ */
+ public void setDefaultHandler(BridgeHandler handler) {
+ this.defaultHandler = handler;
+ }
+
+ /**
+ * 获取到CallBackFunction data执行调用并且从数据集移除
+ *
+ * @param url
+ */
+ private void handlerReturnData(String url) {
+ String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
+ CallBackFunction f = responseCallbacks.get(functionName);
+ String data = BridgeUtil.getDataFromReturnUrl(url);
+ if (f != null) {
+ f.onCallBack(data);
+ responseCallbacks.remove(functionName);
+ }
+ }
+
+ @Override
+ public void send(String data) {
+ send(data, null);
+ }
+
+ @Override
+ public void send(String data, CallBackFunction responseCallback) {
+ doSend(null, data, responseCallback);
+ }
+
+ /**
+ * 保存message到消息队列
+ *
+ * @param handlerName handlerName
+ * @param data data
+ * @param responseCallback CallBackFunction
+ */
+ private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
+ Message m = new Message();
+ if (!TextUtils.isEmpty(data)) {
+ m.setData(data);
+ }
+ if (responseCallback != null) {
+ String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
+ responseCallbacks.put(callbackStr, responseCallback);
+ m.setCallbackId(callbackStr);
+ }
+ if (!TextUtils.isEmpty(handlerName)) {
+ m.setHandlerName(handlerName);
+ }
+ queueMessage(m);
+ }
+
+ /**
+ * list != null 添加到消息集合否则分发消息
+ *
+ * @param m Message
+ */
+ private void queueMessage(Message m) {
+ if (startupMessage != null) {
+ startupMessage.add(m);
+ } else {
+ dispatchMessage(m);
+ }
+ }
+
+ /**
+ * 分发message 必须在主线程才分发成功
+ *
+ * @param m Message
+ */
+ private void dispatchMessage(Message m) {
+ String messageJson = m.toJson();
+ //escape special characters for json string 为json字符串转义特殊字符
+ messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
+ messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
+ messageJson = messageJson.replaceAll("(?<=[^\\\\])(\')", "\\\\\'");
+ messageJson = messageJson.replaceAll("%7B", URLEncoder.encode("%7B"));
+ messageJson = messageJson.replaceAll("%7D", URLEncoder.encode("%7D"));
+ messageJson = messageJson.replaceAll("%22", URLEncoder.encode("%22"));
+ String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
+ // 必须要找主线程才会将数据传递出去 --- 划重点
+ if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
+ this.loadUrl(javascriptCommand);
+ }
+ }
+
+ /**
+ * 刷新消息队列
+ */
+ private void flushMessageQueue() {
+ if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
+ loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
+
+ @Override
+ public void onCallBack(String data) {
+ // deserializeMessage 反序列化消息
+ List list = null;
+ try {
+ list = Message.toArrayList(data);
+ } catch (Exception e) {
+ Log.w(TAG, e);
+ return;
+ }
+ if (list == null || list.isEmpty()) {
+ return;
+ }
+ for (int i = 0; i < list.size(); i++) {
+ Message m = list.get(i);
+ String responseId = m.getResponseId();
+ // 是否是response CallBackFunction
+ if (!TextUtils.isEmpty(responseId)) {
+ CallBackFunction function = responseCallbacks.get(responseId);
+ String responseData = m.getResponseData();
+ function.onCallBack(responseData);
+ responseCallbacks.remove(responseId);
+ } else {
+ CallBackFunction responseFunction = null;
+ // if had callbackId 如果有回调Id
+ final String callbackId = m.getCallbackId();
+ if (!TextUtils.isEmpty(callbackId)) {
+ responseFunction = new CallBackFunction() {
+ @Override
+ public void onCallBack(String data) {
+ Message responseMsg = new Message();
+ responseMsg.setResponseId(callbackId);
+ responseMsg.setResponseData(data);
+ queueMessage(responseMsg);
+ }
+ };
+ } else {
+ responseFunction = new CallBackFunction() {
+ @Override
+ public void onCallBack(String data) {
+ // do nothing
+ }
+ };
+ }
+ // BridgeHandler执行
+ BridgeHandler handler;
+ if (!TextUtils.isEmpty(m.getHandlerName())) {
+ handler = messageHandlers.get(m.getHandlerName());
+ } else {
+ handler = defaultHandler;
+ }
+ if (handler != null) {
+ handler.handler(m.getData(), responseFunction);
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ private void loadUrl(String jsUrl, CallBackFunction returnCallback) {
+ this.loadUrl(jsUrl);
+ // 添加至 Map
+ responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
+ }
+
+ private void loadUrl(String jsUrl) {
+ webView.loadUrl(jsUrl);
+ }
+
+ /**
+ * register handler,so that javascript can call it
+ * 注册处理程序,以便javascript调用它
+ *
+ * @param handlerName handlerName
+ * @param handler BridgeHandler
+ */
+ public void registerHandler(String handlerName, BridgeHandler handler) {
+ if (handler != null) {
+ // 添加至 Map
+ messageHandlers.put(handlerName, handler);
+ }
+ }
+
+ /**
+ * unregister handler
+ *
+ * @param handlerName
+ */
+ public void unregisterHandler(String handlerName) {
+ if (handlerName != null) {
+ messageHandlers.remove(handlerName);
+ }
+ }
+
+ /**
+ * call javascript registered handler
+ * 调用javascript处理程序注册
+ *
+ * @param handlerName handlerName
+ * @param data data
+ * @param callBack CallBackFunction
+ */
+ public void callHandler(String handlerName, String data, CallBackFunction callBack) {
+ doSend(handlerName, data, callBack);
+ }
+
+ public void onPageFinished() {
+ webViewLoadLocalJs();
+
+ if (getStartupMessage() != null) {
+ for (Message m : getStartupMessage()) {
+ dispatchMessage(m);
+ }
+ setStartupMessage(null);
+ }
+ }
+
+ private void webViewLoadLocalJs() {
+ String jsContent = BridgeUtil.assetFile2Str(webView.getContext(), BridgeHelper.BRIDGE_JS);
+ loadUrl("javascript:" + jsContent);
+ }
+
+ public boolean shouldOverrideUrlLoading(String url) {
+ try {
+ // decode 之前,处理 % 和 +
+ String replacedUrl = url.replaceAll("%(?![0-9a-fA-F]{2})", "%25").replaceAll("\\+", "%2B");
+ url = URLDecoder.decode(replacedUrl, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ Log.w(TAG, e);
+ }
+
+ if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) { // 如果是返回数据
+ handlerReturnData(url);
+ return true;
+ } else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //
+ flushMessageQueue();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeUtil.java b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeUtil.java
index ae9e55c..dee8584 100644
--- a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeUtil.java
+++ b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeUtil.java
@@ -8,6 +8,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
+
public class BridgeUtil {
final static String YY_OVERRIDE_SCHEMA = "yy://";
final static String YY_RETURN_DATA = YY_OVERRIDE_SCHEMA + "return/";//格式为 yy://return/{function}/returncontent
@@ -17,53 +18,17 @@ public class BridgeUtil {
final static String SPLIT_MARK = "/";
final static String CALLBACK_ID_FORMAT = "JAVA_CB_%s";
- final static String JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');";
+ final static String JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative(%s);";
final static String JS_FETCH_QUEUE_FROM_JAVA = "javascript:WebViewJavascriptBridge._fetchQueue();";
public final static String JAVASCRIPT_STR = "javascript:";
- // 例子 javascript:WebViewJavascriptBridge._fetchQueue(); --> _fetchQueue
- public static String parseFunctionName(String jsUrl){
- return jsUrl.replace("javascript:WebViewJavascriptBridge.", "").replaceAll("\\(.*\\);", "");
- }
-
- // 获取到传递信息的body值
- // url = yy://return/_fetchQueue/[{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
- public static String getDataFromReturnUrl(String url) {
- if(url.startsWith(YY_FETCH_QUEUE)) {
- // return = [{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
- return url.replace(YY_FETCH_QUEUE, EMPTY_STR);
- }
- // temp = _fetchQueue/[{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
- String temp = url.replace(YY_RETURN_DATA, EMPTY_STR);
- String[] functionAndData = temp.split(SPLIT_MARK);
+ public static final String JAVA_SCRIPT = "WebViewJavascriptBridge.js";
+ public final static String UNDERLINE_STR = "_";
+ public final static String CALLBACK_ID_FORMAT = "JAVA_CB_%s";
+ public final static String JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');";
+ public final static String JAVASCRIPT_STR = "javascript:%s";
- if(functionAndData.length >= 2) {
- StringBuilder sb = new StringBuilder();
- for (int i = 1; i < functionAndData.length; i++) {
- sb.append(functionAndData[i]);
- }
- // return = [{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
- return sb.toString();
- }
- return null;
- }
-
- // 获取到传递信息的方法
- // url = yy://return/_fetchQueue/[{"responseId":"JAVA_CB_1_360","responseData":"Javascript Says Right back aka!"}]
- public static String getFunctionFromReturnUrl(String url) {
- // temp = _fetchQueue/[{"responseId":"JAVA_CB_1_360","responseData":"Javascript Says Right back aka!"}]
- String temp = url.replace(YY_RETURN_DATA, EMPTY_STR);
- String[] functionAndData = temp.split(SPLIT_MARK);
- if(functionAndData.length >= 1){
- // functionAndData[0] = _fetchQueue
- return functionAndData[0];
- }
- return null;
- }
-
-
-
/**
* js 文件将注入为第一个script引用
* @param view WebView
@@ -117,6 +82,7 @@ public static String assetFile2Str(Context c, String urlStr){
try {
in.close();
} catch (IOException e) {
+ e.printStackTrace();
}
}
}
diff --git a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeWebView.java b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeWebView.java
index 5728681..4ec4f77 100644
--- a/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeWebView.java
+++ b/library/src/main/java/com/github/lzyzsd/jsbridge/BridgeWebView.java
@@ -5,265 +5,263 @@
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
+import androidx.collection.ArrayMap;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.webkit.SslErrorHandler;
import android.webkit.WebView;
+import android.util.Log;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.github.lzyzsd.library.BuildConfig;
+import com.google.gson.Gson;
+
+
+import org.json.JSONObject;
+
import java.net.URLEncoder;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressLint("SetJavaScriptEnabled")
-public class BridgeWebView extends WebView implements WebViewJavascriptBridge{
-
- private final String TAG = "BridgeWebView";
+public class BridgeWebView extends WebView implements WebViewJavascriptBridge, BridgeWebViewClient.OnLoadJSListener {
+ private final int URL_MAX_CHARACTER_NUM=2097152;
public static final String toLoadJs = "WebViewJavascriptBridge.js";
Map responseCallbacks = new HashMap();
Map messageHandlers = new HashMap();
BridgeHandler defaultHandler = new DefaultHandler();
+ private Map mCallbacks = new ArrayMap<>();
- private List startupMessage = new ArrayList();
+ private List