1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.supplier; 6 7 import android.os.Handler; 8 9 import androidx.annotation.Nullable; 10 11 import org.chromium.base.Callback; 12 import org.chromium.base.ObserverList; 13 import org.chromium.base.ResettersForTesting; 14 15 import java.util.Objects; 16 17 /** 18 * Concrete implementation of {@link ObservableSupplier} to be used by classes owning the 19 * ObservableSupplier and providing it as a dependency to others. 20 * 21 * This class must only be accessed from a single thread. 22 * 23 * To use: 24 * 1. Create a new ObservableSupplierImpl<E> to pass as a dependency 25 * 2. Call {@link #set(Object)} when the real object becomes available. {@link #set(Object)} may 26 * be called multiple times. Observers will be notified each time a new object is set. 27 * 28 * @param <E> The type of the wrapped object. 29 */ 30 public class ObservableSupplierImpl<E> implements ObservableSupplier<E> { 31 private static boolean sIgnoreThreadChecksForTesting; 32 33 private final Thread mThread = Thread.currentThread(); 34 private final Handler mHandler = new Handler(); 35 36 private E mObject; 37 private final ObserverList<Callback<E>> mObservers = new ObserverList<>(); 38 ObservableSupplierImpl()39 public ObservableSupplierImpl() {} 40 ObservableSupplierImpl(E initialValue)41 public ObservableSupplierImpl(E initialValue) { 42 mObject = initialValue; 43 } 44 45 @Override addObserver(Callback<E> obs)46 public E addObserver(Callback<E> obs) { 47 checkThread(); 48 mObservers.addObserver(obs); 49 50 if (mObject != null) { 51 final E currentObject = mObject; 52 mHandler.post( 53 () -> { 54 if (mObject != currentObject || !mObservers.hasObserver(obs)) return; 55 obs.onResult(mObject); 56 }); 57 } 58 59 return mObject; 60 } 61 62 @Override removeObserver(Callback<E> obs)63 public void removeObserver(Callback<E> obs) { 64 checkThread(); 65 mObservers.removeObserver(obs); 66 } 67 68 /** 69 * Set the object supplied by this supplier. This will notify registered callbacks that the 70 * dependency is available if the object changes. Object equality is used when deciding if the 71 * object has changed, not reference equality. 72 * 73 * @param object The object to supply. 74 */ set(E object)75 public void set(E object) { 76 checkThread(); 77 if (Objects.equals(object, mObject)) { 78 return; 79 } 80 81 mObject = object; 82 83 for (Callback<E> observer : mObservers) { 84 observer.onResult(mObject); 85 } 86 } 87 88 @Override get()89 public @Nullable E get() { 90 checkThread(); 91 return mObject; 92 } 93 94 /** Returns if there are any observers currently. */ hasObservers()95 public boolean hasObservers() { 96 return !mObservers.isEmpty(); 97 } 98 checkThread()99 private void checkThread() { 100 assert sIgnoreThreadChecksForTesting || mThread == Thread.currentThread() 101 : "ObservableSupplierImpl must only be used on a single Thread."; 102 } 103 104 /** Used to allow developers to access supplier values on the instrumentation thread. */ setIgnoreThreadChecksForTesting(boolean ignoreThreadChecks)105 public static void setIgnoreThreadChecksForTesting(boolean ignoreThreadChecks) { 106 sIgnoreThreadChecksForTesting = ignoreThreadChecks; 107 ResettersForTesting.register(() -> sIgnoreThreadChecksForTesting = false); 108 } 109 } 110