diff options
| -rw-r--r-- | core/java/android/util/Finalizers.java | 127 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 23 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 10 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 36 |
4 files changed, 171 insertions, 25 deletions
diff --git a/core/java/android/util/Finalizers.java b/core/java/android/util/Finalizers.java new file mode 100644 index 000000000000..671f2d496e63 --- /dev/null +++ b/core/java/android/util/Finalizers.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010 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 android.util; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * This class can be used to implement reliable finalizers. + * + * @hide + */ +public final class Finalizers { + private static final String LOG_TAG = "Finalizers"; + + private static final Object[] sLock = new Object[0]; + private static boolean sInit; + private static Reclaimer sReclaimer; + + /** + * Subclass of PhantomReference used to reclaim resources. + */ + public static abstract class ReclaimableReference<T> extends PhantomReference<T> { + public ReclaimableReference(T r, ReferenceQueue<Object> q) { + super(r, q); + } + + public abstract void reclaim(); + } + + /** + * Returns the queue used to reclaim ReclaimableReferences. + * + * @return A reference queue or null before initialization + */ + public static ReferenceQueue<Object> getQueue() { + synchronized (sLock) { + if (!sInit) { + return null; + } + if (!sReclaimer.isRunning()) { + sReclaimer = new Reclaimer(sReclaimer.mQueue); + sReclaimer.start(); + } + return sReclaimer.mQueue; + } + } + + /** + * Invoked by Zygote. Don't touch! + */ + public static void init() { + synchronized (sLock) { + if (!sInit && sReclaimer == null) { + sReclaimer = new Reclaimer(); + sReclaimer.start(); + sInit = true; + } + } + } + + private static class Reclaimer extends Thread { + ReferenceQueue<Object> mQueue; + + private volatile boolean mRunning = false; + + Reclaimer() { + this(new ReferenceQueue<Object>()); + } + + Reclaimer(ReferenceQueue<Object> queue) { + super("Reclaimer"); + setDaemon(true); + mQueue = queue; + } + + @Override + public void start() { + mRunning = true; + super.start(); + } + + boolean isRunning() { + return mRunning; + } + + @SuppressWarnings({"InfiniteLoopStatement"}) + @Override + public void run() { + try { + while (true) { + try { + cleanUp(mQueue.remove()); + } catch (InterruptedException e) { + // Ignore + } + } + } catch (Exception e) { + Log.e(LOG_TAG, "Reclaimer thread exiting: ", e); + } finally { + mRunning = false; + } + } + + private void cleanUp(Reference<?> reference) { + do { + reference.clear(); + ((ReclaimableReference<?>) reference).reclaim(); + } while ((reference = mQueue.poll()) != null); + } + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 7c644e41b946..735b35ace53c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1828,8 +1828,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int[] mDrawableState = null; - private SoftReference<Bitmap> mDrawingCache; - private SoftReference<Bitmap> mUnscaledDrawingCache; + private Bitmap mDrawingCache; + private Bitmap mUnscaledDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -6916,8 +6916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { buildDrawingCache(autoScale); } - return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : - (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); + return autoScale ? mDrawingCache : mUnscaledDrawingCache; } /** @@ -6932,13 +6931,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ public void destroyDrawingCache() { if (mDrawingCache != null) { - final Bitmap bitmap = mDrawingCache.get(); - if (bitmap != null) bitmap.recycle(); + mDrawingCache.recycle(); mDrawingCache = null; } if (mUnscaledDrawingCache != null) { - final Bitmap bitmap = mUnscaledDrawingCache.get(); - if (bitmap != null) bitmap.recycle(); + mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } } @@ -6999,8 +6996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? - (mDrawingCache == null || mDrawingCache.get() == null) : - (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { + mDrawingCache == null : mUnscaledDrawingCache == null)) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -7033,8 +7029,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } boolean clear = true; - Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : - (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); + Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache; if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { Bitmap.Config quality; @@ -7066,9 +7061,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility bitmap = Bitmap.createBitmap(width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if (autoScale) { - mDrawingCache = new SoftReference<Bitmap>(bitmap); + mDrawingCache = bitmap; } else { - mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); + mUnscaledDrawingCache = bitmap; } if (opaque && translucentWindow) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 59600dcdc455..5767832b58d0 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -24,6 +24,7 @@ import android.os.IBinder; import android.os.Process; import android.os.SystemProperties; import android.util.Config; +import android.util.Finalizers; import android.util.Log; import android.util.Slog; @@ -141,6 +142,12 @@ public class RuntimeInit { Debug.enableEmulatorTraceOutput(); } + /** + * Initialize the thread used to reclaim resources without + * going through finalizers. + */ + Finalizers.init(); + initialized = true; } @@ -331,9 +338,6 @@ public class RuntimeInit { } } - /** Counter used to prevent reentrancy in {@link #reportException}. */ - private static final AtomicInteger sInReportException = new AtomicInteger(); - /** * Set the object identifying this application/process, for reporting VM * errors. diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 537dd3aeb730..d9ee3ec700a8 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -19,12 +19,16 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; +import android.util.Finalizers; import java.io.OutputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; public final class Bitmap implements Parcelable { /** @@ -55,7 +59,7 @@ public final class Bitmap implements Parcelable { private static volatile Matrix sScaleMatrix; private static volatile int sDefaultDensity = -1; - + /** * For backwards compatibility, allows the app layer to change the default * density when running old apps. @@ -81,8 +85,7 @@ public final class Bitmap implements Parcelable { This can be called from JNI code. */ - private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, - int density) { + private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) { if (nativeBitmap == 0) { throw new RuntimeException("internal error: native bitmap is 0"); } @@ -94,6 +97,13 @@ public final class Bitmap implements Parcelable { if (density >= 0) { mDensity = density; } + + // If the finalizers queue is null, we are running in zygote and the + // bitmap will never be reclaimed, so we don't need to run our native + // destructor + if (Finalizers.getQueue() != null) { + new BitmapFinalizer(this); + } } /** @@ -1016,12 +1026,22 @@ public final class Bitmap implements Parcelable { nativePrepareToDraw(mNativeBitmap); } - @Override - protected void finalize() throws Throwable { - try { + private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> { + private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet( + new HashSet<BitmapFinalizer>()); + + private int mNativeBitmap; + + BitmapFinalizer(Bitmap b) { + super(b, Finalizers.getQueue()); + mNativeBitmap = b.mNativeBitmap; + sFinalizers.add(this); + } + + @Override + public void reclaim() { nativeDestructor(mNativeBitmap); - } finally { - super.finalize(); + sFinalizers.remove(this); } } |