1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bitmap; 18 19 import android.util.Log; 20 import android.util.LruCache; 21 22 import com.android.bitmap.ReusableBitmap.NullReusableBitmap; 23 import com.android.bitmap.util.Trace; 24 25 /** 26 * This subclass provides custom pool behavior. The pool can be set to block on {@link #poll()} if 27 * nothing can be returned. This is useful if you know you will incur high costs upon receiving 28 * nothing from the pool, and you do not want to incur those costs at the critical moment when the 29 * UI is animating. 30 * 31 * This subclass provides custom cache behavior. Null values can be cached. Later, 32 * when the same key is used to retrieve the value, a {@link NullReusableBitmap} singleton will 33 * be returned. 34 */ 35 public class UnrefedBitmapCache extends UnrefedPooledCache<RequestKey, ReusableBitmap> 36 implements BitmapCache { 37 private boolean mBlocking = false; 38 private final Object mLock = new Object(); 39 40 private LruCache<RequestKey, NullReusableBitmap> mNullRequests; 41 42 private final static boolean DEBUG = DecodeTask.DEBUG; 43 private final static String TAG = UnrefedBitmapCache.class.getSimpleName(); 44 45 public UnrefedBitmapCache(final int targetSizeBytes, final float nonPooledFraction, 46 final int nullCapacity) { 47 super(targetSizeBytes, nonPooledFraction); 48 49 if (nullCapacity > 0) { 50 mNullRequests = new LruCache<RequestKey, NullReusableBitmap>(nullCapacity); 51 } 52 } 53 54 /** 55 * Declare that {@link #poll()} should now block until it can return something. 56 */ 57 @Override 58 public void setBlocking(final boolean blocking) { 59 synchronized (mLock) { 60 if (DEBUG) { 61 Log.d(TAG, String.format("AltBitmapCache: block %b", blocking)); 62 } 63 mBlocking = blocking; 64 if (!mBlocking) { 65 // no longer blocking. Notify every thread. 66 mLock.notifyAll(); 67 } 68 } 69 } 70 71 @Override 72 protected int sizeOf(final ReusableBitmap value) { 73 return value.getByteCount(); 74 } 75 76 /** 77 * If {@link #setBlocking(boolean)} has been called with true, this method will block until a 78 * resource is available. 79 * @return an available resource, or null if none are available. Null will never be returned 80 * until blocking is set to false. 81 */ 82 @Override 83 public ReusableBitmap poll() { 84 ReusableBitmap bitmap; 85 synchronized (mLock) { 86 while ((bitmap = super.poll()) == null && mBlocking) { 87 if (DEBUG) { 88 Log.d(TAG, String.format( 89 "AltBitmapCache: %s waiting", Thread.currentThread().getName())); 90 } 91 Trace.beginSection("sleep"); 92 try { 93 // block 94 mLock.wait(); 95 if (DEBUG) { 96 Log.d(TAG, String.format("AltBitmapCache: %s notified", 97 Thread.currentThread().getName())); 98 } 99 } catch (InterruptedException ignored) { 100 } 101 Trace.endSection(); 102 } 103 } 104 return bitmap; 105 } 106 107 @Override 108 public void offer(final ReusableBitmap value) { 109 synchronized (mLock) { 110 super.offer(value); 111 if (DEBUG) { 112 Log.d(TAG, "AltBitmapCache: offer +1"); 113 } 114 // new resource gained. Notify one thread. 115 mLock.notify(); 116 } 117 } 118 119 @Override 120 public ReusableBitmap get(final RequestKey key, final boolean incrementRefCount) { 121 if (mNullRequests != null && mNullRequests.get(key) != null) { 122 return NullReusableBitmap.getInstance(); 123 } 124 return super.get(key, incrementRefCount); 125 } 126 127 /** 128 * Note: The cache only supports same-sized bitmaps. 129 */ 130 @Override 131 public ReusableBitmap put(final RequestKey key, final ReusableBitmap value) { 132 if (mNullRequests != null && (value == null || value == NullReusableBitmap.getInstance())) { 133 mNullRequests.put(key, NullReusableBitmap.getInstance()); 134 return null; 135 } 136 137 return super.put(key, value); 138 } 139 } 140