Skip to content

Commit 2a0e416

Browse files
committed
feat(sequentially): send events to the server sequentially.
1 parent a8eb540 commit 2a0e416

File tree

6 files changed

+109
-52
lines changed

6 files changed

+109
-52
lines changed

AndroidSDK/src/com/leanplum/Leanplum.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ public void noPendingDownloads() {
573573
});
574574

575575
// Reduce latency by running the rest of the start call in a background thread.
576-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
576+
Util.executeAsyncTask(true, new AsyncTask<Void, Void, Void>() {
577577
@Override
578578
protected Void doInBackground(Void... params) {
579579
try {
@@ -924,7 +924,7 @@ static void pause() {
924924
}
925925
LeanplumInternal.setIsPaused(true);
926926

927-
if (LeanplumInternal.isPaused()) {
927+
if (LeanplumInternal.issuedStart()) {
928928
pauseInternal();
929929
} else {
930930
LeanplumInternal.addStartIssuedHandler(new Runnable() {

AndroidSDK/src/com/leanplum/internal/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public static boolean isNoop() {
7070

7171
public static class Defaults {
7272
public static final String COUNT_KEY = "__leanplum_unsynced";
73+
public static final String START_COUNT_KEY = "__leanplum_unsynced_start";
7374
public static final String ITEM_KEY = "__leanplum_unsynced_%d";
7475
public static final String VARIABLES_KEY = "__leanplum_variables";
7576
public static final String ATTRIBUTES_KEY = "__leanplum_attributes";

AndroidSDK/src/com/leanplum/internal/FileManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public static void enableResourceSyncing(final List<String> patternsToInclude,
365365
final List<Pattern> compiledExcludePatterns = compilePatterns(patternsToExclude);
366366

367367
if (isAsync) {
368-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
368+
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
369369
@Override
370370
protected Void doInBackground(Void... params) {
371371
try {

AndroidSDK/src/com/leanplum/internal/LeanplumInternal.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ public static void setUserLocationAttribute(final Location location,
384384
@Override
385385
public void onResponse(final boolean success) {
386386
// Geocoder query must be executed on background thread.
387-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
387+
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
388388
@Override
389389
protected Void doInBackground(Void... voids) {
390390
if (!success) {

AndroidSDK/src/com/leanplum/internal/Request.java

Lines changed: 88 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class Request {
5555
private static final long DEVELOPMENT_MIN_DELAY_MS = 100;
5656
private static final long DEVELOPMENT_MAX_DELAY_MS = 5000;
5757
private static final long PRODUCTION_DELAY = 60000;
58+
private static final int MAX_ACTIONS_PER_API_CALL = 10000;
5859
private static final String LEANPLUM = "__leanplum__";
5960

6061
private static String appId;
@@ -286,7 +287,7 @@ private void triggerErrorCallback(Exception e) {
286287
}
287288

288289
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
289-
private boolean attachApiKeys(Map<String, Object> dict) {
290+
private static boolean attachApiKeys(Map<String, Object> dict) {
290291
if (appId == null || accessKey == null) {
291292
Log.e("API keys are not set. Please use Leanplum.setAppIdForDevelopmentMode or "
292293
+ "Leanplum.setAppIdForProductionMode.");
@@ -378,23 +379,24 @@ private void sendNow() {
378379

379380
this.sendEventually();
380381

381-
final List<Map<String, Object>> requestsToSend = popUnsentRequests();
382-
if (requestsToSend.isEmpty()) {
383-
return;
384-
}
385-
386-
final Map<String, Object> multiRequestArgs = new HashMap<>();
387-
multiRequestArgs.put(Constants.Params.DATA, jsonEncodeUnsentRequests(requestsToSend));
388-
multiRequestArgs.put(Constants.Params.SDK_VERSION, Constants.LEANPLUM_VERSION);
389-
multiRequestArgs.put(Constants.Params.ACTION, Constants.Methods.MULTI);
390-
multiRequestArgs.put(Constants.Params.TIME, Double.toString(new Date().getTime() / 1000.0));
391-
if (!this.attachApiKeys(multiRequestArgs)) {
392-
return;
393-
}
394-
395-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
382+
Util.executeAsyncTask(true, new AsyncTask<Void, Void, Void>() {
396383
@Override
397384
protected Void doInBackground(Void... params) {
385+
final List<Map<String, Object>> requestsToSend = getUnsentRequests();
386+
if (requestsToSend.isEmpty()) {
387+
return null;
388+
}
389+
390+
final Map<String, Object> multiRequestArgs = new HashMap<>();
391+
multiRequestArgs.put(Constants.Params.DATA, jsonEncodeUnsentRequests(requestsToSend));
392+
multiRequestArgs.put(Constants.Params.SDK_VERSION, Constants.LEANPLUM_VERSION);
393+
multiRequestArgs.put(Constants.Params.ACTION, Constants.Methods.MULTI);
394+
multiRequestArgs.put(Constants.Params.TIME, Double.toString(new Date().getTime() / 1000.0));
395+
if (!Request.attachApiKeys(multiRequestArgs)) {
396+
return null;
397+
}
398+
saveUnsentRequest(requestsToSend);
399+
398400
JSONObject result = null;
399401
HttpURLConnection op = null;
400402
try {
@@ -411,11 +413,10 @@ protected Void doInBackground(Void... params) {
411413
int statusCode = op.getResponseCode();
412414

413415
Exception errorException = null;
414-
if (statusCode >= 400) {
416+
if (statusCode == 200) {
417+
deleteSentRequests(requestsToSend.size());
418+
} else if (statusCode >= 400) {
415419
errorException = new Exception("HTTP error " + statusCode);
416-
if (statusCode == 408 || (statusCode >= 500 && statusCode <= 599)) {
417-
pushUnsentRequests(requestsToSend);
418-
}
419420
} else {
420421
if (result != null) {
421422
int numResponses = Request.numResponses(result);
@@ -433,7 +434,6 @@ protected Void doInBackground(Void... params) {
433434
Log.getStackTraceString(e));
434435
parseResponseJson(null, requestsToSend, e);
435436
} catch (Exception e) {
436-
pushUnsentRequests(requestsToSend);
437437
Log.e("Unable to send request: " + e.toString() + "\n" +
438438
Log.getStackTraceString(e));
439439
parseResponseJson(result, requestsToSend, e);
@@ -461,6 +461,64 @@ public void sendEventually() {
461461
}
462462
}
463463

464+
private static void deleteSentRequests(int requestSize) {
465+
if (requestSize == 0) {
466+
return;
467+
}
468+
469+
synchronized (lock) {
470+
Context context = Leanplum.getContext();
471+
SharedPreferences preferences = context.getSharedPreferences(
472+
LEANPLUM, Context.MODE_PRIVATE);
473+
SharedPreferences.Editor editor = preferences.edit();
474+
int start = preferences.getInt(Constants.Defaults.START_COUNT_KEY, 0);
475+
for (int i = start; i < start + requestSize; i++) {
476+
editor.remove(String.format(Locale.US, Constants.Defaults.ITEM_KEY, i));
477+
}
478+
editor.putInt(Constants.Defaults.START_COUNT_KEY, start + requestSize);
479+
try {
480+
editor.apply();
481+
} catch (NoSuchMethodError e) {
482+
editor.commit();
483+
}
484+
}
485+
}
486+
487+
private static void updateRequest(int requestNumber, Map<String, Object> args) {
488+
synchronized (lock) {
489+
Context context = Leanplum.getContext();
490+
SharedPreferences preferences = context.getSharedPreferences(
491+
LEANPLUM, Context.MODE_PRIVATE);
492+
SharedPreferences.Editor editor = preferences.edit();
493+
int start = preferences.getInt(Constants.Defaults.START_COUNT_KEY, 0);
494+
editor.putString(String.format(Locale.US, Constants.Defaults.ITEM_KEY, start + requestNumber),
495+
JsonConverter.toJson(args));
496+
try {
497+
editor.apply();
498+
} catch (NoSuchMethodError e) {
499+
editor.commit();
500+
}
501+
}
502+
}
503+
504+
private static void saveUnsentRequest(List<Map<String, Object>> requestData) {
505+
if (requestData == null || requestData.isEmpty()) {
506+
return;
507+
}
508+
for (int i = 0; i < requestData.size(); i++) {
509+
Map<String, Object> args = requestData.get(i);
510+
Object retryCountString = args.get("retryCount");
511+
int retryCount;
512+
if (retryCountString != null) {
513+
retryCount = Integer.parseInt(retryCountString.toString()) + 1;
514+
} else {
515+
retryCount = 1;
516+
}
517+
args.put("retryCount", Integer.toString(retryCount));
518+
updateRequest(i, args);
519+
}
520+
}
521+
464522
static List<Map<String, Object>> popUnsentRequests() {
465523
return getUnsentRequests(true);
466524
}
@@ -474,7 +532,6 @@ private static List<Map<String, Object>> getUnsentRequests(boolean remove) {
474532

475533
synchronized (lock) {
476534
lastSendTimeMs = System.currentTimeMillis();
477-
478535
Context context = Leanplum.getContext();
479536
SharedPreferences preferences = context.getSharedPreferences(
480537
LEANPLUM, Context.MODE_PRIVATE);
@@ -484,11 +541,11 @@ private static List<Map<String, Object>> getUnsentRequests(boolean remove) {
484541
if (count == 0) {
485542
return new ArrayList<>();
486543
}
487-
if (remove) {
488-
editor.remove(Constants.Defaults.COUNT_KEY);
544+
int start = preferences.getInt(Constants.Defaults.START_COUNT_KEY, 0);
545+
if (count - start > MAX_ACTIONS_PER_API_CALL) {
546+
count = MAX_ACTIONS_PER_API_CALL;
489547
}
490-
491-
for (int i = 0; i < count; i++) {
548+
for (int i = start; i < count; i++) {
492549
String itemKey = String.format(Locale.US, Constants.Defaults.ITEM_KEY, i);
493550
Map<String, Object> requestArgs;
494551
try {
@@ -502,15 +559,16 @@ private static List<Map<String, Object>> getUnsentRequests(boolean remove) {
502559
editor.remove(itemKey);
503560
}
504561
}
505-
if (remove) {
562+
if (remove || count == start) {
563+
editor.remove(Constants.Defaults.COUNT_KEY);
564+
editor.remove(Constants.Defaults.START_COUNT_KEY);
506565
try {
507566
editor.apply();
508567
} catch (NoSuchMethodError e) {
509568
editor.commit();
510569
}
511570
}
512571
}
513-
514572
requestData = removeIrrelevantBackgroundStartRequests(requestData);
515573
return requestData;
516574
}
@@ -560,22 +618,6 @@ private static String jsonEncodeUnsentRequests(List<Map<String, Object>> request
560618
return JsonConverter.toJson(data);
561619
}
562620

563-
private static void pushUnsentRequests(List<Map<String, Object>> requestData) {
564-
if (requestData == null) {
565-
return;
566-
}
567-
for (Map<String, Object> args : requestData) {
568-
Object retryCountString = args.get("retryCount");
569-
int retryCount;
570-
if (retryCountString != null) {
571-
retryCount = Integer.parseInt(retryCountString.toString()) + 1;
572-
} else {
573-
retryCount = 1;
574-
}
575-
args.put("retryCount", Integer.toString(retryCount));
576-
saveRequestForLater(args);
577-
}
578-
}
579621

580622
private static String getSizeAsString(int bytes) {
581623
if (bytes < (1 << 10)) {
@@ -650,7 +692,7 @@ public void sendFilesNow(final List<String> filenames, final List<InputStream> s
650692
printUploadProgress();
651693

652694
// Now upload the files
653-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
695+
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
654696
@Override
655697
protected Void doInBackground(Void... params) {
656698
synchronized (uploadFileLock) { // Don't overload app and server with many upload tasks
@@ -734,7 +776,7 @@ void downloadFile(final String path, final String url) {
734776
return;
735777
}
736778

737-
Util.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
779+
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
738780
@Override
739781
protected Void doInBackground(Void... params) {
740782
try {

AndroidSDK/src/com/leanplum/internal/Util.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
*/
9292
public class Util {
9393
private static final Executor asyncExecutor = Executors.newCachedThreadPool();
94+
private static final Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
9495

9596
private static final String ACCESS_WIFI_STATE_PERMISSION = "android.permission.ACCESS_WIFI_STATE";
9697

@@ -723,9 +724,22 @@ public static <T> T multiIndex(Map<?, ?> map, Object... indices) {
723724
return CollectionUtil.uncheckedCast(current);
724725
}
725726

726-
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> task, T... params) {
727+
/**
728+
* Execute async task on single thread Executer or cached thread pool Executer.
729+
*
730+
* @param singleThread True if needs to be executed on single thread Executer, otherwise it will
731+
* use cached thread pool Executer.
732+
* @param task Async task to execute.
733+
* @param params Params.
734+
*/
735+
public static <T> void executeAsyncTask(boolean singleThread, AsyncTask<T, ?, ?> task,
736+
T... params) {
727737
if (Build.VERSION.SDK_INT >= 11) {
728-
task.executeOnExecutor(asyncExecutor, params);
738+
if (singleThread) {
739+
task.executeOnExecutor(singleThreadExecutor, params);
740+
} else {
741+
task.executeOnExecutor(asyncExecutor, params);
742+
}
729743
} else {
730744
task.execute(params);
731745
}

0 commit comments

Comments
 (0)