CleanupReference.java revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.content.common; 6 7import android.os.Handler; 8import android.os.Looper; 9import android.os.Message; 10 11import java.lang.ref.PhantomReference; 12import java.lang.ref.ReferenceQueue; 13import java.util.HashSet; 14import java.util.Set; 15 16/** 17 * Handles running cleanup tasks after an object has been GC'd. Cleanup tasks 18 * are always executed on the main thread. In general, classes should not have 19 * finalizers and likewise should not use this class for the same reasons. The 20 * exception is where public APIs exist that require native side resources to be 21 * cleaned up in response to java side GC of API objects. (Private/internal 22 * interfaces should always favor explicit resource releases / destroy() 23 * protocol for this rather than depend on GC to trigger native cleanup). 24 */ 25public class CleanupReference extends PhantomReference<Object> { 26 27 static { 28 // Bootstrap the cleanup trigger. 29 createGarbageCollectionDetector(); 30 } 31 32 /** 33 * Create a scratch object with a finalizer that triggers cleanup. 34 */ 35 private static void createGarbageCollectionDetector() { 36 new Object() { 37 @Override 38 protected void finalize() throws Throwable { 39 try { 40 Message.obtain(sHandler).sendToTarget(); 41 // Create a new detector since this one has now been GC'd. 42 createGarbageCollectionDetector(); 43 } finally { 44 super.finalize(); 45 } 46 } 47 }; 48 } 49 50 /** 51 * This {@link Handler} polls {@link #sRefs}, looking for cleanup tasks that 52 * are ready to run. 53 */ 54 private static Handler sHandler = new Handler(Looper.getMainLooper()) { 55 @Override 56 public void handleMessage(android.os.Message msg) { 57 TraceEvent.begin(); 58 CleanupReference ref = null; 59 while ((ref = (CleanupReference) sQueue.poll()) != null) { 60 ref.cleanupNow(); 61 ref.clear(); 62 } 63 TraceEvent.end(); 64 } 65 }; 66 67 private static ReferenceQueue<Object> sQueue = new ReferenceQueue<Object>(); 68 69 /** 70 * Keep a strong reference to {@link CleanupReference} so that it will 71 * actually get enqueued. 72 */ 73 private static Set<CleanupReference> sRefs = new HashSet<CleanupReference>(); 74 75 private Runnable mCleanupTask; 76 77 /** 78 * @param obj the object whose loss of reachability should trigger the 79 * cleanup task. 80 * @param cleanupTask the task to run once obj loses reachability. 81 */ 82 public CleanupReference(Object obj, Runnable cleanupTask) { 83 super(obj, sQueue); 84 mCleanupTask = cleanupTask; 85 sRefs.add(this); 86 // Proactively force a cleanup to catch anything that has been enqueued 87 // but is still waiting for the GC detector's finalizer to run. 88 // Note this could still run behind (if the main thread is consistently too 89 // busy to service messages) but we could investigate synchronously flushing the 90 // reference queue if this method is itself being called on the main thread. 91 Message.obtain(sHandler).sendToTarget(); 92 } 93 94 /** 95 * Clear the cleanup task {@link Runnable} so that nothing will be done 96 * after garbage collection. 97 */ 98 public void cleanupNow() { 99 if (mCleanupTask != null) { 100 mCleanupTask.run(); 101 mCleanupTask = null; 102 } 103 sRefs.remove(this); 104 } 105} 106