Skip to content

Commit 795f596

Browse files
Break the cyclic dependency in android network (#1212)
The HttpTask class is internal class, and it should not keep any references to outer class Resolves: OLPSUP-14277 Signed-off-by: Mykhailo Kuchma <[email protected]>
1 parent b06414e commit 795f596

File tree

1 file changed

+88
-82
lines changed

1 file changed

+88
-82
lines changed

olp-cpp-sdk-core/src/http/android/HttpClient.java

Lines changed: 88 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package com.here.olp.network;
2121

22-
import android.annotation.SuppressLint;
2322
import android.os.AsyncTask;
2423
import android.os.OperationCanceledException;
2524
import android.util.Log;
@@ -28,20 +27,21 @@
2827
import java.io.FileNotFoundException;
2928
import java.io.IOException;
3029
import java.io.InputStream;
30+
import java.lang.ref.WeakReference;
3131
import java.net.HttpURLConnection;
3232
import java.net.InetSocketAddress;
3333
import java.net.MalformedURLException;
3434
import java.net.ProtocolException;
3535
import java.net.Proxy;
36+
import java.net.SocketTimeoutException;
3637
import java.net.URL;
3738
import java.net.URLConnection;
3839
import java.net.UnknownHostException;
39-
import java.net.SocketTimeoutException;
40-
import java.util.concurrent.atomic.AtomicBoolean;
41-
import java.util.concurrent.ExecutorService;
42-
import java.util.concurrent.Executors;
4340
import java.util.List;
4441
import java.util.Map;
42+
import java.util.concurrent.ExecutorService;
43+
import java.util.concurrent.Executors;
44+
import java.util.concurrent.atomic.AtomicBoolean;
4545

4646
import javax.net.ssl.SSLException;
4747

@@ -94,17 +94,17 @@ public static HttpVerb toHttpVerb(int verb) {
9494
/** Class to hold the request's data, which will be used by HttpUrlConnection object. */
9595
public final class Request {
9696
public Request(
97-
String url,
98-
HttpVerb verb,
99-
long requestId,
100-
int connectionTimeout,
101-
int requestTimeout,
102-
String[] headers,
103-
byte[] postData,
104-
String proxyServer,
105-
int proxyPort,
106-
int proxyType,
107-
int maxRetries) {
97+
String url,
98+
HttpVerb verb,
99+
long requestId,
100+
int connectionTimeout,
101+
int requestTimeout,
102+
String[] headers,
103+
byte[] postData,
104+
String proxyServer,
105+
int proxyPort,
106+
int proxyType,
107+
int maxRetries) {
108108
this.url = url;
109109
this.verb = verb;
110110
this.requestId = requestId;
@@ -129,18 +129,10 @@ public Request(
129129
case 4:
130130
case 5:
131131
this.proxyType = Proxy.Type.SOCKS;
132-
Log.w(
133-
LOGTAG,
134-
"HttpClient::Request(): Unsupported proxy version ("
135-
+ proxyType
136-
+ "). Falling back to SOCKS4(4)");
132+
Log.w(LOGTAG, "HttpClient::Request(): Unsupported proxy version (" + proxyType + "). Falling back to SOCKS4(4)");
137133
break;
138134
default:
139-
Log.w(
140-
LOGTAG,
141-
"HttpClient::Request(): Unsupported proxy version ("
142-
+ proxyType
143-
+ "). Falling back to HTTP(0)");
135+
Log.w(LOGTAG, "HttpClient::Request(): Unsupported proxy version (" + proxyType + "). Falling back to HTTP(0)");
144136
this.proxyType = Proxy.Type.HTTP;
145137
break;
146138
}
@@ -215,14 +207,18 @@ public final int maxRetries() {
215207
/**
216208
* Task class sends the request HttpUrlConnection and responsible for handling response as well.
217209
*/
218-
@SuppressLint("StaticFieldLeak")
219-
private class HttpTask extends AsyncTask<Request, Void, Void> {
220-
private AtomicBoolean cancelled = new AtomicBoolean(false);
210+
private static class HttpTask extends AsyncTask<Request, Void, Void> {
211+
private final WeakReference<HttpClient> weakReference;
212+
private final AtomicBoolean cancelled = new AtomicBoolean(false);
221213

222214
public synchronized void cancelTask() {
223215
this.cancelled.set(true);
224216
}
225217

218+
private HttpTask(HttpClient client) {
219+
weakReference = new WeakReference<>(client);
220+
}
221+
226222
@Override
227223
protected Void doInBackground(Request... requests) {
228224
for (Request request : requests) {
@@ -234,9 +230,14 @@ protected Void doInBackground(Request... requests) {
234230
try {
235231
int retryCount = 0;
236232
boolean isDone = false;
237-
URL url = new URL(request.url());
233+
final URL url = new URL(request.url());
238234

239235
do {
236+
final HttpClient httpClient = weakReference.get();
237+
if (httpClient == null) {
238+
return null;
239+
}
240+
240241
// TODO: replace with more elegant and controlled approach
241242
if (retryCount > 0) {
242243
Thread.sleep(100);
@@ -250,10 +251,10 @@ protected Void doInBackground(Request... requests) {
250251
conn = url.openConnection(Proxy.NO_PROXY);
251252
} else {
252253
conn =
253-
url.openConnection(
254-
new Proxy(
255-
request.proxyType(),
256-
new InetSocketAddress(request.proxyServer(), request.proxyPort())));
254+
url.openConnection(
255+
new Proxy(
256+
request.proxyType(),
257+
new InetSocketAddress(request.proxyServer(), request.proxyPort())));
257258
}
258259
} else {
259260
conn = url.openConnection();
@@ -349,15 +350,15 @@ protected Void doInBackground(Request... requests) {
349350
error = httpConn.getResponseMessage();
350351

351352
if ((status > 0)
352-
&& ((status < HttpURLConnection.HTTP_OK)
353+
&& ((status < HttpURLConnection.HTTP_OK)
353354
|| (status >= HttpURLConnection.HTTP_SERVER_ERROR))) {
354355
if (retryCount++ < request.maxRetries()) {
355356
continue;
356357
}
357358
}
358359
} catch (SocketTimeoutException | UnknownHostException e) {
359360
if (retryCount++ < request.maxRetries()) {
360-
resetRequest(request.requestId());
361+
httpClient.resetRequest(request.requestId());
361362
continue;
362363
} else {
363364
throw e;
@@ -396,8 +397,8 @@ protected Void doInBackground(Request... requests) {
396397

397398
int contentSize = conn.getContentLength();
398399
if(contentSize > 0){
399-
downloadContentSize += contentSize;
400-
downloadContentSizePresent = true;
400+
downloadContentSize += contentSize;
401+
downloadContentSizePresent = true;
401402
}
402403

403404
// Get all the headers of the response
@@ -414,8 +415,8 @@ protected Void doInBackground(Request... requests) {
414415

415416
checkCancelled();
416417

417-
headersCallback(request.requestId(), headersArray);
418-
dateAndOffsetCallback(request.requestId(), 0l, offset);
418+
httpClient.headersCallback(request.requestId(), headersArray);
419+
httpClient.dateAndOffsetCallback(request.requestId(), 0l, offset);
419420

420421
// Do the input phase
421422
InputStream in = null;
@@ -435,9 +436,9 @@ protected Void doInBackground(Request... requests) {
435436

436437
while ((len = in.read(buffer)) >= 0) {
437438
checkCancelled();
438-
dataCallback(request.requestId(), buffer, len);
439+
httpClient.dataCallback(request.requestId(), buffer, len);
439440
if(!downloadContentSizePresent){
440-
downloadContentSize += len;
441+
downloadContentSize += len;
441442
}
442443
}
443444
}
@@ -449,12 +450,12 @@ protected Void doInBackground(Request... requests) {
449450
}
450451
} catch (ProtocolException e) {
451452
if (status != HttpURLConnection.HTTP_NOT_MODIFIED
452-
&& status != HttpURLConnection.HTTP_NO_CONTENT) {
453+
&& status != HttpURLConnection.HTTP_NO_CONTENT) {
453454
throw e;
454455
}
455456
} catch (SocketTimeoutException e) {
456457
if (retryCount++ < request.maxRetries()) {
457-
resetRequest(request.requestId());
458+
httpClient.resetRequest(request.requestId());
458459
continue;
459460
} else {
460461
throw e;
@@ -466,24 +467,22 @@ protected Void doInBackground(Request... requests) {
466467
// The request is completed, not cancelled or retried
467468
// Notifies the native (C++) side that request was completed
468469
isDone = true;
469-
completeRequest(request.requestId(), status, uploadedContentSize, downloadContentSize, error, contentType);
470+
httpClient.completeRequest(request.requestId(), status, uploadedContentSize, downloadContentSize, error, contentType);
470471
} while (!isDone);
471472
} catch (SSLException e) {
472-
completeRequest(request.requestId(), AUTHORIZATION_ERROR, uploadedContentSize, downloadContentSize, "SSL connection failed.", "");
473+
completeErrorRequest(request.requestId(), AUTHORIZATION_ERROR, uploadedContentSize, downloadContentSize, "SSL connection failed.");
473474
} catch (MalformedURLException e) {
474-
completeRequest(
475-
request.requestId(), INVALID_URL_ERROR, uploadedContentSize, downloadContentSize, "The provided URL is not valid.", "");
475+
completeErrorRequest(request.requestId(), INVALID_URL_ERROR, uploadedContentSize, downloadContentSize, "The provided URL is not valid.");
476476
} catch (OperationCanceledException e) {
477-
completeRequest(request.requestId(), CANCELLED_ERROR, uploadedContentSize, downloadContentSize, "Cancelled", "");
477+
completeErrorRequest(request.requestId(), CANCELLED_ERROR, uploadedContentSize, downloadContentSize, "Cancelled");
478478
} catch (SocketTimeoutException e) {
479-
completeRequest(request.requestId(), TIMEOUT_ERROR, uploadedContentSize, downloadContentSize, "Timed out", "");
480-
} catch (java.net.UnknownHostException e) {
481-
completeRequest(
482-
request.requestId(), OFFLINE_ERROR, uploadedContentSize, downloadContentSize, "The device has no internet connectivity", "");
479+
completeErrorRequest(request.requestId(), TIMEOUT_ERROR, uploadedContentSize, downloadContentSize, "Timed out");
480+
} catch (UnknownHostException e) {
481+
completeErrorRequest(request.requestId(), OFFLINE_ERROR, uploadedContentSize, downloadContentSize, "The device has no internet connectivity");
483482
} catch (Exception e) {
484483
Log.e(LOGTAG, "HttpClient::HttpTask::run exception: " + e);
485484
e.printStackTrace();
486-
completeRequest(request.requestId(), IO_ERROR, uploadedContentSize, downloadContentSize, e.toString(), "");
485+
completeErrorRequest(request.requestId(), IO_ERROR, uploadedContentSize, downloadContentSize, e.toString());
487486
} finally {
488487
uploadedContentSize = 0;
489488
downloadContentSize = 0;
@@ -494,6 +493,13 @@ protected Void doInBackground(Request... requests) {
494493
return null;
495494
}
496495

496+
private void completeErrorRequest(long requestId, int status, int uploadedBytes, int downloadedBytes, String error) {
497+
final HttpClient httpClient = weakReference.get();
498+
if (httpClient != null) {
499+
httpClient.completeRequest(requestId, status, uploadedBytes, downloadedBytes, error, "");
500+
}
501+
}
502+
497503
private final void checkCancelled() {
498504
if (this.cancelled.get()) {
499505
throw new OperationCanceledException();
@@ -571,7 +577,7 @@ private final int calculateHeadersSize(Map<String, List<String>> headers) throws
571577
}
572578
for (String value : values) {
573579
if(value != null) {
574-
size += value.length();
580+
size += value.length();
575581
}
576582
}
577583
}
@@ -597,31 +603,31 @@ public void shutdown() {
597603
}
598604

599605
public HttpTask send(
600-
String url,
601-
int httpMethod,
602-
long requestId,
603-
int connTimeout,
604-
int reqTimeout,
605-
String[] headers,
606-
byte[] postData,
607-
String proxyServer,
608-
int proxyPort,
609-
int proxyType,
610-
int maxRetries) {
611-
Request request =
612-
new Request(
613-
url,
614-
HttpClient.toHttpVerb(httpMethod),
615-
requestId,
616-
connTimeout,
617-
reqTimeout,
618-
headers,
619-
postData,
620-
proxyServer,
621-
proxyPort,
622-
proxyType,
623-
maxRetries);
624-
HttpTask task = new HttpTask();
606+
String url,
607+
int httpMethod,
608+
long requestId,
609+
int connTimeout,
610+
int reqTimeout,
611+
String[] headers,
612+
byte[] postData,
613+
String proxyServer,
614+
int proxyPort,
615+
int proxyType,
616+
int maxRetries) {
617+
final Request request =
618+
new Request(
619+
url,
620+
HttpClient.toHttpVerb(httpMethod),
621+
requestId,
622+
connTimeout,
623+
reqTimeout,
624+
headers,
625+
postData,
626+
proxyServer,
627+
proxyPort,
628+
proxyType,
629+
maxRetries);
630+
final HttpTask task = new HttpTask(this);
625631
task.executeOnExecutor(executor, request);
626632
return task;
627633
}
@@ -630,7 +636,7 @@ public HttpTask send(
630636
// Synchronization is required in order to provide thread-safe access to `nativePtr`
631637
// Callback for completed request
632638
private synchronized native void completeRequest(
633-
long requestId, int status, int uploadedBytes, int downloadedBytes, String error, String contentType);
639+
long requestId, int status, int uploadedBytes, int downloadedBytes, String error, String contentType);
634640
// Callback for data received
635641
private synchronized native void dataCallback(long requestId, byte[] data, int len);
636642
// Callback set date and offset
@@ -639,4 +645,4 @@ private synchronized native void completeRequest(
639645
private synchronized native void headersCallback(long requestId, String[] headers);
640646
// Reset request for retry
641647
private synchronized native void resetRequest(long requestId);
642-
}
648+
}

0 commit comments

Comments
 (0)