SparseArrayBitmapPool.java revision 033d43c3334c4633221fb18c5e826964b495f3c1
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
17package com.android.photos.data;
18
19import android.graphics.Bitmap;
20import android.util.SparseArray;
21
22import android.util.Pools.Pool;
23
24public class SparseArrayBitmapPool {
25
26    private static final int BITMAPS_TO_KEEP_AFTER_UNNEEDED_HINT = 4;
27    private int mCapacityBytes;
28    private SparseArray<Node> mStore = new SparseArray<Node>();
29    private int mSizeBytes = 0;
30
31    private Pool<Node> mNodePool;
32    private Node mPoolNodesHead = null;
33    private Node mPoolNodesTail = null;
34
35    protected static class Node {
36        Bitmap bitmap;
37        Node prevInBucket;
38        Node nextInBucket;
39        Node nextInPool;
40        Node prevInPool;
41    }
42
43    public SparseArrayBitmapPool(int capacityBytes, Pool<Node> nodePool) {
44        mCapacityBytes = capacityBytes;
45        mNodePool = nodePool;
46    }
47
48    public synchronized void setCapacity(int capacityBytes) {
49        mCapacityBytes = capacityBytes;
50        freeUpCapacity(0);
51    }
52
53    private void freeUpCapacity(int bytesNeeded) {
54        int targetSize = mCapacityBytes - bytesNeeded;
55        while (mPoolNodesTail != null && mSizeBytes > targetSize) {
56            unlinkAndRecycleNode(mPoolNodesTail, true);
57        }
58    }
59
60    private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) {
61        // Remove the node from its spot in its bucket
62        if (n.prevInBucket != null) {
63            n.prevInBucket.nextInBucket = n.nextInBucket;
64        } else {
65            mStore.put(n.bitmap.getWidth(), n.nextInBucket);
66        }
67        if (n.nextInBucket != null) {
68            n.nextInBucket.prevInBucket = n.prevInBucket;
69        }
70
71        // Remove the node from its spot in the list of pool nodes
72        if (n.prevInPool != null) {
73            n.prevInPool.nextInPool = n.nextInPool;
74        } else {
75            mPoolNodesHead = n.nextInPool;
76        }
77        if (n.nextInPool != null) {
78            n.nextInPool.prevInPool = n.prevInPool;
79        } else {
80            mPoolNodesTail = n.prevInPool;
81        }
82
83        // Recycle the node
84        n.nextInBucket = null;
85        n.nextInPool = null;
86        n.prevInBucket = null;
87        n.prevInPool = null;
88        mSizeBytes -= n.bitmap.getByteCount();
89        if (recycleBitmap) n.bitmap.recycle();
90        n.bitmap = null;
91        mNodePool.release(n);
92    }
93
94    public synchronized int getCapacity() {
95        return mCapacityBytes;
96    }
97
98    public synchronized int getSize() {
99        return mSizeBytes;
100    }
101
102    public synchronized Bitmap get(int width, int height) {
103        Node cur = mStore.get(width);
104        while (cur != null) {
105            if (cur.bitmap.getHeight() == height) {
106                Bitmap b = cur.bitmap;
107                unlinkAndRecycleNode(cur, false);
108                return b;
109            }
110            cur = cur.nextInBucket;
111        }
112        return null;
113    }
114
115    public synchronized boolean put(Bitmap b) {
116        if (b == null) {
117            return false;
118        }
119        int bytes = b.getByteCount();
120        freeUpCapacity(bytes);
121        Node newNode = mNodePool.acquire();
122        if (newNode == null) {
123            newNode = new Node();
124        }
125        newNode.bitmap = b;
126        newNode.prevInBucket = null;
127        newNode.prevInPool = null;
128        newNode.nextInPool = mPoolNodesHead;
129        mPoolNodesHead = newNode;
130        int key = b.getWidth();
131        newNode.nextInBucket = mStore.get(key);
132        if (newNode.nextInBucket != null) {
133            newNode.nextInBucket.prevInBucket = newNode;
134        }
135        mStore.put(key, newNode);
136        if (newNode.nextInPool == null) {
137            mPoolNodesTail = newNode;
138        } else {
139            newNode.nextInPool.prevInPool = newNode;
140        }
141        mSizeBytes += bytes;
142        return true;
143    }
144
145    public synchronized void clear() {
146        freeUpCapacity(mCapacityBytes);
147    }
148}
149