• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UnrefedBitmapCache(final int targetSizeBytes, final float nonPooledFraction, final int nullCapacity)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
setBlocking(final boolean blocking)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
sizeOf(final ReusableBitmap value)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
poll()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
offer(final ReusableBitmap value)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
get(final RequestKey key, final boolean incrementRefCount)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
put(final RequestKey key, final ReusableBitmap value)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