• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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