Loading k9mail/src/main/java/com/fsck/k9/Throttle.java +46 −77 Original line number Diff line number Diff line Loading @@ -29,50 +29,31 @@ import timber.log.Timber; * This class used to "throttle" a flow of events. * * When {@link #onEvent()} is called, it calls the callback in a certain timeout later. * Initially {@link #mMinTimeout} is used as the timeout, but if it gets multiple {@link #onEvent} * calls in a certain amount of time, it extends the timeout, until it reaches {@link #mMaxTimeout}. * Initially {@link #minTimeout} is used as the timeout, but if it gets multiple {@link #onEvent} * calls in a certain amount of time, it extends the timeout, until it reaches {@link #maxTimeout}. * * This class is primarily used to throttle content changed events. */ public class Throttle { public static final boolean DEBUG = false; // Don't submit with true public static final int DEFAULT_MIN_TIMEOUT = 150; public static final int DEFAULT_MAX_TIMEOUT = 2500; /* package */ static final int TIMEOUT_EXTEND_INTERVAL = 500; private static final int TIMEOUT_EXTEND_INTERVAL = 500; private static Timer TIMER = new Timer(); private final Clock mClock; private final Timer mTimer; /** Name of the instance. Only for logging. */ private final String mName; /** Handler for UI thread. */ private final Handler mHandler; /** Callback to be called */ private final Runnable mCallback; private final Clock clock; private final Timer timer; /** Minimum (default) timeout, in milliseconds. */ private final int mMinTimeout; private final String name; private final Handler handler; private final Runnable callback; /** Max timeout, in milliseconds. */ private final int mMaxTimeout; /** Current timeout, in milliseconds. */ private int mTimeout; private final int minTimeout; private final int maxTimeout; private int currentTimeout; /** When {@link #onEvent()} was last called. */ private long mLastEventTime; private MyTimerTask mRunningTimerTask; private long lastEventTime; /** Constructor with default timeout */ public Throttle(String name, Runnable callback, Handler handler) { this(name, callback, handler, DEFAULT_MIN_TIMEOUT, DEFAULT_MAX_TIMEOUT); } private MyTimerTask runningTimerTask; /** Constructor that takes custom timeout */ public Throttle(String name, Runnable callback, Handler handler,int minTimeout, Loading @@ -81,64 +62,60 @@ public class Throttle { } /** Constructor for tests */ /* package */ Throttle(String name, Runnable callback, Handler handler,int minTimeout, private Throttle(String name, Runnable callback, Handler handler, int minTimeout, int maxTimeout, Clock clock, Timer timer) { if (maxTimeout < minTimeout) { throw new IllegalArgumentException(); } mName = name; mCallback = callback; mClock = clock; mTimer = timer; mHandler = handler; mMinTimeout = minTimeout; mMaxTimeout = maxTimeout; mTimeout = mMinTimeout; } private void debugLog(String message) { Timber.d("Throttle: [%s] %s", mName, message); this.name = name; this.callback = callback; this.clock = clock; this.timer = timer; this.handler = handler; this.minTimeout = minTimeout; this.maxTimeout = maxTimeout; currentTimeout = this.minTimeout; } private boolean isCallbackScheduled() { return mRunningTimerTask != null; return runningTimerTask != null; } public void cancelScheduledCallback() { if (mRunningTimerTask != null) { if (DEBUG) debugLog("Canceling scheduled callback"); mRunningTimerTask.cancel(); mRunningTimerTask = null; if (runningTimerTask != null) { Timber.d("Throttle: [%s] %s", name, "Canceling scheduled callback"); runningTimerTask.cancel(); runningTimerTask = null; } } /* package */ void updateTimeout() { final long now = mClock.getTime(); if ((now - mLastEventTime) <= TIMEOUT_EXTEND_INTERVAL) { mTimeout *= 2; if (mTimeout >= mMaxTimeout) { mTimeout = mMaxTimeout; private void updateTimeout() { final long now = clock.getTime(); if ((now - lastEventTime) <= TIMEOUT_EXTEND_INTERVAL) { currentTimeout *= 2; if (currentTimeout >= maxTimeout) { currentTimeout = maxTimeout; } if (DEBUG) debugLog("Timeout extended " + mTimeout); Timber.d("Throttle: [%s] %s", name, "Timeout extended " + currentTimeout); } else { mTimeout = mMinTimeout; if (DEBUG) debugLog("Timeout reset to " + mTimeout); currentTimeout = minTimeout; Timber.d("Throttle: [%s] %s", name, "Timeout reset to " + currentTimeout); } mLastEventTime = now; lastEventTime = now; } public void onEvent() { if (DEBUG) debugLog("onEvent"); Timber.d("Throttle: [%s] %s", name, "onEvent"); updateTimeout(); if (isCallbackScheduled()) { if (DEBUG) debugLog(" callback already scheduled"); Timber.d("Throttle: [%s] %s", name, " callback already scheduled"); } else { if (DEBUG) debugLog(" scheduling callback"); mRunningTimerTask = new MyTimerTask(); mTimer.schedule(mRunningTimerTask, mTimeout); Timber.d("Throttle: [%s] %s", name, " scheduling callback"); runningTimerTask = new MyTimerTask(); timer.schedule(runningTimerTask, currentTimeout); } } Loading @@ -150,7 +127,7 @@ public class Throttle { @Override public void run() { mHandler.post(new HandlerRunnable()); handler.post(new HandlerRunnable()); } @Override Loading @@ -162,20 +139,12 @@ public class Throttle { private class HandlerRunnable implements Runnable { @Override public void run() { mRunningTimerTask = null; runningTimerTask = null; if (!mCanceled) { // This check has to be done on the UI thread. if (DEBUG) debugLog("Kicking callback"); mCallback.run(); } Timber.d("Throttle: [%s] %s", name, "Kicking callback"); callback.run(); } } } /* package */ int getTimeoutForTest() { return mTimeout; } /* package */ long getLastEventTimeForTest() { return mLastEventTime; } } Loading
k9mail/src/main/java/com/fsck/k9/Throttle.java +46 −77 Original line number Diff line number Diff line Loading @@ -29,50 +29,31 @@ import timber.log.Timber; * This class used to "throttle" a flow of events. * * When {@link #onEvent()} is called, it calls the callback in a certain timeout later. * Initially {@link #mMinTimeout} is used as the timeout, but if it gets multiple {@link #onEvent} * calls in a certain amount of time, it extends the timeout, until it reaches {@link #mMaxTimeout}. * Initially {@link #minTimeout} is used as the timeout, but if it gets multiple {@link #onEvent} * calls in a certain amount of time, it extends the timeout, until it reaches {@link #maxTimeout}. * * This class is primarily used to throttle content changed events. */ public class Throttle { public static final boolean DEBUG = false; // Don't submit with true public static final int DEFAULT_MIN_TIMEOUT = 150; public static final int DEFAULT_MAX_TIMEOUT = 2500; /* package */ static final int TIMEOUT_EXTEND_INTERVAL = 500; private static final int TIMEOUT_EXTEND_INTERVAL = 500; private static Timer TIMER = new Timer(); private final Clock mClock; private final Timer mTimer; /** Name of the instance. Only for logging. */ private final String mName; /** Handler for UI thread. */ private final Handler mHandler; /** Callback to be called */ private final Runnable mCallback; private final Clock clock; private final Timer timer; /** Minimum (default) timeout, in milliseconds. */ private final int mMinTimeout; private final String name; private final Handler handler; private final Runnable callback; /** Max timeout, in milliseconds. */ private final int mMaxTimeout; /** Current timeout, in milliseconds. */ private int mTimeout; private final int minTimeout; private final int maxTimeout; private int currentTimeout; /** When {@link #onEvent()} was last called. */ private long mLastEventTime; private MyTimerTask mRunningTimerTask; private long lastEventTime; /** Constructor with default timeout */ public Throttle(String name, Runnable callback, Handler handler) { this(name, callback, handler, DEFAULT_MIN_TIMEOUT, DEFAULT_MAX_TIMEOUT); } private MyTimerTask runningTimerTask; /** Constructor that takes custom timeout */ public Throttle(String name, Runnable callback, Handler handler,int minTimeout, Loading @@ -81,64 +62,60 @@ public class Throttle { } /** Constructor for tests */ /* package */ Throttle(String name, Runnable callback, Handler handler,int minTimeout, private Throttle(String name, Runnable callback, Handler handler, int minTimeout, int maxTimeout, Clock clock, Timer timer) { if (maxTimeout < minTimeout) { throw new IllegalArgumentException(); } mName = name; mCallback = callback; mClock = clock; mTimer = timer; mHandler = handler; mMinTimeout = minTimeout; mMaxTimeout = maxTimeout; mTimeout = mMinTimeout; } private void debugLog(String message) { Timber.d("Throttle: [%s] %s", mName, message); this.name = name; this.callback = callback; this.clock = clock; this.timer = timer; this.handler = handler; this.minTimeout = minTimeout; this.maxTimeout = maxTimeout; currentTimeout = this.minTimeout; } private boolean isCallbackScheduled() { return mRunningTimerTask != null; return runningTimerTask != null; } public void cancelScheduledCallback() { if (mRunningTimerTask != null) { if (DEBUG) debugLog("Canceling scheduled callback"); mRunningTimerTask.cancel(); mRunningTimerTask = null; if (runningTimerTask != null) { Timber.d("Throttle: [%s] %s", name, "Canceling scheduled callback"); runningTimerTask.cancel(); runningTimerTask = null; } } /* package */ void updateTimeout() { final long now = mClock.getTime(); if ((now - mLastEventTime) <= TIMEOUT_EXTEND_INTERVAL) { mTimeout *= 2; if (mTimeout >= mMaxTimeout) { mTimeout = mMaxTimeout; private void updateTimeout() { final long now = clock.getTime(); if ((now - lastEventTime) <= TIMEOUT_EXTEND_INTERVAL) { currentTimeout *= 2; if (currentTimeout >= maxTimeout) { currentTimeout = maxTimeout; } if (DEBUG) debugLog("Timeout extended " + mTimeout); Timber.d("Throttle: [%s] %s", name, "Timeout extended " + currentTimeout); } else { mTimeout = mMinTimeout; if (DEBUG) debugLog("Timeout reset to " + mTimeout); currentTimeout = minTimeout; Timber.d("Throttle: [%s] %s", name, "Timeout reset to " + currentTimeout); } mLastEventTime = now; lastEventTime = now; } public void onEvent() { if (DEBUG) debugLog("onEvent"); Timber.d("Throttle: [%s] %s", name, "onEvent"); updateTimeout(); if (isCallbackScheduled()) { if (DEBUG) debugLog(" callback already scheduled"); Timber.d("Throttle: [%s] %s", name, " callback already scheduled"); } else { if (DEBUG) debugLog(" scheduling callback"); mRunningTimerTask = new MyTimerTask(); mTimer.schedule(mRunningTimerTask, mTimeout); Timber.d("Throttle: [%s] %s", name, " scheduling callback"); runningTimerTask = new MyTimerTask(); timer.schedule(runningTimerTask, currentTimeout); } } Loading @@ -150,7 +127,7 @@ public class Throttle { @Override public void run() { mHandler.post(new HandlerRunnable()); handler.post(new HandlerRunnable()); } @Override Loading @@ -162,20 +139,12 @@ public class Throttle { private class HandlerRunnable implements Runnable { @Override public void run() { mRunningTimerTask = null; runningTimerTask = null; if (!mCanceled) { // This check has to be done on the UI thread. if (DEBUG) debugLog("Kicking callback"); mCallback.run(); } Timber.d("Throttle: [%s] %s", name, "Kicking callback"); callback.run(); } } } /* package */ int getTimeoutForTest() { return mTimeout; } /* package */ long getLastEventTimeForTest() { return mLastEventTime; } }