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