mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Prevent FCM bottlenecking.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package org.thoughtcrime.securesms.util.concurrent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Wraps another executor to make a new executor that only keeps around two tasks:
|
||||
* - The actively running task
|
||||
* - A single enqueued task
|
||||
*
|
||||
* If multiple tasks are enqueued while one is running, only the latest task is kept. The rest are
|
||||
* dropped.
|
||||
*
|
||||
* This is useful when you want to enqueue a bunch of tasks at unknown intervals, but only the most
|
||||
* recent one is relevant. For instance, running a query in response to changing user input.
|
||||
*
|
||||
* Based on SerialExecutor
|
||||
* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html
|
||||
*/
|
||||
public final class SerialMonoLifoExecutor implements Executor {
|
||||
private final Executor executor;
|
||||
|
||||
private Runnable next;
|
||||
private Runnable active;
|
||||
|
||||
public SerialMonoLifoExecutor(@NonNull Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void execute(@NonNull Runnable command) {
|
||||
enqueue(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if a pending task was replaced by this one, otherwise false.
|
||||
*/
|
||||
public synchronized boolean enqueue(@NonNull Runnable command) {
|
||||
boolean performedReplace = next != null;
|
||||
|
||||
next = () -> {
|
||||
try {
|
||||
command.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
};
|
||||
|
||||
if (active == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
|
||||
return performedReplace;
|
||||
}
|
||||
|
||||
private synchronized void scheduleNext() {
|
||||
active = next;
|
||||
next = null;
|
||||
if (active != null) {
|
||||
executor.execute(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.annimon.stream.function.Predicate;
|
||||
|
||||
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.whispersystems.libsignal.util.guava.Function;
|
||||
|
||||
@@ -57,7 +58,7 @@ public final class LiveDataUtil {
|
||||
*/
|
||||
public static <A, B> LiveData<B> mapAsync(@NonNull Executor executor, @NonNull LiveData<A> source, @NonNull Function<A, B> backgroundFunction) {
|
||||
MediatorLiveData<B> outputLiveData = new MediatorLiveData<>();
|
||||
Executor liveDataExecutor = new SerialLiveDataExecutor(executor);
|
||||
Executor liveDataExecutor = new SerialMonoLifoExecutor(executor);
|
||||
|
||||
outputLiveData.addSource(source, currentValue -> liveDataExecutor.execute(() -> outputLiveData.postValue(backgroundFunction.apply(currentValue))));
|
||||
|
||||
@@ -119,42 +120,4 @@ public final class LiveDataUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executor decorator that runs serially but enqueues just the latest task, dropping any pending task.
|
||||
* <p>
|
||||
* Based on SerialExecutor https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html
|
||||
* but modified to represent a queue of size one which is replaced by the latest call to {@link #execute(Runnable)}.
|
||||
*/
|
||||
private static final class SerialLiveDataExecutor implements Executor {
|
||||
private final Executor executor;
|
||||
private Runnable next;
|
||||
private Runnable active;
|
||||
|
||||
SerialLiveDataExecutor(@NonNull Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public synchronized void execute(@NonNull Runnable command) {
|
||||
next = () -> {
|
||||
try {
|
||||
command.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
};
|
||||
|
||||
if (active == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void scheduleNext() {
|
||||
active = next;
|
||||
next = null;
|
||||
if (active != null) {
|
||||
executor.execute(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user