5

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?

fehbari
  • 1,429
  • 2
  • 16
  • 22
  • This is also happening in the ORMLite I am looking it this now – Mashiah Feb 16 '15 at 08:19
  • I'm starting to suspect it indeed has something to do with greenDAO. If I comment out the line where I actually save the objects to the database, it no longer locks the UI. But I don't know what else to do since I'm already performing the DB operations in a separate thread. – fehbari Feb 16 '15 at 08:31
  • do heavy things in service, and update UI by Broadcast or Handler – HenryChuang Feb 16 '15 at 08:46
  • It may be a bug. Check this out: https://code.google.com/p/android-developer-preview/issues/detail?id=367 – ezaquarii Feb 16 '15 at 09:28
  • @HenryChuang Do you mean an actual Service or any other class with code running in a background thread? If you mean #2 that's exactly what I'm doing. I process the objects in my SyncService class in the background thread, and when I'm done I send a broadcast to update the UI. But it's still freezing the app for several seconds inside my performSync(...) method and I can't figure out why. – fehbari Feb 16 '15 at 09:30
  • @ezaquarii They mention the issue was fixed in October, so I'm not sure if it's that. :/ – fehbari Feb 16 '15 at 09:37
  • Yes, they fixed it in their repository. Do you have fixed Android on your device? :) – ezaquarii Feb 16 '15 at 09:38
  • I'm running the release version of Lollipop, so I believe yes. (: – fehbari Feb 16 '15 at 09:42
  • maybe you can try to use IntentService : http://developer.android.com/reference/android/app/IntentService.html – HenryChuang Feb 17 '15 at 01:16
  • I've managed to fix the freezing by creating another thread before persisting items, but I don't understand why this is necessary at all. It doesn't feel like the ideal (or even elegant) solution. Please check the updated question. Any insights would be appreciated! – fehbari Feb 22 '15 at 16:32

0 Answers0