2

I have an IntentService which is supposed to send thousands of Volley requests to my server. It works fine and is very fast. I create my requests based on a Cursor I get from a ContentProvider (getContentResolver()).

I do however want to avoid making requests for items which are already available client-side. I get this list of items by calling

List<List> savedLyrics = DatabaseHelper.getInstance(this).listMetadata();

If that list is empty, it works fine. However, if it's not - then when this method

savedLyrics.contains(Arrays.asList(artist, title))

gets called, it seems to really slow things down.

public class BatchDownloaderService extends IntentService implements Response.Listener<String>, Response.ErrorListener {
    private int total = 0;
    private int count = 0;
    private int successCount = 0;
    private RequestQueue requestQueue;
    private OkHttpClient client = null;

    public BatchDownloaderService() {
        super("Batch Downloader Service");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onHandleIntent(intent);
        return START_NOT_STICKY;
    }

    @Override
    protected void onHandleIntent(Intent intent) {
    if (client == null)
        getClient();
    Uri content = intent.getExtras().getParcelable("uri");
    List<List> savedLyrics = DatabaseHelper.getInstance(this).listMetadata();
    Cursor cursor = /* Get My Cursor */;
    if (cursor == null)
        return;
    total = cursor.getCount();
    updateProgress();
    final Cache cache = new DiskBasedCache(getCacheDir(), 1024*1024);
    final Network network = new BasicNetwork(new OkHttp3Stack(client));
    requestQueue = new RequestQueue(cache, network, 8);
    requestQueue.start();
    while (cursor.moveToNext()) {
        String artist = cursor.getString(0);
        String title = cursor.getString(1);
        if (artist == null || title == null || artist.isEmpty() || title.isEmpty() || savedLyrics.contains(Arrays.asList(artist, title))) {
            // If the local database already contains this item (or if null), skip the request
            updateProgress();
            continue;
        }
        try {
            Request request = QuickLyricAPI.getVolleyRequest(lrc, this, this, artist, title);
            requestQueue.add(request);
        } catch (Exception e) {
            // Stuff
        }
    }
    cursor.close();
    }

    private void updateProgress() {
        /* Update the progressbar in the notification */
    }

    @Override
    public void onErrorResponse(VolleyError error) {
        updateProgress();
        error.printStackTrace();
    }

    @Override
    public void onResponse(String response) {
    /* Stuff */
    updateProgress();
    }

    private void getClient() {
        /* Stuff */
    }
}

If I comment out the call to .contains(), then I see no difference with the list being empty and everything is smooth.

I tried to replace the list with a TreeSet() with a comparator, it didn't make it faster. I tried to use String arrays instead of Lists, it didn't make it faster. I also tried using a Countdownlatch to make it so that onHandleIntent() doesn't finish before all the requests were done. Didn't work.

  1. How can an IntentService slow down the whole device and even crash my SystemUI like this? I thought it was running in its own thread.
  2. How can checking a list (2500 items in this case) be so expensive?

Testing this on a Nextbit Robin with 7.0

Cheers

geecko
  • 660
  • 1
  • 9
  • 22
  • 1
    Do you actually get an ANR message? Do you know what CPU usage you have when this request is performed. If the system is already running lots of threads and performing other tasks then it can still affect the UI, so your task may not be that expensive, but because of current system load affects your UI thread. You can try a method trace to see your IntentService worker thread Thread and other Threads whilst your method is being performed. – Mark Jun 09 '17 at 15:37
  • @MarkKeen I do get an ANR message. The CPU Monitor in Android studio does seem to say it's consuming close to 0% CPU. The devices I tested this were definitely not running expensive tasks in the background. I will try a method trace. – geecko Jun 10 '17 at 06:14
  • @MarkKeen It seemed to say List.contains() was consuming an important amount of CPU, which makes sense. I replaced it with a TreeSet but the performance issue still occurs. – geecko Jun 10 '17 at 08:36
  • Hmm .. well intent service calls onHandleIntent on a worker thread .. you could try running your method in the new thread, or use a Executor just to eliminate anything to do with the IntentService. Your singleton database helper doesn't have any synchronized blocks causing the UI to wait for any reason, maybe instantiate a new dbhelper and try? – Mark Jun 10 '17 at 10:02

0 Answers0