diff options
author | Owen Lin <owenlin@google.com> | 2012-04-09 10:15:35 +0800 |
---|---|---|
committer | Owen Lin <owenlin@google.com> | 2012-04-10 14:39:23 +0800 |
commit | cafd30f96355ad446943d60cd2595d08423944e2 (patch) | |
tree | 04f1473abb3d25eef6c36fe90bf1281120f89153 /src/com/android/gallery3d/data | |
parent | 8ef6c55bdad9a3e835ce56bdc98681434b4ac5b3 (diff) | |
download | android_packages_apps_Gallery2-cafd30f96355ad446943d60cd2595d08423944e2.tar.gz android_packages_apps_Gallery2-cafd30f96355ad446943d60cd2595d08423944e2.tar.bz2 android_packages_apps_Gallery2-cafd30f96355ad446943d60cd2595d08423944e2.zip |
Add BytesBufferPool to prevent GC.
Change-Id: Ia8513ff380a60f102481cbf25650eca149b75064
Diffstat (limited to 'src/com/android/gallery3d/data')
-rw-r--r-- | src/com/android/gallery3d/data/BitmapPool.java | 96 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/BytesBufferPool.java | 91 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ImageCacheRequest.java | 76 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ImageCacheService.java | 39 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/MediaItem.java | 11 |
5 files changed, 259 insertions, 54 deletions
diff --git a/src/com/android/gallery3d/data/BitmapPool.java b/src/com/android/gallery3d/data/BitmapPool.java new file mode 100644 index 000000000..c52a57b0a --- /dev/null +++ b/src/com/android/gallery3d/data/BitmapPool.java @@ -0,0 +1,96 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +package com.android.gallery3d.data; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; + +import com.android.gallery3d.ui.Log; +import com.android.gallery3d.util.ThreadPool.JobContext; + +import java.io.FileDescriptor; +import java.util.ArrayList; + +public class BitmapPool { + private static final String TAG = "BitmapPool"; + + private static final int POOL_SIZE = 16; + private final ArrayList<Bitmap> mPool = new ArrayList<Bitmap>(POOL_SIZE); + + private final int mWidth; + private final int mHeight; + + public BitmapPool(int width, int height) { + mWidth = width; + mHeight = height; + } + + public synchronized Bitmap getBitmap() { + int size = mPool.size(); + return size > 0 ? mPool.remove(size - 1) : null; + } + + public void recycle(Bitmap bitmap) { + if (bitmap == null) return; + if ((bitmap.getWidth() != mWidth) || (bitmap.getHeight() != mHeight)) { + bitmap.recycle(); + return; + } + synchronized (this) { + if (mPool.size() < POOL_SIZE) mPool.add(bitmap); + } + } + + public synchronized void clear() { + mPool.clear(); + } + + public Bitmap decode(JobContext jc, + byte[] data, int offset, int length, BitmapFactory.Options options) { + if (options == null) options = new BitmapFactory.Options(); + if (options.inSampleSize < 1) options.inSampleSize = 1; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + options.inBitmap = (options.inSampleSize == 1) ? getBitmap() : null; + try { + Bitmap bitmap = DecodeUtils.decode(jc, data, offset, length, options); + if (options.inBitmap != null && options.inBitmap != bitmap) { + recycle(options.inBitmap); + options.inBitmap = null; + } + return bitmap; + } catch (IllegalArgumentException e) { + if (options.inBitmap == null) throw e; + + Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); + recycle(options.inBitmap); + options.inBitmap = null; + return DecodeUtils.decode(jc, data, offset, length, options); + } + } + + // This is the same as the method above except the source data comes + // from a file descriptor instead of a byte array. + public Bitmap decode(JobContext jc, + FileDescriptor fileDescriptor, Options options) { + if (options == null) options = new BitmapFactory.Options(); + if (options.inSampleSize < 1) options.inSampleSize = 1; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + options.inBitmap = (options.inSampleSize == 1) ? getBitmap() : null; + try { + Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options); + if (options.inBitmap != null&& options.inBitmap != bitmap) { + recycle(options.inBitmap); + options.inBitmap = null; + } + return bitmap; + } catch (IllegalArgumentException e) { + if (options.inBitmap == null) throw e; + + Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); + recycle(options.inBitmap); + options.inBitmap = null; + return DecodeUtils.decode(jc, fileDescriptor, options); + } + } +} diff --git a/src/com/android/gallery3d/data/BytesBufferPool.java b/src/com/android/gallery3d/data/BytesBufferPool.java new file mode 100644 index 000000000..d2da323fc --- /dev/null +++ b/src/com/android/gallery3d/data/BytesBufferPool.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.data; + +import com.android.gallery3d.util.ThreadPool.JobContext; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; + +public class BytesBufferPool { + + private static final int READ_STEP = 4096; + + public static class BytesBuffer { + public byte[] data; + public int offset; + public int length; + + private BytesBuffer(int capacity) { + this.data = new byte[capacity]; + } + + // an helper function to read content from FileDescriptor + public void readFrom(JobContext jc, FileDescriptor fd) throws IOException { + FileInputStream fis = new FileInputStream(fd); + length = 0; + try { + int capacity = data.length; + while (true) { + int step = Math.min(READ_STEP, capacity - length); + int rc = fis.read(data, length, step); + if (rc < 0 || jc.isCancelled()) return; + length += rc; + + if (length == capacity) { + byte[] newData = new byte[data.length * 2]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + capacity = data.length; + } + } + } finally { + fis.close(); + } + } + } + + private final int mPoolSize; + private final int mBufferSize; + private final ArrayList<BytesBuffer> mList; + + public BytesBufferPool(int poolSize, int bufferSize) { + mList = new ArrayList<BytesBuffer>(poolSize); + mPoolSize = poolSize; + mBufferSize = bufferSize; + } + + public synchronized BytesBuffer get() { + int n = mList.size(); + return n > 0 ? mList.remove(n - 1) : new BytesBuffer(mBufferSize); + } + + public synchronized void recycle(BytesBuffer buffer) { + if (buffer.data.length != mBufferSize) return; + if (mList.size() < mPoolSize) { + buffer.offset = 0; + buffer.length = 0; + mList.add(buffer); + } + } + + public synchronized void clear() { + mList.clear(); + } +} diff --git a/src/com/android/gallery3d/data/ImageCacheRequest.java b/src/com/android/gallery3d/data/ImageCacheRequest.java index d3d4f51e7..c10158bcd 100644 --- a/src/com/android/gallery3d/data/ImageCacheRequest.java +++ b/src/com/android/gallery3d/data/ImageCacheRequest.java @@ -21,7 +21,7 @@ import android.graphics.BitmapFactory; import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.data.ImageCacheService.ImageData; +import com.android.gallery3d.data.BytesBufferPool.BytesBuffer; import com.android.gallery3d.util.ThreadPool.Job; import com.android.gallery3d.util.ThreadPool.JobContext; @@ -41,52 +41,56 @@ abstract class ImageCacheRequest implements Job<Bitmap> { mTargetSize = targetSize; } + @Override public Bitmap run(JobContext jc) { String debugTag = mPath + "," + ((mType == MediaItem.TYPE_THUMBNAIL) ? "THUMB" : (mType == MediaItem.TYPE_MICROTHUMBNAIL) ? "MICROTHUMB" : "?"); ImageCacheService cacheService = mApplication.getImageCacheService(); - ImageData data = cacheService.getImageData(mPath, mType); - if (jc.isCancelled()) return null; - - if (data != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap; - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = MediaItem.getMicroThumbPool().decode(jc, - data.mData, data.mOffset, data.mData.length - data.mOffset, options); - } else { - bitmap = DecodeUtils.decode(jc, - data.mData, data.mOffset, data.mData.length - data.mOffset, options); - } - if (bitmap == null && !jc.isCancelled()) { - Log.w(TAG, "decode cached failed " + debugTag); - } - return bitmap; - } else { - Bitmap bitmap = onDecodeOriginal(jc, mType); + BytesBuffer buffer = MediaItem.getBytesBufferPool().get(); + try { + boolean found = cacheService.getImageData(mPath, mType, buffer); if (jc.isCancelled()) return null; - - if (bitmap == null) { - Log.w(TAG, "decode orig failed " + debugTag); - return null; - } - - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeAndCropCenter(bitmap, mTargetSize, true); - } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, mTargetSize, true); + if (found) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + Bitmap bitmap; + if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { + bitmap = MediaItem.getMicroThumbPool().decode(jc, + buffer.data, buffer.offset, buffer.length, options); + } else { + bitmap = DecodeUtils.decode(jc, + buffer.data, buffer.offset, buffer.length, options); + } + if (bitmap == null && !jc.isCancelled()) { + Log.w(TAG, "decode cached failed " + debugTag); + } + return bitmap; } - if (jc.isCancelled()) return null; + } finally { + MediaItem.getBytesBufferPool().recycle(buffer); + } + Bitmap bitmap = onDecodeOriginal(jc, mType); + if (jc.isCancelled()) return null; - byte[] array = BitmapUtils.compressToBytes(bitmap); - if (jc.isCancelled()) return null; + if (bitmap == null) { + Log.w(TAG, "decode orig failed " + debugTag); + return null; + } - cacheService.putImageData(mPath, mType, array); - return bitmap; + if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { + bitmap = BitmapUtils.resizeAndCropCenter(bitmap, mTargetSize, true); + } else { + bitmap = BitmapUtils.resizeDownBySideLength(bitmap, mTargetSize, true); } + if (jc.isCancelled()) return null; + + byte[] array = BitmapUtils.compressToBytes(bitmap); + if (jc.isCancelled()) return null; + + cacheService.putImageData(mPath, mType, array); + return bitmap; } public abstract Bitmap onDecodeOriginal(JobContext jc, int targetSize); diff --git a/src/com/android/gallery3d/data/ImageCacheService.java b/src/com/android/gallery3d/data/ImageCacheService.java index fdc27749b..0e7931389 100644 --- a/src/com/android/gallery3d/data/ImageCacheService.java +++ b/src/com/android/gallery3d/data/ImageCacheService.java @@ -19,7 +19,9 @@ package com.android.gallery3d.data; import android.content.Context; import com.android.gallery3d.common.BlobCache; +import com.android.gallery3d.common.BlobCache.LookupRequest; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.data.BytesBufferPool.BytesBuffer; import com.android.gallery3d.util.CacheManager; import com.android.gallery3d.util.GalleryUtils; @@ -43,32 +45,35 @@ public class ImageCacheService { IMAGE_CACHE_VERSION); } - public static class ImageData { - public ImageData(byte[] data, int offset) { - mData = data; - mOffset = offset; - } - public byte[] mData; - public int mOffset; - } - - public ImageData getImageData(Path path, int type) { + /** + * Gets the cached image data for the given <code>path</code> and <code>type</code>. + * + * The image data will be stored in <code>buffer.data</code>, started from + * <code>buffer.offset</code> for <code>buffer.length</code> bytes. If the + * buffer.data is not big enough, a new byte array will be allocated and returned. + * + * @return true if the image data is found; false if not found. + */ + public boolean getImageData(Path path, int type, BytesBuffer buffer) { byte[] key = makeKey(path, type); long cacheKey = Utils.crc64Long(key); try { - byte[] value = null; + LookupRequest request = new LookupRequest(); + request.key = cacheKey; + request.buffer = buffer.data; synchronized (mCache) { - value = mCache.lookup(cacheKey); + if (!mCache.lookup(request)) return false; } - if (value == null) return null; - if (isSameKey(key, value)) { - int offset = key.length; - return new ImageData(value, offset); + if (isSameKey(key, request.buffer)) { + buffer.data = request.buffer; + buffer.offset = key.length; + buffer.length = request.length - buffer.offset; + return true; } } catch (IOException ex) { // ignore. } - return null; + return false; } public void putImageData(Path path, int type, byte[] value) { diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java index 2b8f8a353..3de70d954 100644 --- a/src/com/android/gallery3d/data/MediaItem.java +++ b/src/com/android/gallery3d/data/MediaItem.java @@ -19,7 +19,6 @@ package com.android.gallery3d.data; import android.graphics.Bitmap; import android.graphics.BitmapRegionDecoder; -import com.android.gallery3d.ui.BitmapPool; import com.android.gallery3d.ui.ScreenNail; import com.android.gallery3d.util.ThreadPool.Job; @@ -40,9 +39,15 @@ public abstract class MediaItem extends MediaObject { public static final String MIME_TYPE_JPEG = "image/jpeg"; + private static final int BYTESBUFFE_POOL_SIZE = 4; + private static final int BYTESBUFFER_SIZE = 200 * 1024; + private static final BitmapPool sMicroThumbPool = new BitmapPool(MICROTHUMBNAIL_TARGET_SIZE, MICROTHUMBNAIL_TARGET_SIZE); + private static final BytesBufferPool sMicroThumbBufferPool = + new BytesBufferPool(BYTESBUFFE_POOL_SIZE, BYTESBUFFER_SIZE); + // TODO: fix default value for latlng and change this. public static final double INVALID_LATLNG = 0f; @@ -116,4 +121,8 @@ public abstract class MediaItem extends MediaObject { public static BitmapPool getMicroThumbPool() { return sMicroThumbPool; } + + public static BytesBufferPool getBytesBufferPool() { + return sMicroThumbBufferPool; + } } |