From 63d6ab6fa768a72bb12560f4e3d6e236c1d8ce10 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 25 Jun 2020 08:23:35 -0700 Subject: [PATCH] Throttle conversation list update frequency. This helps fast phones process messages faster by reducing contention on the database while processing a large batch of messages. --- .../ConversationListDataSource.java | 9 ++- .../securesms/util/ThrottledDebouncer.java | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/ThrottledDebouncer.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java index bba8e95cda..0512868301 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.ThrottledDebouncer; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.util.paging.Invalidator; import org.thoughtcrime.securesms.util.paging.SizeFixResult; @@ -26,6 +27,8 @@ abstract class ConversationListDataSource extends PositionalDataSource { + invalidate(); + context.getContentResolver().unregisterContentObserver(this); + }); } }; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ThrottledDebouncer.java b/app/src/main/java/org/thoughtcrime/securesms/util/ThrottledDebouncer.java new file mode 100644 index 0000000000..c7fd28c993 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ThrottledDebouncer.java @@ -0,0 +1,69 @@ +package org.thoughtcrime.securesms.util; + +import android.os.Handler; +import android.os.Message; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; + +/** + * Mixes the behavior of {@link Throttler} and {@link Debouncer}. + * + * Like a throttler, it will limit the number of runnables to be executed to be at most once every + * specified interval, while allowing the first runnable to be run immediately. + * + * However, like a debouncer, instead of completely discarding runnables that are published in the + * throttling period, the most recent one will be saved and run at the end of the throttling period. + * + * Useful for publishing a set of identical or near-identical tasks that you want to be responsive + * and guaranteed, but limited in execution frequency. + */ +public class ThrottledDebouncer { + + private static final int WHAT = 24601; + + private final OverflowHandler handler; + private final long threshold; + + /** + * @param threshold Only one runnable will be executed via {@link #publish(Runnable)} every + * {@code threshold} milliseconds. + */ + @MainThread + public ThrottledDebouncer(long threshold) { + this.handler = new OverflowHandler(); + this.threshold = threshold; + } + + @MainThread + public void publish(Runnable runnable) { + if (handler.hasMessages(WHAT)) { + handler.setRunnable(runnable); + } else { + runnable.run(); + handler.sendMessageDelayed(handler.obtainMessage(WHAT), threshold); + } + } + + @MainThread + public void clear() { + handler.removeCallbacksAndMessages(null); + } + + private static class OverflowHandler extends Handler { + + private Runnable runnable; + + @Override + public void handleMessage(Message msg) { + if (msg.what == WHAT && runnable != null) { + runnable.run(); + runnable = null; + } + } + + public void setRunnable(@NonNull Runnable runnable) { + this.runnable = runnable; + } + } +}