136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar/* 236436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * Copyright (C) 2017 The Android Open Source Project 336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * 436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * you may not use this file except in compliance with the License. 636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * You may obtain a copy of the License at 736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * 836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * 1036436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * Unless required by applicable law or agreed to in writing, software 1136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 1236436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * See the License for the specific language governing permissions and 1436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * limitations under the License. 1536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar */ 1636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 17ba069d50913c3fb250bb60ec310439db36895337Alan Viverettepackage androidx.lifecycle; 1836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 19ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.MainThread; 20ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.NonNull; 21ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.RestrictTo; 22ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.VisibleForTesting; 23ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.WorkerThread; 24ddee2b5170ae257a7b2494f8aaa8459ebed806dcAurimas Liutikasimport androidx.arch.core.executor.ArchTaskExecutor; 2536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 26f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craikimport java.util.concurrent.Executor; 2736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyarimport java.util.concurrent.atomic.AtomicBoolean; 2836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 2936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar/** 30f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * A LiveData class that can be invalidated & computed when there are active observers. 31f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * <p> 32f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * It can be invalidated via {@link #invalidate()}, which will result in a call to 33f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * {@link #compute()} if there are active observers (or when they start observing) 3436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * <p> 3536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * This is an internal class for now, might be public if we see the necessity. 3636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * 3736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * @param <T> The type of the live data 3836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * @hide internal 3936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar */ 4036436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 41f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyarpublic abstract class ComputableLiveData<T> { 42f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar 43f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik private final Executor mExecutor; 44f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar private final LiveData<T> mLiveData; 4536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 4636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar private AtomicBoolean mInvalid = new AtomicBoolean(true); 4745adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar private AtomicBoolean mComputing = new AtomicBoolean(false); 4836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 4936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar /** 50f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * Creates a computable live data that computes values on the arch IO thread executor. 5136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar */ 52459caadc8f6875fc78a36ae716193bf991f0808cSergey Vasilinets @SuppressWarnings("WeakerAccess") 5336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar public ComputableLiveData() { 54f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik this(ArchTaskExecutor.getIOThreadExecutor()); 55f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik } 56f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik 57f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik /** 58f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * 59f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * Creates a computable live data that computes values on the specified executor. 60f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * 61f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik * @param executor Executor that is used to compute new LiveData values. 62f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik */ 63f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik @SuppressWarnings("WeakerAccess") 64f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik public ComputableLiveData(@NonNull Executor executor) { 65f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik mExecutor = executor; 66f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar mLiveData = new LiveData<T>() { 67f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar @Override 68f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar protected void onActive() { 69f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik mExecutor.execute(mRefreshRunnable); 70f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar } 71f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar }; 7236436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 7336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 74f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar /** 75f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar * Returns the LiveData managed by this class. 76f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar * 77f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar * @return A LiveData that is controlled by ComputableLiveData. 78f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar */ 79f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar @SuppressWarnings("WeakerAccess") 80f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar @NonNull 81f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar public LiveData<T> getLiveData() { 82f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar return mLiveData; 8336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 8436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 8536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @VisibleForTesting 8636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar final Runnable mRefreshRunnable = new Runnable() { 8736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @WorkerThread 8836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @Override 8936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar public void run() { 9045adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar boolean computed; 9145adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar do { 9245adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar computed = false; 9345adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // compute can happen only in 1 thread but no reason to lock others. 9445adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar if (mComputing.compareAndSet(false, true)) { 9545adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // as long as it is invalid, keep computing. 9645adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar try { 9745adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar T value = null; 9845adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar while (mInvalid.compareAndSet(true, false)) { 9945adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar computed = true; 10045adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar value = compute(); 10145adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } 10245adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar if (computed) { 103f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar mLiveData.postValue(value); 10445adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } 10545adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } finally { 10645adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // release compute lock 10745adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar mComputing.set(false); 10845adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } 10945adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } 11045adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // check invalid after releasing compute lock to avoid the following scenario. 11145adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // Thread A runs compute() 11245adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // Thread A checks invalid, it is false 11345adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // Main thread sets invalid to true 11445adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // Thread B runs, fails to acquire compute lock and skips 11545adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // Thread A releases compute lock 11645adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar // We've left invalid in set state. The check below recovers. 11745adc615a9243a77b99a172bc4410dadc91e24b1Yigit Boyar } while (computed && mInvalid.get()); 11836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 11936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar }; 12036436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 12136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar // invalidation check always happens on the main thread 12236436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @VisibleForTesting 12336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar final Runnable mInvalidationRunnable = new Runnable() { 12436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @MainThread 12536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @Override 12636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar public void run() { 127f59164365fb6de9f148b597af5a6e19b3b7c8c2eYigit Boyar boolean isActive = mLiveData.hasActiveObservers(); 12836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar if (mInvalid.compareAndSet(false, true)) { 12936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar if (isActive) { 130f9c95209c71f8513fc5cc71406c53b89dfa39cc0Chris Craik mExecutor.execute(mRefreshRunnable); 13136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 13236436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 13336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 13436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar }; 13536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 13636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar /** 13736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * Invalidates the LiveData. 13836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * <p> 13936436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar * When there are active observers, this will trigger a call to {@link #compute()}. 14036436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar */ 14136436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar public void invalidate() { 142ae36c8b11a64d3cdc9ba6e37d9f3d1d250fdc4a8Yigit Boyar ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable); 14336436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar } 14436436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar 14536436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @SuppressWarnings("WeakerAccess") 14636436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar @WorkerThread 14736436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar protected abstract T compute(); 14836436741fe52fa90bbeeddf7baa05f97d734f5f1Yigit Boyar} 149