/* * Copyright (C) 2011 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.dialer.util; import android.util.LruCache; import com.android.contacts.common.testing.NeededForTesting; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; /** * An LRU cache in which all items can be marked as expired at a given time and it is possible to * query whether a particular cached value is expired or not. *
* A typical use case for this is caching of values which are expensive to compute but which are * still useful when out of date. *
* Consider a cache for contact information: *
{@code * private ExpirableCache* which stores the contact information for a given phone number. *mContactCache;}
* When we need to store contact information for a given phone number, we can look up the info in * the cache: *
{@code * CachedValue* We might also want to fetch the contact information again if the item is expired. *cachedContact = mContactCache.getCachedValue(phoneNumber); * }
* if (cachedContact.isExpired()) { * fetchContactForNumber(phoneNumber, * new FetchListener() { * @Override * public void onFetched(Contact contact) { * mContactCache.put(phoneNumber, contact); * } * }); * }* and insert it back into the cache when the fetch completes. *
* At a certain point we want to expire the content of the cache because we know the content may * no longer be up-to-date, for instance, when resuming the activity this is shown into: *
* @Override * protected onResume() { * // We were paused for some time, the cached value might no longer be up to date. * mContactCache.expireAll(); * super.onResume(); * } ** The values will be still available from the cache, but they will be expired. *
* If interested only in the value itself, not whether it is expired or not, one should use the * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should * use the {@link #get(Object)} method instead. *
* This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache * by using the {@link #create(LruCache)} method, which can define a custom expiration policy. * Since the underlying cache maps keys to cached values it can determine which items are expired * and which are not, allowing for an implementation that evicts expired items before non expired * ones. *
* This class is thread-safe.
*
* @param
* It provides access to the value stored in the cache but also allows to check whether the
* value is expired.
*
* @param
* Items in the cache can belong to a previous generation, but in that case they would be
* expired.
*
* @see ExpirableCache.CachedValue#isExpired()
*/
private final AtomicInteger mGeneration;
private ExpirableCache(LruCache
* The cached value gives access both to the value associated with the key and whether it is
* expired or not.
*
* If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
* instead.
*
* If only wants values that are not expired, use {@link #get(Object)} instead.
*
* @param key the key to look up
*/
public CachedValue
* When using this method, it is not possible to determine whether the value is expired or not.
* Use {@link #getCachedValue(Object)} to achieve that instead. However, if using
* {@link #getCachedValue(Object)} to determine if an item is expired, one should use the item
* within the {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the
* value afterwards, since that is not guaranteed to return the same value or that the newly
* returned value is in the same state.
*
* @param key the key to look up
*/
public V getPossiblyExpired(K key) {
CachedValue
* This method will return null if either there is no value associated with this key or if the
* associated value is expired.
*
* @param key the key to look up
*/
@NeededForTesting
public V get(K key) {
CachedValue
* Newly added item will not be expired until {@link #expireAll()} is next called.
*
* @param key the key to look up
* @param value the value to associate with the key
*/
public void put(K key, V value) {
mCache.put(key, newCachedValue(value));
}
/**
* Mark all items currently in the cache as expired.
*
* Newly added items after this call will be marked as not expired.
*
* Expiring the items in the cache does not imply they will be evicted.
*/
public void expireAll() {
mGeneration.incrementAndGet();
}
/**
* Creates a new {@link CachedValue} instance to be stored in this cache.
*
* Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
*/
public CachedValue
* The created cache takes ownership of the cache passed in as an argument.
*
* @param