| /* |
| * 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(); |
| } |
| } |