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