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