From 1ba461c7b62373d24b289a3681eb14e3e9f38b24 Mon Sep 17 00:00:00 2001 From: Wei-Ta Chen Date: Fri, 29 May 2009 16:03:39 -0700 Subject: [PATCH] Add an API demo that shows the effets of setting Bitmaps as being purgeable. --- samples/ApiDemos/AndroidManifest.xml | 14 +++ .../apis/graphics/PurgeableBitmap.java | 118 ++++++++++++++++++ .../apis/graphics/PurgeableBitmapView.java | 116 +++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index d353d31d8..fb34afa9f 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1612,6 +1612,20 @@ + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java new file mode 100644 index 000000000..14ec8c417 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 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.example.android.apis.graphics; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; + +/** + * PurgeableBitmap demonstrates the effects of setting Bitmaps as being + * purgeable. + * + * In the NonPurgeable case, an encoded bitstream is decoded to a different + * Bitmap over and over again up to 200 times until out-of-memory occurs. + * In contrast, the Purgeable case shows that the system can complete decoding + * the encoded bitstream 200 times without hitting the out-of-memory case. + */ +public class PurgeableBitmap extends GraphicsActivity { + + private PurgeableBitmapView mView; + private final RefreshHandler mRedrawHandler = new RefreshHandler(); + + class RefreshHandler extends Handler { + + @Override + public void handleMessage(Message msg) { + int index = mView.update(this); + if (index > 0) { + showAlertDialog(getDialogMessage(true, index)); + } else if (index < 0){ + mView.invalidate(); + showAlertDialog(getDialogMessage(false, -index)); + } else { + mView.invalidate(); + } + } + + public void sleep(long delayMillis) { + this.removeMessages(0); + sendMessageDelayed(obtainMessage(0), delayMillis); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mView = new PurgeableBitmapView(this, detectIfPurgeableRequest()); + mRedrawHandler.sleep(0); + setContentView(mView); + } + + private boolean detectIfPurgeableRequest() { + PackageManager pm = getPackageManager(); + CharSequence labelSeq = null; + try { + ActivityInfo info = pm.getActivityInfo(this.getComponentName(), + PackageManager.GET_META_DATA); + labelSeq = info.loadLabel(pm); + } catch (NameNotFoundException e) { + e.printStackTrace(); + return false; + } + + String[] components = labelSeq.toString().split("/"); + if (components[components.length - 1].equals("Purgeable")) { + return true; + } else { + return false; + } + } + + private String getDialogMessage(boolean isOutOfMemory, int index) { + StringBuilder sb = new StringBuilder(); + if (isOutOfMemory) { + sb.append("Out of memery occurs when the "); + sb.append(index); + sb.append("th Bitmap is decoded."); + } else { + sb.append("Complete decoding ") + .append(index) + .append(" bitmaps without running out of memory."); + } + return sb.toString(); + } + + private void showAlertDialog(String message) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(message) + .setCancelable(false) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + +} diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java new file mode 100644 index 000000000..210562873 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 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.example.android.apis.graphics; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.BitmapFactory.Options; +import android.view.View; + +import java.io.ByteArrayOutputStream; + +/** + * PurgeableBitmapView works with PurgeableBitmap to demonstrate the effects of setting + * Bitmaps as being purgeable. + * + * PurgeableBitmapView decodes an encoded bitstream to a Bitmap each time update() + * is invoked(), and its onDraw() draws the Bitmap and a number to screen. + * The number is used to indicate the number of Bitmaps that has been decoded. + */ +public class PurgeableBitmapView extends View { + private final byte[] bitstream; + + private Bitmap mBitmap; + private final int mArraySize = 200; + private final Bitmap[] mBitmapArray = new Bitmap [mArraySize]; + private final Options mOptions = new Options(); + private static final int WIDTH = 150; + private static final int HEIGHT = 450; + private static final int STRIDE = 320; // must be >= WIDTH + private int mDecodingCount = 0; + private final Paint mPaint = new Paint(); + private final int textSize = 32; + private static int delay = 100; + + public PurgeableBitmapView(Context context, boolean isPurgeable) { + super(context); + setFocusable(true); + mOptions.inPurgeable = isPurgeable; + + int[] colors = createColors(); + Bitmap src = Bitmap.createBitmap(colors, 0, STRIDE, WIDTH, HEIGHT, + Bitmap.Config.ARGB_8888); + bitstream = generateBitstream(src, Bitmap.CompressFormat.JPEG, 80); + + mPaint.setTextSize(textSize); + mPaint.setColor(Color.GRAY); + } + + private int[] createColors() { + int[] colors = new int[STRIDE * HEIGHT]; + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + int r = x * 255 / (WIDTH - 1); + int g = y * 255 / (HEIGHT - 1); + int b = 255 - Math.min(r, g); + int a = Math.max(r, g); + colors[y * STRIDE + x] = (a << 24) | (r << 16) | (g << 8) | b; + } + } + return colors; + } + + public int update(PurgeableBitmap.RefreshHandler handler) { + try { + mBitmapArray[mDecodingCount] = BitmapFactory.decodeByteArray( + bitstream, 0, bitstream.length, mOptions); + mBitmap = mBitmapArray[mDecodingCount]; + mDecodingCount++; + if (mDecodingCount < mArraySize) { + handler.sleep(delay); + return 0; + } else { + return -mDecodingCount; + } + + } catch (OutOfMemoryError error) { + for (int i = 0; i < mDecodingCount; i++) { + mBitmapArray[i].recycle(); + } + return mDecodingCount + 1; + } + } + + @Override protected void onDraw(Canvas canvas) { + canvas.drawColor(Color.WHITE); + canvas.drawBitmap(mBitmap, 0, 0, null); + canvas.drawText(String.valueOf(mDecodingCount), WIDTH / 2 - 20, + HEIGHT / 2, mPaint); + } + + private byte[] generateBitstream(Bitmap src, Bitmap.CompressFormat format, + int quality) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + src.compress(format, quality, os); + return os.toByteArray(); + } + +}