Skip to content

Commit 8eaa808

Browse files
authored
Merge pull request #449 from YangSen-qn/develop
UC Query Add SingleFlight & connect check
2 parents cb40f3b + bfd32f0 commit 8eaa808

File tree

14 files changed

+619
-40
lines changed

14 files changed

+619
-40
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#Changelog
2+
## 8.1.2(2021-01-18)
3+
* 区域查询采用SingleFlight模式
4+
* 增加网络链接状态检测
5+
26
## 8.1.1 (2021-01-06)
37
* 优化日志统计
48

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ https://github.com/qiniudemo/qiniu-lab-android
2828
| 7.0.7 | Android 2.2+ | android-async-http 1.4.8 |
2929

3030
### 注意
31-
* 推荐使用最新版:8.1.1
31+
* 推荐使用最新版:8.1.2
3232
* AndroidNetwork.getMobileDbm()可以获取手机信号强度,需要如下权限(API>=18时生效)
3333
```
3434
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.qiniu.android;
2+
3+
import com.qiniu.android.http.connectCheck.ConnectChecker;
4+
import com.qiniu.android.storage.GlobalConfiguration;
5+
6+
public class ConnectCheckTest extends BaseTest {
7+
8+
public void testCheck() {
9+
10+
int maxCount = 100;
11+
int successCount = 0;
12+
for (int i = 0; i < maxCount; i++) {
13+
if (ConnectChecker.check()) {
14+
successCount += 1;
15+
}
16+
}
17+
18+
assertEquals("maxCount:" + maxCount + " successCount:" + successCount, maxCount, successCount);
19+
}
20+
21+
public void testCustomCheckHosts() {
22+
GlobalConfiguration.getInstance().connectCheckURLStrings = new String[]{"https://www.baidu.com"};
23+
int maxCount = 100;
24+
int successCount = 0;
25+
for (int i = 0; i < maxCount; i++) {
26+
if (ConnectChecker.check()) {
27+
successCount += 1;
28+
}
29+
}
30+
31+
assertEquals("maxCount:" + maxCount + " successCount:" + successCount, maxCount, successCount);
32+
}
33+
34+
public void testNotConnected() {
35+
GlobalConfiguration.getInstance().connectCheckURLStrings = new String[]{"https://www.test1.com", "https://www.test2.com"};
36+
int maxCount = 100;
37+
int successCount = 0;
38+
for (int i = 0; i < maxCount; i++) {
39+
if (ConnectChecker.check()) {
40+
successCount += 1;
41+
}
42+
}
43+
44+
assertEquals("maxCount:" + maxCount + " successCount:" + successCount, 0, successCount);
45+
}
46+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.qiniu.android;
2+
3+
import com.qiniu.android.utils.LogUtil;
4+
import com.qiniu.android.utils.SingleFlight;
5+
6+
public class SingleFlightTest extends BaseTest {
7+
8+
private static final int RetryCount = 5;
9+
10+
public void testSync() {
11+
final TestStatus testStatus = new TestStatus();
12+
testStatus.maxCount = 1000;
13+
testStatus.completeCount = 0;
14+
15+
SingleFlight singleFlight = new SingleFlight();
16+
for (int i = 0; i < testStatus.maxCount; i++) {
17+
singleFlightPerform(singleFlight, i, RetryCount, false, new CompleteHandler() {
18+
@Override
19+
public void complete() throws Exception {
20+
testStatus.completeCount += 1;
21+
LogUtil.d("== sync completeCount:" + testStatus.completeCount);
22+
}
23+
});
24+
}
25+
26+
wait(new WaitConditional() {
27+
@Override
28+
public boolean shouldWait() {
29+
return testStatus.maxCount != testStatus.completeCount;
30+
}
31+
}, 60);
32+
}
33+
34+
public void testSyncRetry() {
35+
final TestStatus testStatus = new TestStatus();
36+
testStatus.maxCount = 1000;
37+
testStatus.completeCount = 0;
38+
39+
SingleFlight singleFlight = new SingleFlight();
40+
for (int i = 0; i < testStatus.maxCount; i++) {
41+
singleFlightPerform(singleFlight, i, 0, false, new CompleteHandler() {
42+
@Override
43+
public void complete() throws Exception {
44+
testStatus.completeCount += 1;
45+
LogUtil.d("== sync completeCount:" + testStatus.completeCount);
46+
}
47+
});
48+
}
49+
50+
wait(new WaitConditional() {
51+
@Override
52+
public boolean shouldWait() {
53+
return testStatus.maxCount != testStatus.completeCount;
54+
}
55+
}, 60);
56+
}
57+
58+
public void testAsync() {
59+
final TestStatus testStatus = new TestStatus();
60+
testStatus.maxCount = 1000;
61+
testStatus.completeCount = 0;
62+
63+
SingleFlight singleFlight = new SingleFlight();
64+
for (int i = 0; i < testStatus.maxCount; i++) {
65+
singleFlightPerform(singleFlight, i, RetryCount, true, new CompleteHandler() {
66+
@Override
67+
public void complete() throws Exception {
68+
synchronized (testStatus) {
69+
testStatus.completeCount += 1;
70+
}
71+
LogUtil.d("== async complete Count:" + testStatus.completeCount);
72+
}
73+
});
74+
}
75+
76+
wait(new WaitConditional() {
77+
@Override
78+
public boolean shouldWait() {
79+
return testStatus.maxCount != testStatus.completeCount;
80+
}
81+
}, 60);
82+
83+
assertTrue("== async" + "max Count:" + testStatus.maxCount + " complete Count:" + testStatus.completeCount, testStatus.maxCount == testStatus.completeCount);
84+
LogUtil.d("== async" + "max Count:" + testStatus.maxCount + " complete Count:" + testStatus.completeCount);
85+
}
86+
87+
public void testAsyncRetry() {
88+
final TestStatus testStatus = new TestStatus();
89+
testStatus.maxCount = 1000;
90+
testStatus.completeCount = 0;
91+
92+
SingleFlight singleFlight = new SingleFlight();
93+
for (int i = 0; i < testStatus.maxCount; i++) {
94+
singleFlightPerform(singleFlight, i, 0, true, new CompleteHandler() {
95+
@Override
96+
public void complete() throws Exception {
97+
synchronized (testStatus) {
98+
testStatus.completeCount += 1;
99+
}
100+
LogUtil.d("== async completeCount:" + testStatus.completeCount);
101+
}
102+
});
103+
}
104+
105+
wait(new WaitConditional() {
106+
@Override
107+
public boolean shouldWait() {
108+
return testStatus.maxCount != testStatus.completeCount;
109+
}
110+
}, 60);
111+
112+
LogUtil.d("== async completeCount:" + testStatus.completeCount + " end");
113+
}
114+
115+
private void singleFlightPerform(final SingleFlight singleFlight,
116+
final int index,
117+
final int retryCount,
118+
final boolean isAsync,
119+
final CompleteHandler completeHandler) {
120+
121+
try {
122+
singleFlight.perform("key", new SingleFlight.ActionHandler() {
123+
@Override
124+
public void action(final SingleFlight.CompleteHandler singleFlightCompleteHandler) throws Exception {
125+
126+
final CompleteHandler completeHandlerP = new CompleteHandler() {
127+
@Override
128+
public void complete() throws Exception {
129+
if (retryCount < RetryCount) {
130+
LogUtil.d("== " + (isAsync ? "async" : "sync") + " action retryCount:" + retryCount + " index:" + index + " error");
131+
throw new Exception("== 123 ==");
132+
} else {
133+
LogUtil.d("== " + (isAsync ? "async" : "sync") + " action retryCount:" + retryCount + " index:" + index + " value");
134+
singleFlightCompleteHandler.complete(index + "");
135+
}
136+
}
137+
};
138+
139+
if (isAsync) {
140+
new Thread(new Runnable() {
141+
@Override
142+
public void run() {
143+
try {
144+
completeHandlerP.complete();
145+
} catch (Exception e) {
146+
singleFlightCompleteHandler.complete(null);
147+
}
148+
}
149+
}).start();
150+
} else {
151+
completeHandlerP.complete();
152+
}
153+
}
154+
}, new SingleFlight.CompleteHandler() {
155+
@Override
156+
public void complete(Object value) {
157+
if (retryCount < RetryCount) {
158+
singleFlightPerform(singleFlight, index, retryCount + 1, isAsync, completeHandler);
159+
} else {
160+
LogUtil.d("== " + (isAsync ? "async" : "sync") + " action complete retryCount:" + retryCount + " value:" + value + " index:" + index);
161+
if (!isAsync) {
162+
assertTrue("index:" + index + "value error",(value + "").equals(index + ""));
163+
}
164+
try {
165+
completeHandler.complete();
166+
} catch (Exception e) {
167+
}
168+
}
169+
}
170+
});
171+
} catch (Exception e) {
172+
singleFlightPerform(singleFlight, index, retryCount + 1, isAsync, completeHandler);
173+
}
174+
}
175+
176+
177+
178+
private interface CompleteHandler {
179+
void complete() throws Exception;
180+
}
181+
182+
183+
protected static class TestStatus {
184+
int maxCount;
185+
int completeCount;
186+
}
187+
188+
}

library/src/main/java/com/qiniu/android/common/AutoZone.java

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.qiniu.android.http.request.RequestTransaction;
55
import com.qiniu.android.http.metrics.UploadRegionRequestMetrics;
66
import com.qiniu.android.storage.UpToken;
7+
import com.qiniu.android.utils.SingleFlight;
78

89
import org.json.JSONObject;
910

@@ -23,6 +24,8 @@ public final class AutoZone extends Zone {
2324
private Map<String, ZonesInfo> zonesInfoMap = new ConcurrentHashMap<>();
2425
private ArrayList<RequestTransaction> transactions = new ArrayList<>();
2526

27+
private static final SingleFlight SingleFlight = new SingleFlight();
28+
2629
//私有云可能改变ucServer
2730
public void setUcServer(String ucServer) {
2831
this.ucServer = ucServer;
@@ -72,27 +75,56 @@ public void preQuery(final UpToken token, final QueryHandler completeHandler) {
7275
return;
7376
}
7477

75-
final RequestTransaction transaction = createUploadRequestTransaction(token);
76-
transaction.queryUploadHosts(true, new RequestTransaction.RequestCompleteHandler() {
77-
@Override
78-
public void complete(ResponseInfo responseInfo, UploadRegionRequestMetrics requestMetrics, JSONObject response) {
79-
if (responseInfo != null && responseInfo.isOK() && response != null) {
80-
ZonesInfo zonesInfoP = ZonesInfo.createZonesInfo(response);
81-
zonesInfoMap.put(cacheKey, zonesInfoP);
82-
GlobalCache.getInstance().cache(response, cacheKey);
83-
completeHandler.complete(0, responseInfo, requestMetrics);
84-
} else {
85-
if (responseInfo.isNetworkBroken()) {
86-
completeHandler.complete(ResponseInfo.NetworkError, responseInfo, requestMetrics);
87-
} else {
88-
ZonesInfo zonesInfoP = FixedZone.localsZoneInfo().getZonesInfo(token);
78+
79+
try {
80+
SingleFlight.perform(cacheKey, new SingleFlight.ActionHandler() {
81+
@Override
82+
public void action(final com.qiniu.android.utils.SingleFlight.CompleteHandler completeHandler) throws Exception {
83+
84+
final RequestTransaction transaction = createUploadRequestTransaction(token);
85+
transaction.queryUploadHosts(true, new RequestTransaction.RequestCompleteHandler() {
86+
@Override
87+
public void complete(ResponseInfo responseInfo, UploadRegionRequestMetrics requestMetrics, JSONObject response) {
88+
destroyUploadRequestTransaction(transaction);
89+
90+
SingleFlightValue value = new SingleFlightValue();
91+
value.responseInfo = responseInfo;
92+
value.response = response;
93+
value.metrics = requestMetrics;
94+
completeHandler.complete(value);
95+
}
96+
});
97+
}
98+
99+
}, new SingleFlight.CompleteHandler() {
100+
@Override
101+
public void complete(Object value) {
102+
SingleFlightValue singleFlightValue = (SingleFlightValue)value;
103+
ResponseInfo responseInfo = singleFlightValue.responseInfo;
104+
UploadRegionRequestMetrics requestMetrics = singleFlightValue.metrics;
105+
JSONObject response = singleFlightValue.response;
106+
107+
if (responseInfo != null && responseInfo.isOK() && response != null) {
108+
ZonesInfo zonesInfoP = ZonesInfo.createZonesInfo(response);
89109
zonesInfoMap.put(cacheKey, zonesInfoP);
110+
GlobalCache.getInstance().cache(response, cacheKey);
90111
completeHandler.complete(0, responseInfo, requestMetrics);
112+
} else {
113+
if (responseInfo.isNetworkBroken()) {
114+
completeHandler.complete(ResponseInfo.NetworkError, responseInfo, requestMetrics);
115+
} else {
116+
ZonesInfo zonesInfoP = FixedZone.localsZoneInfo().getZonesInfo(token);
117+
zonesInfoMap.put(cacheKey, zonesInfoP);
118+
completeHandler.complete(0, responseInfo, requestMetrics);
119+
}
91120
}
92121
}
93-
destroyUploadRequestTransaction(transaction);
94-
}
95-
});
122+
});
123+
124+
} catch (Exception e) {
125+
/// 此处永远不会执行,回调只为占位
126+
completeHandler.complete(ResponseInfo.NetworkError, ResponseInfo.localIOError("uc query"), null);
127+
}
96128
}
97129

98130
private RequestTransaction createUploadRequestTransaction(UpToken token) {
@@ -107,6 +139,11 @@ private void destroyUploadRequestTransaction(RequestTransaction transaction) {
107139
transactions.remove(transaction);
108140
}
109141

142+
private static class SingleFlightValue {
143+
private ResponseInfo responseInfo;
144+
private JSONObject response;
145+
private UploadRegionRequestMetrics metrics;
146+
}
110147

111148
private static class GlobalCache {
112149
private static GlobalCache globalCache = new GlobalCache();

library/src/main/java/com/qiniu/android/common/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
public final class Constants {
5-
public static final String VERSION = "8.1.1";
5+
public static final String VERSION = "8.1.2";
66

77
public static final String UTF_8 = "utf-8";
88
}

library/src/main/java/com/qiniu/android/http/ResponseInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public boolean canConnectToHost(){
279279

280280
public boolean isHostUnavailable(){
281281
// 基本不可恢复,注:会影响下次请求,范围太大可能会造成大量的timeout
282-
if (isTlsError() || statusCode == 502 || statusCode == 503 || statusCode == 504 || statusCode == 599) {
282+
if (statusCode == 502 || statusCode == 503 || statusCode == 504 || statusCode == 599) {
283283
return true;
284284
} else {
285285
return false;

0 commit comments

Comments
 (0)