I'm experiencing a weird issue. I have my app's 2-way sync code running inside a ScheduledThreadPoolExecutor
. The code iterates over a large list of objects from the server's response and saves them to the local database (SQLite with GreenDAO).
The problem is, while this thread is processing the list of objects, the UI locks for several seconds and I get multiple warnings of skipped frames in logcat.
02-16 02:34:34.539 5730-5730/com.myapp.android D/SyncService﹕ Sync done.
02-16 02:34:34.552 5730-5730/com.myapp.android I/Choreographer﹕ Skipped 123 frames! The application may be doing too much work on its main thread.
02-16 02:34:41.060 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 168341(7MB) AllocSpace objects, 1(81KB) LOS objects, 15% free, 39MB/46MB, paused 1.774ms total 108.998ms
02-16 02:34:42.515 5730-5748/com.myapp.android I/art﹕ Background partial concurrent mark sweep GC freed 249676(9MB) AllocSpace objects, 34(3MB) LOS objects, 32% free, 33MB/49MB, paused 10.125ms total 118.693ms
02-16 02:34:45.361 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 166082(7MB) AllocSpace objects, 1(81KB) LOS objects, 14% free, 41MB/49MB, paused 3.151ms total 115.302ms
02-16 02:34:46.792 5730-5748/com.myapp.android I/art﹕ Background partial concurrent mark sweep GC freed 296675(11MB) AllocSpace objects, 0(0B) LOS objects, 29% free, 38MB/54MB, paused 4.427ms total 122.870ms
02-16 02:34:49.785 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 172207(7MB) AllocSpace objects, 0(0B) LOS objects, 13% free, 46MB/54MB, paused 3.229ms total 125.006ms
02-16 02:34:51.274 5730-5748/com.myapp.android I/art﹕ Background partial concurrent mark sweep GC freed 302918(11MB) AllocSpace objects, 0(0B) LOS objects, 27% free, 43MB/59MB, paused 5.143ms total 132.996ms
02-16 02:34:54.200 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 167589(7MB) AllocSpace objects, 6(486KB) LOS objects, 13% free, 51MB/59MB, paused 5.902ms total 136.040ms
02-16 02:34:55.570 5730-5748/com.myapp.android I/art﹕ Background partial concurrent mark sweep GC freed 292352(11MB) AllocSpace objects, 0(0B) LOS objects, 24% free, 48MB/64MB, paused 4.440ms total 157.392ms
02-16 02:34:58.510 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 173205(7MB) AllocSpace objects, 0(0B) LOS objects, 11% free, 56MB/64MB, paused 5.286ms total 133.044ms
02-16 02:35:00.100 5730-5748/com.myapp.android I/art﹕ Background partial concurrent mark sweep GC freed 303665(11MB) AllocSpace objects, 0(0B) LOS objects, 23% free, 53MB/69MB, paused 10.063ms total 233.137ms
02-16 02:35:03.054 5730-5748/com.myapp.android I/art﹕ Background sticky concurrent mark sweep GC freed 173642(7MB) AllocSpace objects, 0(0B) LOS objects, 10% free, 61MB/69MB, paused 6.528ms total 148.586ms
02-16 02:35:03.784 5730-5730/com.myapp.android I/Choreographer﹕ Skipped 1752 frames! The application may be doing too much work on its main thread.
I don't understand why this is happening. As far as I know the code is not running in the UI thread, and therefore should not cause this kind of issue.
Here's a sample of my code. Inside performSync(changesOnly, false)
is where I iterate over the list and persist data to the database.
public void performSync(final boolean changesOnly, int delay) {
// Skip sync if it's already running.
if (!isSyncing()) {
// Create new thread for responsiveness.
new ScheduledThreadPoolExecutor(1).schedule(new Runnable() {
@Override
public void run() {
// Forward call to internal sync method.
performSync(changesOnly, false);
}
}, delay, TimeUnit.SECONDS);
}
}
If I comment out the call to performSync(changesOnly, false)
the UI doesn't lock, since no objects are processed. I tried using a regular Thread
and even an AsyncTask
instead of the ScheduledThreadPoolExecutor
, but the problem persisted.
In case you're wondering why are there two performSync
methods, the second one (where the processing happens) is recursive, and keeps sending batches of 200 objects to the server until it's done. This is not causing the issue, as when the lock happens I'm only receiving data (not sending), and therefore the method only executes once.
Any ideas of what could be causing this? I'm not performing database operations in the UI thread, and the lock only happens with a large list of objects.
UPDATE: I've managed to fix the freezing by creating another thread before iterating the list and persisting items to the database. So I have the ScheduledThreadPoolExecutor
where the sync happens, and a regular Thread
where I persist items to the database.
// Create another thread for processing objects.
new Thread(new Runnable() {
@Override
public void run() {
// Start thread with low priority.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Process new objects and persist to the database.
if (response.getObjects() != null) {
for (MyObject object : response.getObjects()) {
...
}
}
}
}).start();
This code is executed inside the aforementioned performSync
method. So it's yet another thread created inside the original ScheduledThreadPoolExecutor
. I honestly don't understand why I need to do this in order to avoid freezing the UI. Any ideas?