summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/data
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2012-04-09 10:15:35 +0800
committerOwen Lin <owenlin@google.com>2012-04-10 14:39:23 +0800
commitcafd30f96355ad446943d60cd2595d08423944e2 (patch)
tree04f1473abb3d25eef6c36fe90bf1281120f89153 /src/com/android/gallery3d/data
parent8ef6c55bdad9a3e835ce56bdc98681434b4ac5b3 (diff)
downloadandroid_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.java96
-rw-r--r--src/com/android/gallery3d/data/BytesBufferPool.java91
-rw-r--r--src/com/android/gallery3d/data/ImageCacheRequest.java76
-rw-r--r--src/com/android/gallery3d/data/ImageCacheService.java39
-rw-r--r--src/com/android/gallery3d/data/MediaItem.java11
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;
+ }
}