Skip to content

Commit 1a3e3b9

Browse files
authored
fix(ANR): Moves saveRequestForLater from main thread. Adds synchroniz… (#162)
* fix(ANR): Moves saveRequestForLater from main thread. Adds synchronized to init method at LeanplumEventDataManager. * fix unit tests.
1 parent 9e0232f commit 1a3e3b9

File tree

4 files changed

+53
-24
lines changed

4 files changed

+53
-24
lines changed

AndroidSDKCore/src/main/java/com/leanplum/internal/LeanplumEventDataManager.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import android.database.DatabaseUtils;
3030
import android.database.sqlite.SQLiteDatabase;
3131
import android.database.sqlite.SQLiteOpenHelper;
32+
import android.os.AsyncTask;
3233

3334
import com.leanplum.Leanplum;
3435
import com.leanplum.utils.SharedPreferencesUtil;
@@ -41,6 +42,8 @@
4142
import java.util.Locale;
4243
import java.util.Map;
4344
import java.util.UUID;
45+
import java.util.concurrent.Executor;
46+
import java.util.concurrent.Executors;
4447

4548
/**
4649
* LeanplumEventDataManager class to work with SQLite.
@@ -57,6 +60,7 @@ public class LeanplumEventDataManager {
5760
private static SQLiteDatabase database;
5861
private static LeanplumDataBaseManager databaseManager;
5962
private static ContentValues contentValues = new ContentValues();
63+
private static final Executor sqlLiteThreadExecutor = Executors.newSingleThreadExecutor();
6064

6165
static boolean willSendErrorLog = false;
6266

@@ -65,7 +69,7 @@ public class LeanplumEventDataManager {
6569
*
6670
* @param context Current context.
6771
*/
68-
public static void init(Context context) {
72+
public static synchronized void init(Context context) {
6973
if (database != null) {
7074
Log.e("Database is already initialized.");
7175
return;
@@ -181,6 +185,17 @@ private static void handleSQLiteError(String log, Throwable t) {
181185
}
182186
}
183187

188+
/**
189+
* Execute async task on single thread Executer.
190+
*
191+
* @param task Async task to execute.
192+
* @param params Params.
193+
*/
194+
static <T> void executeAsyncTask(AsyncTask<T, ?, ?> task,
195+
T... params) {
196+
task.executeOnExecutor(sqlLiteThreadExecutor, params);
197+
}
198+
184199
private static class LeanplumDataBaseManager extends SQLiteOpenHelper {
185200
LeanplumDataBaseManager(Context context) {
186201
super(context, DATABASE_NAME, null, DATABASE_VERSION);

AndroidSDKCore/src/main/java/com/leanplum/internal/Request.java

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -230,30 +230,37 @@ private Map<String, Object> createArgsDictionary() {
230230
return args;
231231
}
232232

233-
private void saveRequestForLater(Map<String, Object> args) {
234-
synchronized (Request.class) {
235-
Context context = Leanplum.getContext();
236-
SharedPreferences preferences = context.getSharedPreferences(
237-
LEANPLUM, Context.MODE_PRIVATE);
238-
SharedPreferences.Editor editor = preferences.edit();
239-
long count = LeanplumEventDataManager.getEventsCount();
240-
String uuid = preferences.getString(Constants.Defaults.UUID_KEY, null);
241-
if (uuid == null || count % MAX_EVENTS_PER_API_CALL == 0) {
242-
uuid = UUID.randomUUID().toString();
243-
editor.putString(Constants.Defaults.UUID_KEY, uuid);
244-
SharedPreferencesUtil.commitChanges(editor);
245-
}
246-
args.put(UUID_KEY, uuid);
247-
LeanplumEventDataManager.insertEvent(JsonConverter.toJson(args));
248-
249-
dataBaseIndex = count;
250-
// Checks if here response and/or error callback for this request. We need to add callbacks to
251-
// eventCallbackManager only if here was internet connection, otherwise triggerErrorCallback
252-
// will handle error callback for this event.
253-
if (response != null || error != null && !Util.isConnected()) {
254-
eventCallbackManager.addCallbacks(this, response, error);
233+
private void saveRequestForLater(final Map<String, Object> args) {
234+
final Request currentRequest = this;
235+
LeanplumEventDataManager.executeAsyncTask(new AsyncTask<Void, Void, Void>() {
236+
@Override
237+
protected Void doInBackground(Void... params) {
238+
synchronized (Request.class) {
239+
Context context = Leanplum.getContext();
240+
SharedPreferences preferences = context.getSharedPreferences(
241+
LEANPLUM, Context.MODE_PRIVATE);
242+
SharedPreferences.Editor editor = preferences.edit();
243+
long count = LeanplumEventDataManager.getEventsCount();
244+
String uuid = preferences.getString(Constants.Defaults.UUID_KEY, null);
245+
if (uuid == null || count % MAX_EVENTS_PER_API_CALL == 0) {
246+
uuid = UUID.randomUUID().toString();
247+
editor.putString(Constants.Defaults.UUID_KEY, uuid);
248+
SharedPreferencesUtil.commitChanges(editor);
249+
}
250+
args.put(UUID_KEY, uuid);
251+
LeanplumEventDataManager.insertEvent(JsonConverter.toJson(args));
252+
253+
dataBaseIndex = count;
254+
// Checks if here response and/or error callback for this request. We need to add callbacks to
255+
// eventCallbackManager only if here was internet connection, otherwise triggerErrorCallback
256+
// will handle error callback for this event.
257+
if (response != null || error != null && !Util.isConnected()) {
258+
eventCallbackManager.addCallbacks(currentRequest, response, error);
259+
}
260+
return null;
261+
}
255262
}
256-
}
263+
});
257264
}
258265

259266
public void send() {

AndroidSDKTests/src/test/java/com/leanplum/__setup/AbstractTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ public void before() throws Exception {
130130
// Mock with our executor which will run on main thread.
131131
ReflectionHelpers.setStaticField(Util.class, "asyncExecutor", new SynchronousExecutor());
132132
ReflectionHelpers.setStaticField(Util.class, "singleThreadExecutor", new SynchronousExecutor());
133+
ReflectionHelpers.setStaticField(LeanplumEventDataManager.class, "sqlLiteThreadExecutor",
134+
new SynchronousExecutor());
135+
133136
ReflectionHelpers.setStaticField(LeanplumEventDataManager.class, "databaseManager", null);
134137
ReflectionHelpers.setStaticField(LeanplumEventDataManager.class, "database", null);
135138
// Get and set application context.

AndroidSDKTests/src/test/java/com/leanplum/internal/RequestTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import com.leanplum.Leanplum;
2626
import com.leanplum.__setup.LeanplumTestApp;
27+
import com.leanplum._whitebox.utilities.SynchronousExecutor;
2728

2829
import junit.framework.TestCase;
2930

@@ -34,6 +35,7 @@
3435
import org.robolectric.RobolectricTestRunner;
3536
import org.robolectric.RuntimeEnvironment;
3637
import org.robolectric.annotation.Config;
38+
import org.robolectric.util.ReflectionHelpers;
3739

3840
import java.lang.reflect.InvocationTargetException;
3941
import java.lang.reflect.Method;
@@ -59,6 +61,8 @@ public void setUp() {
5961
Application context = RuntimeEnvironment.application;
6062
assertNotNull(context);
6163
Leanplum.setApplicationContext(context);
64+
ReflectionHelpers.setStaticField(LeanplumEventDataManager.class, "sqlLiteThreadExecutor", new SynchronousExecutor());
65+
LeanplumEventDataManagerTest.setDatabaseToNull();
6266
}
6367

6468
/**

0 commit comments

Comments
 (0)