mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 08:09:12 +01:00
Prevent main thread posts when unobserved LiveRecipients change.
This commit is contained in:
committed by
jeffrey-signal
parent
cf7fee2de8
commit
b0b2c32a6f
@@ -6,13 +6,10 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
|
|
||||||
import org.signal.core.util.ThreadUtil;
|
import org.signal.core.util.ThreadUtil;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.database.DistributionListTables;
|
|
||||||
import org.thoughtcrime.securesms.database.GroupTable;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.RecipientRecord;
|
import org.thoughtcrime.securesms.database.model.RecipientRecord;
|
||||||
@@ -23,7 +20,9 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Observable;
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||||
|
|
||||||
public final class LiveRecipient {
|
public final class LiveRecipient {
|
||||||
@@ -31,26 +30,21 @@ public final class LiveRecipient {
|
|||||||
private static final String TAG = Log.tag(LiveRecipient.class);
|
private static final String TAG = Log.tag(LiveRecipient.class);
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final MutableLiveData<Recipient> liveData;
|
|
||||||
private final LiveData<Recipient> observableLiveData;
|
private final LiveData<Recipient> observableLiveData;
|
||||||
private final LiveData<Recipient> observableLiveDataResolved;
|
private final LiveData<Recipient> observableLiveDataResolved;
|
||||||
private final Set<RecipientForeverObserver> observers;
|
private final Set<RecipientForeverObserver> observers;
|
||||||
private final Observer<Recipient> foreverObserver;
|
private final Observer<Recipient> foreverObserver;
|
||||||
private final AtomicReference<Recipient> recipient;
|
private final AtomicReference<Recipient> recipient;
|
||||||
private final RecipientTable recipientTable;
|
private final RecipientTable recipientTable;
|
||||||
private final GroupTable groupDatabase;
|
|
||||||
private final DistributionListTables distributionListTables;
|
|
||||||
private final MutableLiveData<Object> refreshForceNotify;
|
|
||||||
private final BehaviorSubject<Recipient> subject;
|
private final BehaviorSubject<Recipient> subject;
|
||||||
|
private final BehaviorSubject<Object> forceNotifySubject;
|
||||||
|
|
||||||
LiveRecipient(@NonNull Context context, @NonNull Recipient defaultRecipient) {
|
LiveRecipient(@NonNull Context context, @NonNull Recipient defaultRecipient) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.liveData = new MutableLiveData<>(defaultRecipient);
|
|
||||||
this.subject = BehaviorSubject.createDefault(defaultRecipient);
|
this.subject = BehaviorSubject.createDefault(defaultRecipient);
|
||||||
|
this.forceNotifySubject = BehaviorSubject.createDefault(new Object());
|
||||||
this.recipient = new AtomicReference<>(defaultRecipient);
|
this.recipient = new AtomicReference<>(defaultRecipient);
|
||||||
this.recipientTable = SignalDatabase.recipients();
|
this.recipientTable = SignalDatabase.recipients();
|
||||||
this.groupDatabase = SignalDatabase.groups();
|
|
||||||
this.distributionListTables = SignalDatabase.distributionLists();
|
|
||||||
this.observers = new CopyOnWriteArraySet<>();
|
this.observers = new CopyOnWriteArraySet<>();
|
||||||
this.foreverObserver = recipient -> {
|
this.foreverObserver = recipient -> {
|
||||||
ThreadUtil.postToMain(() -> {
|
ThreadUtil.postToMain(() -> {
|
||||||
@@ -59,10 +53,9 @@ public final class LiveRecipient {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
this.refreshForceNotify = new MutableLiveData<>(new Object());
|
this.observableLiveData = new ObservableLiveData<>(Observable.combineLatest(subject.distinctUntilChanged(Recipient::hasSameContent),
|
||||||
this.observableLiveData = LiveDataUtil.combineLatest(LiveDataUtil.distinctUntilChanged(liveData, Recipient::hasSameContent),
|
forceNotifySubject,
|
||||||
refreshForceNotify,
|
(recipient, force) -> recipient));
|
||||||
(recipient, force) -> recipient);
|
|
||||||
this.observableLiveDataResolved = LiveDataUtil.filter(this.observableLiveData, r -> !r.isResolving());
|
this.observableLiveDataResolved = LiveDataUtil.filter(this.observableLiveData, r -> !r.isResolving());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +171,7 @@ public final class LiveRecipient {
|
|||||||
|
|
||||||
Recipient recipient = fetchAndCacheRecipientFromDisk(id);
|
Recipient recipient = fetchAndCacheRecipientFromDisk(id);
|
||||||
set(recipient);
|
set(recipient);
|
||||||
refreshForceNotify.postValue(new Object());
|
forceNotifySubject.onNext(new Object());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull LiveData<Recipient> getLiveData() {
|
public @NonNull LiveData<Recipient> getLiveData() {
|
||||||
@@ -205,7 +198,6 @@ public final class LiveRecipient {
|
|||||||
|
|
||||||
synchronized void set(@NonNull Recipient recipient) {
|
synchronized void set(@NonNull Recipient recipient) {
|
||||||
this.recipient.set(recipient);
|
this.recipient.set(recipient);
|
||||||
this.liveData.postValue(recipient);
|
|
||||||
this.subject.onNext(recipient);
|
this.subject.onNext(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,4 +213,32 @@ public final class LiveRecipient {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(recipient);
|
return Objects.hash(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LiveData} backed by an {@link Observable}. Subscribes to the source only when there
|
||||||
|
* are active observers ({@link #onActive()}) and disposes when all observers are removed
|
||||||
|
* ({@link #onInactive()}). This ensures no main thread work is scheduled for unobserved instances.
|
||||||
|
*/
|
||||||
|
private static class ObservableLiveData<T> extends LiveData<T> {
|
||||||
|
private final Observable<T> source;
|
||||||
|
private Disposable disposable;
|
||||||
|
|
||||||
|
ObservableLiveData(@NonNull Observable<T> source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActive() {
|
||||||
|
disposable = source.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(this::setValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onInactive() {
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
disposable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user