Skip to content

Commit

Permalink
[TIMOB-25941] Android: Optimized HTTPClient download performance (#10274
Browse files Browse the repository at this point in the history
)

- Increased network response buffer size from 4kb to 8kb.
- Removed "ondatastream" event's undocumented "blob" property.
  * Improves performance by avoiding an extra buffer copy.
  * Note that iOS does not support this undocumented API.
- Modified "responseText" property to only create a string when accessed.
  * Major performance improvement. No longer transcodes per response buffer.
  * Fixed "responseText" to provide total text downloaded like iOS instead of only the buffered segment's text.
  • Loading branch information
jquick-axway authored and hansemannn committed Aug 24, 2018
1 parent 05da959 commit 007bde5
Showing 1 changed file with 81 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
Expand All @@ -32,7 +33,6 @@
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -221,6 +221,7 @@ private void handleResponse(HttpURLConnection connection) throws IOException
charset = "UTF-8";
}
responseData = null;
responseText = null;

int status = connection.getResponseCode();
// Stream holding the server's response
Expand All @@ -245,7 +246,7 @@ private void handleResponse(HttpURLConnection connection) throws IOException
Log.d(TAG, "Content length: " + contentLength, Log.DEBUG_MODE);
int count = 0;
long totalSize = 0;
byte[] buf = new byte[4096];
byte[] buf = new byte[8192];
Log.d(TAG, "Available: " + is.available(), Log.DEBUG_MODE);

while ((count = is.read(buf)) != -1) {
Expand All @@ -254,7 +255,6 @@ private void handleResponse(HttpURLConnection connection) throws IOException
}
totalSize += count;
try {
responseText = new String(Arrays.copyOfRange(buf, 0, count));
handleEntityData(buf, count, totalSize, contentLength);
} catch (IOException e) {
Log.e(TAG, "Error handling entity data", e);
Expand Down Expand Up @@ -350,15 +350,10 @@ private void handleEntityData(byte[] data, int size, long totalSize, long conten
KrollDict callbackData = new KrollDict();
callbackData.put("totalCount", contentLength);
callbackData.put("totalSize", totalSize);
callbackData.put("size", size);
callbackData.put(TiC.PROPERTY_SIZE, size);

byte[] blobData = new byte[size];
System.arraycopy(data, 0, blobData, 0, size);

TiBlob blob = TiBlob.blobFromData(blobData, contentType);
callbackData.put("blob", blob);
double progress = ((double) totalSize) / ((double) contentLength);
// return progress as -1 if it is outside the valid range
double progress = ((double) totalSize) / ((double) contentLength);
if (progress > 1 || progress < 0) {
progress = NetworkModule.PROGRESS_UNKNOWN;
}
Expand All @@ -369,9 +364,19 @@ private void handleEntityData(byte[] data, int size, long totalSize, long conten

private void finishedReceivingEntityData(long contentLength) throws IOException
{
if (responseOut == null) {
return;
}

responseOut.flush();
if (responseOut instanceof ByteArrayOutputStream) {
ByteArrayOutputStream byteStream = (ByteArrayOutputStream) responseOut;
responseData = TiBlob.blobFromData(byteStream.toByteArray(), contentType);
} else if (responseOut instanceof FileOutputStream) {
FileDescriptor fileDescriptor = ((FileOutputStream) responseOut).getFD();
if (fileDescriptor != null) {
fileDescriptor.sync();
}
}
responseOut.close();
responseOut = null;
Expand Down Expand Up @@ -420,7 +425,6 @@ public TiHTTPClient(HTTPClientProxy proxy)
httpClientThreadCounter = new AtomicInteger();
}
readyState = 0;
responseText = "";
connected = false;
this.nvPairs = new ArrayList<NameValuePair>();
this.parts = new HashMap<String, ContentBody>();
Expand Down Expand Up @@ -488,8 +492,18 @@ private String decodeResponseData(String charsetName)
return null;
}

byte[] byteBuffer = null;
if (responseData != null) {
byteBuffer = responseData.getBytes();
} else if (responseOut instanceof ByteArrayOutputStream) {
byteBuffer = ((ByteArrayOutputStream) responseOut).toByteArray();
}
if (byteBuffer == null) {
return null;
}

CharsetDecoder decoder = charset.newDecoder();
ByteBuffer in = ByteBuffer.wrap(responseData.getBytes());
ByteBuffer in = ByteBuffer.wrap(byteBuffer);

try {
CharBuffer decodedText = decoder.decode(in);
Expand Down Expand Up @@ -528,58 +542,86 @@ private String detectResponseDataEncoding()
return null;
}

CharSequence responseSequence = responseData.toString();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(responseSequence);
if (matcher.find()) {
return matcher.group(1);
CharSequence responseSequence = null;
if (responseData != null) {
responseSequence = responseData.toString();
} else if (responseOut instanceof ByteArrayOutputStream) {
responseSequence = responseOut.toString();
}
if (responseSequence != null) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(responseSequence);
if (matcher.find()) {
return matcher.group(1);
}
}

return null;
}

public String getResponseText()
{
if (responseText != null || responseData == null) {
return responseText;
// If we've already transcoded the response text below, then return it now.
if (this.responseText != null) {
return this.responseText;
}

// First try decoding the response data using the charset
// specified in the response content-type header.
if (charset != null) {
responseText = decodeResponseData(charset);
if (responseText != null) {
return responseText;
}
String text = null;
if (this.charset != null) {
text = decodeResponseData(this.charset);
}

// If the first attempt to decode fails try detecting the correct
// charset by scanning the response data.
String detectedCharset = detectResponseDataEncoding();
if (detectedCharset != null) {
Log.d(TAG, "detected charset: " + detectedCharset, Log.DEBUG_MODE);
responseText = decodeResponseData(detectedCharset);
if (responseText != null) {
charset = detectedCharset;
return responseText;
if (text == null) {
String detectedCharset = detectResponseDataEncoding();
if (detectedCharset != null) {
Log.d(TAG, "detected charset: " + detectedCharset, Log.DEBUG_MODE);
text = decodeResponseData(detectedCharset);
if (text != null) {
this.charset = detectedCharset;
}
}
}

// As a last resort try our fallback charsets to decode the data.
for (String charset : FALLBACK_CHARSETS) {
responseText = decodeResponseData(charset);
if (responseText != null) {
return responseText;
if (text == null) {
for (String charset : FALLBACK_CHARSETS) {
text = decodeResponseData(charset);
if (text != null) {
this.charset = charset;
break;
}
}
}

Log.e(TAG, "Could not decode response text.");
return responseText;
// Log an error if we've failed to transcode the response above.
if (text == null) {
Log.e(TAG, "Could not decode response text.");
text = "";
}

// Store the response text for quick access later, but only if we've finished receiving the response.
if (this.responseOut == null) {
this.responseText = text;
}

// Return the transcoded response text.
return text;
}

public TiBlob getResponseData()
{
return responseData;
TiBlob blob = this.responseData;
if (blob == null) {
if (this.responseOut instanceof ByteArrayOutputStream) {
blob = TiBlob.blobFromData(((ByteArrayOutputStream) this.responseOut).toByteArray());
} else if (this.responseText != null) {
blob = TiBlob.blobFromString(this.responseText);
}
}
return blob;
}

public DocumentProxy getResponseXML()
Expand Down

0 comments on commit 007bde5

Please sign in to comment.