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,11 +52,6 @@ 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 ();
@@ -80,11 +76,14 @@ public class JsonStreamerEntity implements HttpEntity {
80
76
// Whether to use gzip compression while uploading
81
77
private final Header contentEncoding ;
82
78
79
+ private final String elapsedField ;
80
+
83
81
private final ResponseHandlerInterface progressHandler ;
84
82
85
- public JsonStreamerEntity (ResponseHandlerInterface progressHandler , boolean useGZipCompression ) {
83
+ public JsonStreamerEntity (ResponseHandlerInterface progressHandler , boolean useGZipCompression , String elapsedField ) {
86
84
this .progressHandler = progressHandler ;
87
85
this .contentEncoding = useGZipCompression ? HEADER_GZIP_ENCODING : null ;
86
+ this .elapsedField = elapsedField ;
88
87
}
89
88
90
89
/**
@@ -157,69 +156,86 @@ public void writeTo(final OutputStream out) throws IOException {
157
156
// Keys used by the HashMaps.
158
157
Set <String > keys = jsonParams .keySet ();
159
158
159
+ int keysCount = keys .size ();
160
+ int keysProcessed = -1 ;
160
161
boolean isFileWrapper ;
161
162
162
163
// Go over all keys and handle each's value.
163
164
for (String key : keys ) {
164
- // Evaluate the value (which cannot be null) .
165
- Object value = jsonParams . get ( key ) ;
165
+ // Indicate that this key has been processed .
166
+ keysProcessed ++ ;
166
167
167
- // Bail out prematurely if value's null.
168
- if (value == null ) {
169
- continue ;
170
- }
168
+ try {
169
+ // Evaluate the value (which cannot be null).
170
+ Object value = jsonParams .get (key );
171
171
172
- // Write the JSON object's key.
173
- os .write (escape (key ));
174
- os .write (':' );
172
+ // Bail out prematurely if value's null.
173
+ if (value == null ) {
174
+ continue ;
175
+ }
176
+
177
+ // Write the JSON object's key.
178
+ os .write (escape (key ));
179
+ os .write (':' );
175
180
176
- // Check if this is a FileWrapper.
177
- isFileWrapper = value instanceof RequestParams .FileWrapper ;
181
+ // Check if this is a FileWrapper.
182
+ isFileWrapper = value instanceof RequestParams .FileWrapper ;
178
183
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 ('{' );
184
+ // If a file should be uploaded.
185
+ if (isFileWrapper || value instanceof RequestParams .StreamWrapper ) {
186
+ // All uploads are sent as an object containing the file's details.
187
+ os .write ('{' );
183
188
184
- // Determine how to handle this entry.
185
- if (isFileWrapper ) {
186
- writeToFromFile (os , (RequestParams .FileWrapper ) value );
189
+ // Determine how to handle this entry.
190
+ if (isFileWrapper ) {
191
+ writeToFromFile (os , (RequestParams .FileWrapper ) value );
192
+ } else {
193
+ writeToFromStream (os , (RequestParams .StreamWrapper ) value );
194
+ }
195
+
196
+ // End the file's object and prepare for next one.
197
+ os .write ('}' );
198
+ } else if (value instanceof JsonValueInterface ) {
199
+ os .write (((JsonValueInterface ) value ).getEscapedJsonValue ());
200
+ } else if (value instanceof org .json .JSONObject ) {
201
+ os .write (((org .json .JSONObject ) value ).toString ().getBytes ());
202
+ } else if (value instanceof org .json .JSONArray ) {
203
+ os .write (((org .json .JSONArray ) value ).toString ().getBytes ());
204
+ } else if (value instanceof Boolean ) {
205
+ os .write ((Boolean ) value ? JSON_TRUE : JSON_FALSE );
206
+ } else if (value instanceof Long ) {
207
+ os .write ((((Number ) value ).longValue () + "" ).getBytes ());
208
+ } else if (value instanceof Double ) {
209
+ os .write ((((Number ) value ).doubleValue () + "" ).getBytes ());
210
+ } else if (value instanceof Float ) {
211
+ os .write ((((Number ) value ).floatValue () + "" ).getBytes ());
212
+ } else if (value instanceof Integer ) {
213
+ os .write ((((Number ) value ).intValue () + "" ).getBytes ());
187
214
} else {
188
- writeToFromStream (os , (RequestParams .StreamWrapper ) value );
215
+ os .write (escape (value .toString ()));
216
+ }
217
+ } finally {
218
+ // Separate each K:V with a comma, except the last one.
219
+ if (!TextUtils .isEmpty (elapsedField ) || keysProcessed < keysCount ) {
220
+ os .write (',' );
189
221
}
190
-
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 ()));
211
222
}
212
-
213
- os .write (',' );
214
223
}
215
224
225
+ // Calculate how many milliseconds it took to upload the contents.
226
+ long elapsedTime = System .currentTimeMillis () - now ;
227
+
216
228
// Include the elapsed time taken to upload everything.
217
229
// This might be useful for somebody, but it serves us well since
218
230
// 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 ());
231
+ if (!TextUtils .isEmpty (elapsedField )) {
232
+ os .write (STREAM_ELAPSED );
233
+ os .write (':' );
234
+ os .write ((elapsedTime + "" ).getBytes ());
235
+ }
236
+
237
+ // Close the JSON object.
238
+ os .write ('}' );
223
239
224
240
Log .i (LOG_TAG , "Uploaded JSON in " + Math .floor (elapsedTime / 1000 ) + " seconds" );
225
241
@@ -321,60 +337,57 @@ static byte[] escape(String string) {
321
337
return JSON_NULL ;
322
338
}
323
339
340
+ // Create a string builder to generate the escaped string.
341
+ StringBuilder sb = new StringBuilder (128 );
342
+
324
343
// Surround with quotations.
325
- BUILDER .append ('"' );
344
+ sb .append ('"' );
326
345
327
346
int length = string .length (), pos = -1 ;
328
347
while (++pos < length ) {
329
348
char ch = string .charAt (pos );
330
349
switch (ch ) {
331
350
case '"' :
332
- BUILDER .append ("\\ \" " );
351
+ sb .append ("\\ \" " );
333
352
break ;
334
353
case '\\' :
335
- BUILDER .append ("\\ \\ " );
354
+ sb .append ("\\ \\ " );
336
355
break ;
337
356
case '\b' :
338
- BUILDER .append ("\\ b" );
357
+ sb .append ("\\ b" );
339
358
break ;
340
359
case '\f' :
341
- BUILDER .append ("\\ f" );
360
+ sb .append ("\\ f" );
342
361
break ;
343
362
case '\n' :
344
- BUILDER .append ("\\ n" );
363
+ sb .append ("\\ n" );
345
364
break ;
346
365
case '\r' :
347
- BUILDER .append ("\\ r" );
366
+ sb .append ("\\ r" );
348
367
break ;
349
368
case '\t' :
350
- BUILDER .append ("\\ t" );
369
+ sb .append ("\\ t" );
351
370
break ;
352
371
default :
353
372
// Reference: http://www.unicode.org/versions/Unicode5.1.0/
354
373
if ((ch >= '\u0000' && ch <= '\u001F' ) || (ch >= '\u007F' && ch <= '\u009F' ) || (ch >= '\u2000' && ch <= '\u20FF' )) {
355
374
String intString = Integer .toHexString (ch );
356
- BUILDER .append ("\\ u" );
375
+ sb .append ("\\ u" );
357
376
int intLength = 4 - intString .length ();
358
377
for (int zero = 0 ; zero < intLength ; zero ++) {
359
- BUILDER .append ('0' );
378
+ sb .append ('0' );
360
379
}
361
- BUILDER .append (intString .toUpperCase (Locale .US ));
380
+ sb .append (intString .toUpperCase (Locale .US ));
362
381
} else {
363
- BUILDER .append (ch );
382
+ sb .append (ch );
364
383
}
365
384
break ;
366
385
}
367
386
}
368
387
369
388
// 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
- }
389
+ sb .append ('"' );
390
+
391
+ return sb .toString ().getBytes ();
379
392
}
380
393
}
0 commit comments