18
18
19
19
package com .loopj .android .http ;
20
20
21
+ import android .text .TextUtils ;
21
22
import android .util .Log ;
22
23
23
24
import org .apache .http .Header ;
@@ -51,18 +52,12 @@ public class JsonStreamerEntity implements HttpEntity {
51
52
// Buffer used for reading from input streams.
52
53
private final byte [] buffer = new byte [BUFFER_SIZE ];
53
54
54
- // Reusable StringBuilder used by escape() method.
55
- // Its size is just initial, if more space is needed, the system will
56
- // automatically enlarge the buffer.
57
- private static final StringBuilder BUILDER = new StringBuilder (128 );
58
-
59
55
private static final byte [] JSON_TRUE = "true" .getBytes ();
60
56
private static final byte [] JSON_FALSE = "false" .getBytes ();
61
57
private static final byte [] JSON_NULL = "null" .getBytes ();
62
58
private static final byte [] STREAM_NAME = escape ("name" );
63
59
private static final byte [] STREAM_TYPE = escape ("type" );
64
60
private static final byte [] STREAM_CONTENTS = escape ("contents" );
65
- private static final byte [] STREAM_ELAPSED = escape ("_elapsed" );
66
61
67
62
private static final Header HEADER_JSON_CONTENT =
68
63
new BasicHeader (
@@ -80,11 +75,16 @@ public class JsonStreamerEntity implements HttpEntity {
80
75
// Whether to use gzip compression while uploading
81
76
private final Header contentEncoding ;
82
77
78
+ private final byte [] elapsedField ;
79
+
83
80
private final ResponseHandlerInterface progressHandler ;
84
81
85
- public JsonStreamerEntity (ResponseHandlerInterface progressHandler , boolean useGZipCompression ) {
82
+ public JsonStreamerEntity (ResponseHandlerInterface progressHandler , boolean useGZipCompression , String elapsedField ) {
86
83
this .progressHandler = progressHandler ;
87
84
this .contentEncoding = useGZipCompression ? HEADER_GZIP_ENCODING : null ;
85
+ this .elapsedField = TextUtils .isEmpty (elapsedField )
86
+ ? null
87
+ : escape (elapsedField );
88
88
}
89
89
90
90
/**
@@ -147,7 +147,7 @@ public void writeTo(final OutputStream out) throws IOException {
147
147
148
148
// Use GZIP compression when sending streams, otherwise just use
149
149
// a buffered output stream to speed things up a bit.
150
- OutputStream os = null != contentEncoding
150
+ OutputStream os = contentEncoding != null
151
151
? new GZIPOutputStream (out , BUFFER_SIZE )
152
152
: out ;
153
153
@@ -157,71 +157,90 @@ public void writeTo(final OutputStream out) throws IOException {
157
157
// Keys used by the HashMaps.
158
158
Set <String > keys = jsonParams .keySet ();
159
159
160
- boolean isFileWrapper ;
161
-
162
- // Go over all keys and handle each's value.
163
- for (String key : keys ) {
164
- // Evaluate the value (which cannot be null).
165
- Object value = jsonParams .get (key );
166
-
167
- // Bail out prematurely if value's null.
168
- if (value == null ) {
169
- continue ;
170
- }
160
+ int keysCount = keys .size ();
161
+ if (0 < keysCount ) {
162
+ int keysProcessed = 0 ;
163
+ boolean isFileWrapper ;
171
164
172
- // Write the JSON object's key.
173
- os .write (escape (key ));
174
- os .write (':' );
165
+ // Go over all keys and handle each's value.
166
+ for (String key : keys ) {
167
+ // Indicate that this key has been processed.
168
+ keysProcessed ++;
175
169
176
- // Check if this is a FileWrapper.
177
- isFileWrapper = value instanceof RequestParams .FileWrapper ;
170
+ try {
171
+ // Evaluate the value (which cannot be null).
172
+ Object value = jsonParams .get (key );
178
173
179
- // If a file should be uploaded.
180
- if (isFileWrapper || value instanceof RequestParams .StreamWrapper ) {
181
- // All uploads are sent as an object containing the file's details.
182
- os .write ('{' );
174
+ // Write the JSON object's key.
175
+ os .write (escape (key ));
176
+ os .write (':' );
183
177
184
- // Determine how to handle this entry.
185
- if (isFileWrapper ) {
186
- writeToFromFile (os , (RequestParams .FileWrapper ) value );
187
- } else {
188
- writeToFromStream (os , (RequestParams .StreamWrapper ) value );
178
+ // Bail out prematurely if value's null.
179
+ if (value == null ) {
180
+ os .write (JSON_NULL );
181
+ } else {
182
+ // Check if this is a FileWrapper.
183
+ isFileWrapper = value instanceof RequestParams .FileWrapper ;
184
+
185
+ // If a file should be uploaded.
186
+ if (isFileWrapper || value instanceof RequestParams .StreamWrapper ) {
187
+ // All uploads are sent as an object containing the file's details.
188
+ os .write ('{' );
189
+
190
+ // Determine how to handle this entry.
191
+ if (isFileWrapper ) {
192
+ writeToFromFile (os , (RequestParams .FileWrapper ) value );
193
+ } else {
194
+ writeToFromStream (os , (RequestParams .StreamWrapper ) value );
195
+ }
196
+
197
+ // End the file's object and prepare for next one.
198
+ os .write ('}' );
199
+ } else if (value instanceof JsonValueInterface ) {
200
+ os .write (((JsonValueInterface ) value ).getEscapedJsonValue ());
201
+ } else if (value instanceof org .json .JSONObject ) {
202
+ os .write (((org .json .JSONObject ) value ).toString ().getBytes ());
203
+ } else if (value instanceof org .json .JSONArray ) {
204
+ os .write (((org .json .JSONArray ) value ).toString ().getBytes ());
205
+ } else if (value instanceof Boolean ) {
206
+ os .write ((Boolean ) value ? JSON_TRUE : JSON_FALSE );
207
+ } else if (value instanceof Long ) {
208
+ os .write ((((Number ) value ).longValue () + "" ).getBytes ());
209
+ } else if (value instanceof Double ) {
210
+ os .write ((((Number ) value ).doubleValue () + "" ).getBytes ());
211
+ } else if (value instanceof Float ) {
212
+ os .write ((((Number ) value ).floatValue () + "" ).getBytes ());
213
+ } else if (value instanceof Integer ) {
214
+ os .write ((((Number ) value ).intValue () + "" ).getBytes ());
215
+ } else {
216
+ os .write (escape (value .toString ()));
217
+ }
218
+ }
219
+ } finally {
220
+ // Separate each K:V with a comma, except the last one.
221
+ if (elapsedField != null || keysProcessed < keysCount ) {
222
+ os .write (',' );
223
+ }
189
224
}
225
+ }
190
226
191
- // End the file's object and prepare for next one.
192
- os .write ('}' );
193
- } else if (value instanceof JsonValueInterface ) {
194
- os .write (((JsonValueInterface ) value ).getEscapedJsonValue ());
195
- } else if (value instanceof org .json .JSONObject ) {
196
- os .write (((org .json .JSONObject ) value ).toString ().getBytes ());
197
- } else if (value instanceof org .json .JSONArray ) {
198
- os .write (((org .json .JSONArray ) value ).toString ().getBytes ());
199
- } else if (value instanceof Boolean ) {
200
- os .write ((Boolean ) value ? JSON_TRUE : JSON_FALSE );
201
- } else if (value instanceof Long ) {
202
- os .write ((((Number ) value ).longValue () + "" ).getBytes ());
203
- } else if (value instanceof Double ) {
204
- os .write ((((Number ) value ).doubleValue () + "" ).getBytes ());
205
- } else if (value instanceof Float ) {
206
- os .write ((((Number ) value ).floatValue () + "" ).getBytes ());
207
- } else if (value instanceof Integer ) {
208
- os .write ((((Number ) value ).intValue () + "" ).getBytes ());
209
- } else {
210
- os .write (escape (value .toString ()));
227
+ // Calculate how many milliseconds it took to upload the contents.
228
+ long elapsedTime = System .currentTimeMillis () - now ;
229
+
230
+ // Include the elapsed time taken to upload everything.
231
+ // This might be useful for somebody, but it serves us well since
232
+ // there will almost always be a ',' as the last sent character.
233
+ if (elapsedField != null ) {
234
+ os .write (elapsedField );
235
+ os .write (':' );
236
+ os .write ((elapsedTime + "" ).getBytes ());
211
237
}
212
238
213
- os . write ( ',' );
239
+ Log . i ( LOG_TAG , "Uploaded JSON in " + Math . floor ( elapsedTime / 1000 ) + " seconds" );
214
240
}
215
241
216
- // Include the elapsed time taken to upload everything.
217
- // This might be useful for somebody, but it serves us well since
218
- // there will almost always be a ',' as the last sent character.
219
- os .write (STREAM_ELAPSED );
220
- os .write (':' );
221
- long elapsedTime = System .currentTimeMillis () - now ;
222
- os .write ((elapsedTime + "}" ).getBytes ());
223
-
224
- Log .i (LOG_TAG , "Uploaded JSON in " + Math .floor (elapsedTime / 1000 ) + " seconds" );
242
+ // Close the JSON object.
243
+ os .write ('}' );
225
244
226
245
// Flush the contents up the stream.
227
246
os .flush ();
@@ -321,60 +340,57 @@ static byte[] escape(String string) {
321
340
return JSON_NULL ;
322
341
}
323
342
343
+ // Create a string builder to generate the escaped string.
344
+ StringBuilder sb = new StringBuilder (128 );
345
+
324
346
// Surround with quotations.
325
- BUILDER .append ('"' );
347
+ sb .append ('"' );
326
348
327
349
int length = string .length (), pos = -1 ;
328
350
while (++pos < length ) {
329
351
char ch = string .charAt (pos );
330
352
switch (ch ) {
331
353
case '"' :
332
- BUILDER .append ("\\ \" " );
354
+ sb .append ("\\ \" " );
333
355
break ;
334
356
case '\\' :
335
- BUILDER .append ("\\ \\ " );
357
+ sb .append ("\\ \\ " );
336
358
break ;
337
359
case '\b' :
338
- BUILDER .append ("\\ b" );
360
+ sb .append ("\\ b" );
339
361
break ;
340
362
case '\f' :
341
- BUILDER .append ("\\ f" );
363
+ sb .append ("\\ f" );
342
364
break ;
343
365
case '\n' :
344
- BUILDER .append ("\\ n" );
366
+ sb .append ("\\ n" );
345
367
break ;
346
368
case '\r' :
347
- BUILDER .append ("\\ r" );
369
+ sb .append ("\\ r" );
348
370
break ;
349
371
case '\t' :
350
- BUILDER .append ("\\ t" );
372
+ sb .append ("\\ t" );
351
373
break ;
352
374
default :
353
375
// Reference: http://www.unicode.org/versions/Unicode5.1.0/
354
376
if ((ch >= '\u0000' && ch <= '\u001F' ) || (ch >= '\u007F' && ch <= '\u009F' ) || (ch >= '\u2000' && ch <= '\u20FF' )) {
355
377
String intString = Integer .toHexString (ch );
356
- BUILDER .append ("\\ u" );
378
+ sb .append ("\\ u" );
357
379
int intLength = 4 - intString .length ();
358
380
for (int zero = 0 ; zero < intLength ; zero ++) {
359
- BUILDER .append ('0' );
381
+ sb .append ('0' );
360
382
}
361
- BUILDER .append (intString .toUpperCase (Locale .US ));
383
+ sb .append (intString .toUpperCase (Locale .US ));
362
384
} else {
363
- BUILDER .append (ch );
385
+ sb .append (ch );
364
386
}
365
387
break ;
366
388
}
367
389
}
368
390
369
391
// Surround with quotations.
370
- BUILDER .append ('"' );
371
-
372
- try {
373
- return BUILDER .toString ().getBytes ();
374
- } finally {
375
- // Empty the String buffer.
376
- // This is 20-30% faster than instantiating a new object.
377
- BUILDER .setLength (0 );
378
- }
392
+ sb .append ('"' );
393
+
394
+ return sb .toString ().getBytes ();
379
395
}
380
396
}
0 commit comments