Skip to content

Commit 2fe0245

Browse files
author
lemon
committed
添加jsbridge注入的重试机制,尽可能确保注入成功
1 parent f20ad60 commit 2fe0245

File tree

6 files changed

+145
-41
lines changed

6 files changed

+145
-41
lines changed
0 Bytes
Binary file not shown.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

easybridge/src/main/assets/easybridge.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@
3030
}
3131
//the name '_easybridge' is an java object that mapping to a javascript object,using addJavaInterface in Java code
3232
if (window._easybridge) {
33-
setTimeout(function () {
34-
//the function enqueue is the pubic method from Java Code,using to call Java logic
35-
_easybridge.enqueue(handlerName, location.href, parameters, callbackId);
36-
}, 0);
33+
//the function enqueue is the pubic method from Java Code,using to call Java logic
34+
_easybridge.enqueue(handlerName, location.href, parameters, callbackId);
3735
} else {
3836
console.error(bridgeName + ':' + "the mapping object '_easybridge' had not been added any more");
3937
}
@@ -111,6 +109,8 @@
111109
_dispatchResult: _dispatchResult,
112110

113111
};
112+
//notify to native that the bridge had been injected finished
113+
window[bridgeName].callHandler('rejectFinished');
114114
//notify to javascript that the bridge had been init
115115
var doc = document;
116116
var readyEvent = doc.createEvent('Events');

easybridge/src/main/java/tech/easily/easybridge/lib/EasyBridge.java

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.os.Build;
44
import android.os.Handler;
5+
import android.os.Looper;
56
import android.text.TextUtils;
67
import android.webkit.JavascriptInterface;
78
import android.webkit.ValueCallback;
@@ -25,16 +26,12 @@
2526
*/
2627
final class EasyBridge {
2728

28-
private static class CallBackHandler extends Handler {
29-
}
30-
31-
private static final String JAVA_SCRIPT_PROTOCOL = "javascript:";
3229
private static final String CALLBACK_FUNCTION = "%s._dispatchResult(\"%s\",\'%s\')";
3330
private static final String EXECUTE_SCRIPT = "%s._executeScript(\'%s\',\'%s\',\'%s\')";
3431

3532
private Map<String, BridgeHandler> registerHandlerMap;
3633
private Map<String, ResultCallBack> jsCallbackMap;
37-
private CallBackHandler callBackHandler;
34+
private Handler callBackHandler;
3835
private SoftReference<EasyBridgeWebView> bridgeWebView;
3936
private String bridgeName;
4037
private static long uniqueId = 1;
@@ -44,7 +41,7 @@ private static class CallBackHandler extends Handler {
4441
registerHandlerMap = new HashMap<>();
4542
jsCallbackMap = new HashMap<>();
4643
this.bridgeWebView = new SoftReference<>(webView);
47-
this.callBackHandler = new CallBackHandler();
44+
this.callBackHandler = new Handler(Looper.getMainLooper());
4845
}
4946

5047

@@ -138,28 +135,21 @@ private void dispatchResult(String callbackId, String parameters) {
138135
return;
139136
}
140137
final String callBackScript = String.format(CALLBACK_FUNCTION, bridgeName, callbackId, parameters);
138+
139+
}
140+
141+
private void executeScriptInMain(final String script) {
142+
if (bridgeWebView == null || bridgeWebView.get() == null) {
143+
return;
144+
}
141145
callBackHandler.post(new Runnable() {
142146
@Override
143147
public void run() {
144-
executeScriptInMain(callBackScript);
148+
bridgeWebView.get().evaluateJavascript(script);
145149
}
146150
});
147151
}
148152

149-
private void executeScriptInMain(String script) {
150-
// 如果系统版本在android4.4及以上,则使用evaluateJavascript,这个方法可以拿到js执行的返回值,否则使用loadUrl
151-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
152-
bridgeWebView.get().evaluateJavascript(script, new ValueCallback<String>() {
153-
@Override
154-
public void onReceiveValue(String value) {
155-
156-
}
157-
});
158-
} else {
159-
bridgeWebView.get().loadUrl(String.format("%s%s", JAVA_SCRIPT_PROTOCOL, script));
160-
}
161-
}
162-
163153
/**
164154
* find the suitable handler
165155
*
Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
package tech.easily.easybridge.lib;
22

3+
import android.os.Handler;
4+
import android.os.Looper;
5+
import android.text.TextUtils;
6+
import android.util.Log;
37
import android.webkit.WebChromeClient;
48
import android.webkit.WebView;
59

10+
import java.util.Date;
11+
612
/**
713
* Created by lemon on 2018/4/2.
814
*/
915
public class EasyBridgeWebChromeClient extends WebChromeClient {
1016

1117
private static final String BRIDGE_SCRIPT_PATH = "easybridge.js";
12-
private static final String JAVA_SCRIPT_PROTOCOL = "javascript:";
1318
public static final String SECURITY_CHECK_PARAMETERS = "inject bridge script";
14-
private boolean isInjected;
19+
private boolean hasCalled;
1520
private EasyBridgeWebView easyBridgeWebView;
21+
private String injectScript;
22+
private InjectBridgeTask injectBridgeTask;
23+
1624

1725
public EasyBridgeWebChromeClient(EasyBridgeWebView webView) {
1826
this.easyBridgeWebView = webView;
@@ -21,32 +29,92 @@ public EasyBridgeWebChromeClient(EasyBridgeWebView webView) {
2129
@Override
2230
public void onProgressChanged(WebView view, int newProgress) {
2331
super.onProgressChanged(view, newProgress);
32+
if (injectBridgeTask == null) {
33+
injectBridgeTask = new InjectBridgeTask(this);
34+
}
2435
// inject the bridge code when the progress above 25% to make sure it worked
2536
if (newProgress <= 25) {
26-
isInjected = false;
37+
hasCalled = false;
2738
return;
2839
}
29-
if (!isInjected) {
40+
if (!hasCalled) {
41+
hasCalled = true;
42+
// reset the injected status label before injecting a new bridge to the page
43+
if (easyBridgeWebView != null) {
44+
easyBridgeWebView.setInjected(false);
45+
}
3046
// TODO: 2018/4/3 it seems that the url received here possibly not the same as the real page url we look forward to
3147
if (easyBridgeWebView.checkSecurityGlobally(view.getUrl(), SECURITY_CHECK_PARAMETERS)) {
32-
isInjected = true;
33-
injectBridge(view);
48+
injectBridgeTask.start();
3449
} else {
35-
deleteBridge(view);
50+
deleteBridge();
3651
}
3752
}
3853
}
3954

40-
private void injectBridge(WebView view) {
41-
String bridgeScript = Utils.readAssetFile(view.getContext(), BRIDGE_SCRIPT_PATH);
42-
String executeScript = "var bridgeName = " + "\"" + easyBridgeWebView.getBridgeName() + "\"" + ";" + bridgeScript;
43-
view.loadUrl(String.format("%s%s", JAVA_SCRIPT_PROTOCOL, executeScript));
55+
private void injectBridge() {
56+
if (easyBridgeWebView == null) {
57+
return;
58+
}
59+
if (TextUtils.isEmpty(injectScript)) {
60+
injectScript = Utils.readAssetFile(easyBridgeWebView.getContext(), BRIDGE_SCRIPT_PATH);
61+
injectScript = "var bridgeName = " + "\"" + easyBridgeWebView.getBridgeName() + "\"" + ";" + injectScript;
62+
}
63+
easyBridgeWebView.evaluateJavascript(injectScript);
4464
}
4565

46-
private void deleteBridge(WebView view) {
47-
// execute script below to remove the bridge had set before :
48-
// delete _easybridge;
66+
67+
private boolean isInjected() {
68+
return easyBridgeWebView != null && easyBridgeWebView.isInjected();
69+
}
70+
71+
/**
72+
* as EasyBridge will inject an object named '_easybridge' into JavaScript context using addJavaScriptInterface()
73+
* we need to removed it when security forbidden
74+
*/
75+
private void deleteBridge() {
76+
if (easyBridgeWebView == null) {
77+
return;
78+
}
4979
String script = "delete " + EasyBridgeWebView.MAPPING_JS_INTERFACE_NAME + ";";
50-
view.loadUrl(String.format("%s%s", JAVA_SCRIPT_PROTOCOL, script));
80+
easyBridgeWebView.evaluateJavascript(script);
81+
}
82+
83+
/**
84+
* a task using to inject bridge ,
85+
* it will try to re-inject the bridge every 300ms(at most 5 times) if the status of bridgeInjected is false
86+
*/
87+
private static class InjectBridgeTask implements Runnable {
88+
89+
private static final int RETRY_COUNT = 5;
90+
private static final long RETRY_DELAY_INTERVAL = 300;
91+
private int retryCount;
92+
private EasyBridgeWebChromeClient webChromeClient;
93+
private Handler mainHandler;
94+
95+
InjectBridgeTask(EasyBridgeWebChromeClient webChromeClient) {
96+
this.webChromeClient = webChromeClient;
97+
this.mainHandler = new Handler(Looper.getMainLooper());
98+
}
99+
100+
@Override
101+
public void run() {
102+
if (!webChromeClient.isInjected() && retryCount <= RETRY_COUNT) {
103+
webChromeClient.injectBridge();
104+
retryCount++;
105+
mainHandler.postDelayed(this, RETRY_DELAY_INTERVAL);
106+
} else {
107+
reset();
108+
}
109+
}
110+
111+
private void reset() {
112+
mainHandler.removeCallbacks(this);
113+
retryCount = 0;
114+
}
115+
116+
public void start() {
117+
mainHandler.post(this);
118+
}
51119
}
52120
}

easybridge/src/main/java/tech/easily/easybridge/lib/EasyBridgeWebView.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@
33
import android.annotation.SuppressLint;
44
import android.content.Context;
55
import android.content.res.TypedArray;
6+
import android.os.Build;
7+
import android.os.Handler;
8+
import android.os.Looper;
9+
import android.support.annotation.Nullable;
610
import android.util.AttributeSet;
11+
import android.util.Log;
12+
import android.webkit.ValueCallback;
713
import android.webkit.WebSettings;
814
import android.webkit.WebView;
915

16+
import java.util.BitSet;
1017
import java.util.HashMap;
1118
import java.util.Map;
1219

20+
import tech.easily.easybridge.lib.handler.BaseBridgeHandler;
1321
import tech.easily.easybridge.lib.handler.BridgeHandler;
1422

1523
/**
@@ -26,11 +34,15 @@
2634
*/
2735
public class EasyBridgeWebView extends WebView {
2836

37+
private static final String JAVA_SCRIPT_PROTOCOL = "javascript:";
2938
static final String MAPPING_JS_INTERFACE_NAME = "_easybridge";
3039
private static final String DEFAULT_BRIDGE_NAME = "easyBridge";
40+
private static final String REGISTER_INJECT_FINISHED = "rejectFinished";
3141
private final EasyBridge easyBridge;
3242
private String bridgeName = DEFAULT_BRIDGE_NAME;
3343
protected SecurityPolicyChecker policyChecker;
44+
// whether the bridge had been injected to the currentPage
45+
private volatile boolean isInjected;
3446

3547
public EasyBridgeWebView(Context context, String bridgeName) {
3648
this(context, (AttributeSet) null);
@@ -60,6 +72,13 @@ private void initWebView() {
6072
addJavascriptInterface(easyBridge, MAPPING_JS_INTERFACE_NAME);
6173
EasyBridgeWebChromeClient webChromeClient = new EasyBridgeWebChromeClient(this);
6274
setWebChromeClient(webChromeClient);
75+
//register a default handler to subscribe the event of bridge injected
76+
registerHandler(new BaseBridgeHandler(REGISTER_INJECT_FINISHED, this) {
77+
@Override
78+
public void onCall(String parameters, ResultCallBack callBack) {
79+
setInjected(true);
80+
}
81+
});
6382
}
6483

6584
public void registerHandler(BridgeHandler handler) {
@@ -98,6 +117,14 @@ public String getBridgeName() {
98117
return bridgeName;
99118
}
100119

120+
public boolean isInjected() {
121+
return isInjected;
122+
}
123+
124+
void setInjected(boolean injected) {
125+
isInjected = injected;
126+
}
127+
101128
public boolean checkSecurityGlobally(String url, String parameters) {
102129
return policyChecker == null || policyChecker.check(url, parameters);
103130
}
@@ -112,9 +139,28 @@ public void setPolicyChecker(SecurityPolicyChecker policyChecker) {
112139
this.policyChecker = policyChecker;
113140
}
114141

142+
public void evaluateJavascript(String script) {
143+
evaluateJavascript(script, new ValueCallback<String>() {
144+
@Override
145+
public void onReceiveValue(String value) {
146+
147+
}
148+
});
149+
}
150+
151+
@Override
152+
public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
153+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
154+
super.evaluateJavascript(script, resultCallback);
155+
} else {
156+
loadUrl(String.format("%s%s", JAVA_SCRIPT_PROTOCOL, script));
157+
}
158+
}
159+
115160
@Override
116161
protected void onDetachedFromWindow() {
117162
super.onDetachedFromWindow();
163+
policyChecker = null;
118164
if (easyBridge != null) {
119165
easyBridge.destroy();
120166
}

0 commit comments

Comments
 (0)