| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.quicksearchbox.util; |
| |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Abstract base class for a one-place cache that holds a value that is produced |
| * asynchronously. |
| * |
| * @param <A> The type of the data held in the cache. |
| */ |
| public abstract class CachedLater<A> implements NowOrLater<A> { |
| |
| private static final String TAG = "QSB.AsyncCache"; |
| private static final boolean DBG = false; |
| |
| private final Object mLock = new Object(); |
| |
| private A mValue; |
| |
| private boolean mCreating; |
| private boolean mValid; |
| |
| private List<Consumer<? super A>> mWaitingConsumers; |
| |
| /** |
| * Creates the object to store in the cache. This method must call |
| * {@link #store} when it's done. |
| * This method must not block. |
| */ |
| protected abstract void create(); |
| |
| /** |
| * Saves a new value to the cache. |
| */ |
| protected void store(A value) { |
| if (DBG) Log.d(TAG, "store()"); |
| List<Consumer<? super A>> waitingConsumers; |
| synchronized (mLock) { |
| mValue = value; |
| mValid = true; |
| mCreating = false; |
| waitingConsumers = mWaitingConsumers; |
| mWaitingConsumers = null; |
| } |
| if (waitingConsumers != null) { |
| for (Consumer<? super A> consumer : waitingConsumers) { |
| if (DBG) Log.d(TAG, "Calling consumer: " + consumer); |
| consumer.consume(value); |
| } |
| } |
| } |
| |
| /** |
| * Gets the value. |
| * |
| * @param consumer A consumer that will be given the cached value. |
| * The consumer may be called synchronously, or asynchronously on |
| * an unspecified thread. |
| */ |
| public void getLater(Consumer<? super A> consumer) { |
| if (DBG) Log.d(TAG, "getLater()"); |
| boolean valid; |
| A value; |
| synchronized (mLock) { |
| valid = mValid; |
| value = mValue; |
| if (!valid) { |
| if (mWaitingConsumers == null) { |
| mWaitingConsumers = new ArrayList<Consumer<? super A>>(); |
| } |
| mWaitingConsumers.add(consumer); |
| } |
| } |
| if (valid) { |
| if (DBG) Log.d(TAG, "valid, calling consumer synchronously"); |
| consumer.consume(value); |
| } else { |
| boolean create = false; |
| synchronized (mLock) { |
| if (!mCreating) { |
| mCreating = true; |
| create = true; |
| } |
| } |
| if (create) { |
| if (DBG) Log.d(TAG, "not valid, calling create()"); |
| create(); |
| } else { |
| if (DBG) Log.d(TAG, "not valid, already creating"); |
| } |
| } |
| } |
| |
| /** |
| * Clears the cache. |
| */ |
| public void clear() { |
| if (DBG) Log.d(TAG, "clear()"); |
| synchronized (mLock) { |
| mValue = null; |
| mValid = false; |
| } |
| } |
| |
| public boolean haveNow() { |
| synchronized (mLock) { |
| return mValid; |
| } |
| } |
| |
| public synchronized A getNow() { |
| synchronized (mLock) { |
| if (!haveNow()) { |
| throw new IllegalStateException("getNow() called when haveNow() is false"); |
| } |
| return mValue; |
| } |
| } |
| |
| } |