1/*
2 * Copyright (C) 2014 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.tv.settings.widget;
18
19import android.graphics.Bitmap;
20import android.util.Log;
21import android.util.SparseArray;
22
23import java.lang.ref.SoftReference;
24import java.lang.reflect.InvocationTargetException;
25import java.lang.reflect.Method;
26import java.util.ArrayList;
27
28// FIXME: this class saves recycle bitmap as SoftReference,  which is too vulnerable to
29// be garbage collected due to other part of application is re-allocating lots of
30// memory,  we will lose all SoftReference in a GC run.  We should maintain
31// certain amount of recycled bitmaps in memory, we may also need remove bitmap from LRUCache
32// if we are not able to get a recycled bitmap here.
33public class RecycleBitmapPool {
34
35    private static final String TAG = "RecycleBitmapPool";
36    private static final boolean DEBUG = false;
37    // allow reuse bitmap with larger bytes, set to 0 to disable different byte size
38    // FIXME: wait b/10608305 to be fixed then turn back on
39    private static final int LARGER_BITMAP_ALLOWED_REUSE = 0;
40    private static final boolean LARGER_BITMAP_ALLOWED = LARGER_BITMAP_ALLOWED_REUSE > 0;
41
42    private static Method sGetAllocationByteCount;
43
44    static {
45        try {
46            // KLP or later
47            sGetAllocationByteCount = Bitmap.class.getMethod("getAllocationByteCount");
48        } catch (NoSuchMethodException e) {
49        }
50    }
51
52    private final SparseArray<ArrayList<SoftReference<Bitmap>>> mRecycled8888 =
53            new SparseArray<ArrayList<SoftReference<Bitmap>>>();
54
55    public RecycleBitmapPool() {
56    }
57
58    public static int getSize(Bitmap bitmap) {
59        if (sGetAllocationByteCount != null) {
60            try {
61                return (Integer) sGetAllocationByteCount.invoke(bitmap);
62            } catch (IllegalArgumentException e) {
63                Log.e(TAG, "getAllocationByteCount() failed", e);
64            } catch (IllegalAccessException e) {
65                Log.e(TAG, "getAllocationByteCount() failed", e);
66            } catch (InvocationTargetException e) {
67                Log.e(TAG, "getAllocationByteCount() failed", e);
68            }
69            sGetAllocationByteCount = null;
70        }
71        return bitmap.getByteCount();
72    }
73
74    private static int getSize(int width, int height) {
75        if (width >= 2048 || height >= 2048) {
76            return 0;
77        }
78        return width * height * 4;
79    }
80
81    public void addRecycledBitmap(Bitmap bitmap) {
82        if (bitmap.isRecycled()) {
83            return;
84        }
85        Bitmap.Config config = bitmap.getConfig();
86        if (config != Bitmap.Config.ARGB_8888) {
87            return;
88        }
89        int key = getSize(bitmap);
90        if (key == 0) {
91            return;
92        }
93        synchronized (mRecycled8888) {
94            ArrayList<SoftReference<Bitmap>> list = mRecycled8888.get(key);
95            if (list == null) {
96                list = new ArrayList<SoftReference<Bitmap>>();
97                mRecycled8888.put(key, list);
98            }
99            list.add(new SoftReference<Bitmap>(bitmap));
100            if (DEBUG) {
101                Log.d(TAG, list.size() + " add bitmap " + bitmap.getWidth() + " "
102                        + bitmap.getHeight());
103            }
104        }
105    }
106
107    public Bitmap getRecycledBitmap(int width, int height) {
108        int key = getSize(width, height);
109        if (key == 0) {
110            return null;
111        }
112        synchronized (mRecycled8888) {
113            // for the new version with getAllocationByteCount(), we allow larger size
114            // to be reused for the bitmap,  otherwise we just looks for same size
115            Bitmap bitmap = getRecycledBitmap(mRecycled8888.get(key));
116            if (sGetAllocationByteCount == null || bitmap != null) {
117                return bitmap;
118            }
119            if (LARGER_BITMAP_ALLOWED) {
120                for (int i = 0, c = mRecycled8888.size(); i < c; i++) {
121                    int k = mRecycled8888.keyAt(i);
122                    if (k > key && k <= key * LARGER_BITMAP_ALLOWED_REUSE) {
123                        bitmap = getRecycledBitmap(mRecycled8888.valueAt(i));
124                        if (bitmap != null) {
125                            return bitmap;
126                        }
127                    }
128                }
129            }
130        }
131        if (DEBUG) {
132            Log.d(TAG, "not avaialbe for " + width + "," + height);
133        }
134        return null;
135    }
136
137    private static Bitmap getRecycledBitmap(ArrayList<SoftReference<Bitmap>> list) {
138        if (list != null && !list.isEmpty()) {
139            while (!list.isEmpty()) {
140                SoftReference<Bitmap> ref = list.remove(list.size() - 1);
141                Bitmap bitmap = ref.get();
142                if (bitmap != null && !bitmap.isRecycled()) {
143                    if (DEBUG) {
144                        Log.d(TAG, "reuse " + bitmap.getWidth() + " " + bitmap.getHeight());
145                    }
146                    return bitmap;
147                } else {
148                    if (DEBUG) {
149                        Log.d(TAG, " we lost SoftReference to bitmap");
150                    }
151                }
152            }
153        }
154        return null;
155    }
156}
157