165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.widget;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Bitmap;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.SparseArray;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.lang.ref.SoftReference;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.lang.reflect.InvocationTargetException;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.lang.reflect.Method;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.ArrayList;
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane// FIXME: this class saves recycle bitmap as SoftReference,  which is too vulnerable to
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane// be garbage collected due to other part of application is re-allocating lots of
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane// memory,  we will lose all SoftReference in a GC run.  We should maintain
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane// certain amount of recycled bitmaps in memory, we may also need remove bitmap from LRUCache
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane// if we are not able to get a recycled bitmap here.
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class RecycleBitmapPool {
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "RecycleBitmapPool";
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final boolean DEBUG = false;
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // allow reuse bitmap with larger bytes, set to 0 to disable different byte size
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // FIXME: wait b/10608305 to be fixed then turn back on
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int LARGER_BITMAP_ALLOWED_REUSE = 0;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final boolean LARGER_BITMAP_ALLOWED = LARGER_BITMAP_ALLOWED_REUSE > 0;
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static Method sGetAllocationByteCount;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    static {
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // KLP or later
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sGetAllocationByteCount = Bitmap.class.getMethod("getAllocationByteCount");
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NoSuchMethodException e) {
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final SparseArray<ArrayList<SoftReference<Bitmap>>> mRecycled8888 =
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            new SparseArray<ArrayList<SoftReference<Bitmap>>>();
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public RecycleBitmapPool() {
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static int getSize(Bitmap bitmap) {
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (sGetAllocationByteCount != null) {
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            try {
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return (Integer) sGetAllocationByteCount.invoke(bitmap);
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (IllegalArgumentException e) {
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "getAllocationByteCount() failed", e);
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (IllegalAccessException e) {
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "getAllocationByteCount() failed", e);
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (InvocationTargetException e) {
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "getAllocationByteCount() failed", e);
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sGetAllocationByteCount = null;
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return bitmap.getByteCount();
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static int getSize(int width, int height) {
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (width >= 2048 || height >= 2048) {
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return 0;
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return width * height * 4;
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void addRecycledBitmap(Bitmap bitmap) {
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (bitmap.isRecycled()) {
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Bitmap.Config config = bitmap.getConfig();
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (config != Bitmap.Config.ARGB_8888) {
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int key = getSize(bitmap);
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (key == 0) {
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        synchronized (mRecycled8888) {
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ArrayList<SoftReference<Bitmap>> list = mRecycled8888.get(key);
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (list == null) {
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                list = new ArrayList<SoftReference<Bitmap>>();
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mRecycled8888.put(key, list);
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            list.add(new SoftReference<Bitmap>(bitmap));
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) {
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d(TAG, list.size() + " add bitmap " + bitmap.getWidth() + " "
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + bitmap.getHeight());
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public Bitmap getRecycledBitmap(int width, int height) {
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int key = getSize(width, height);
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (key == 0) {
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        synchronized (mRecycled8888) {
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // for the new version with getAllocationByteCount(), we allow larger size
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // to be reused for the bitmap,  otherwise we just looks for same size
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Bitmap bitmap = getRecycledBitmap(mRecycled8888.get(key));
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (sGetAllocationByteCount == null || bitmap != null) {
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return bitmap;
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (LARGER_BITMAP_ALLOWED) {
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                for (int i = 0, c = mRecycled8888.size(); i < c; i++) {
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    int k = mRecycled8888.keyAt(i);
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (k > key && k <= key * LARGER_BITMAP_ALLOWED_REUSE) {
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        bitmap = getRecycledBitmap(mRecycled8888.valueAt(i));
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        if (bitmap != null) {
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            return bitmap;
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        }
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) {
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.d(TAG, "not avaialbe for " + width + "," + height);
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static Bitmap getRecycledBitmap(ArrayList<SoftReference<Bitmap>> list) {
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (list != null && !list.isEmpty()) {
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            while (!list.isEmpty()) {
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                SoftReference<Bitmap> ref = list.remove(list.size() - 1);
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Bitmap bitmap = ref.get();
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (bitmap != null && !bitmap.isRecycled()) {
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (DEBUG) {
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        Log.d(TAG, "reuse " + bitmap.getWidth() + " " + bitmap.getHeight());
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return bitmap;
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (DEBUG) {
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        Log.d(TAG, " we lost SoftReference to bitmap");
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
157