/* * 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. */ import java.lang.ref.ReferenceQueue; import java.lang.ref.PhantomReference; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; public class Bitmap { String mName; /* for debugging */ int mWidth, mHeight; Bitmap.NativeWrapper mNativeWrapper; private static int sSerial = 100; private static ArrayList sPhantomList = new ArrayList<PhantomWrapper>(); private static ReferenceQueue<PhantomWrapper> sPhantomQueue = new ReferenceQueue<PhantomWrapper>(); private static BitmapWatcher sWatcher = new BitmapWatcher(sPhantomQueue); static { sWatcher.setDaemon(true); sWatcher.start(); }; Bitmap(String name, int width, int height, Bitmap.NativeWrapper nativeData) { mName = name; mWidth = width; mHeight = height; mNativeWrapper = nativeData; System.out.println("Created " + this); } public String toString() { return "Bitmap " + mName + ": " + mWidth + "x" + mHeight + " (" + mNativeWrapper.mNativeData + ")"; } public void drawAt(int x, int y) { System.out.println("Drawing " + this); } public static void shutDown() { sWatcher.shutDown(); try { sWatcher.join(); } catch (InterruptedException ie) { System.out.println("join intr"); } System.out.println("Bitmap has shut down"); } /* * Pretend we're allocating native storage. Just returns a unique * serial number. */ static Bitmap.NativeWrapper allocNativeStorage(int width, int height) { int nativeData; synchronized (Bitmap.class) { nativeData = sSerial++; } Bitmap.NativeWrapper wrapper = new Bitmap.NativeWrapper(nativeData); PhantomWrapper phan = new PhantomWrapper(wrapper, sPhantomQueue, nativeData); sPhantomList.add(phan); wrapper.mPhantomWrapper = phan; return wrapper; } static void freeNativeStorage(int nativeDataPtr, CountDownLatch freeSignal) { System.out.println("freeNativeStorage: " + nativeDataPtr); // Wake up the main thread that is [or will be] blocked until this native data is freed. freeSignal.countDown(); } /* * Wraps a native data pointer in an object. When this object is no * longer referenced, we free the native data. */ static class NativeWrapper { public NativeWrapper(int nativeDataPtr) { mNativeData = nativeDataPtr; } public int mNativeData; // The PhantomWrapper corresponding to this NativeWrapper. public PhantomWrapper mPhantomWrapper; /* @Override protected void finalize() throws Throwable { System.out.println("finalized " + mNativeData); } */ } } /* * Keep an eye on the native data. * * We keep a copy of the native data pointer value, and set the wrapper * as our referent. We need the copy because you can't get the referred-to * object back out of a PhantomReference. */ class PhantomWrapper extends PhantomReference { PhantomWrapper(Bitmap.NativeWrapper wrapper, ReferenceQueue<PhantomWrapper> queue, int nativeDataPtr) { super(wrapper, queue); mNativeData = nativeDataPtr; } public int mNativeData; // This will be signaled once mNativeData has been freed. public CountDownLatch mFreeSignal = new CountDownLatch(1); } /* * Thread that watches for un-referenced bitmap data. */ class BitmapWatcher extends Thread { ReferenceQueue<PhantomWrapper> mQueue; BitmapWatcher(ReferenceQueue<PhantomWrapper> queue) { mQueue = queue; setName("Bitmap Watcher"); } public void run() { while (true) { try { PhantomWrapper ref = (PhantomWrapper) mQueue.remove(); //System.out.println("dequeued ref " + ref.mNativeData + // " - " + ref); Bitmap.freeNativeStorage(ref.mNativeData, ref.mFreeSignal); } catch (InterruptedException ie) { System.out.println("intr"); break; } } } public void shutDown() { interrupt(); } }