DelegateManager.java revision 9fe7fca9bcdceade9c654c6a8dcf0c48be16d78d
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.layoutlib.bridge.impl; 18 19import com.android.annotations.Nullable; 20import com.android.layoutlib.bridge.util.Debug; 21import com.android.layoutlib.bridge.util.SparseWeakArray; 22 23import android.util.SparseArray; 24 25import java.lang.ref.WeakReference; 26import java.util.ArrayList; 27import java.util.List; 28 29/** 30 * Manages native delegates. 31 * 32 * This is used in conjunction with layoublib_create: certain Android java classes are mere 33 * wrappers around a heavily native based implementation, and we need a way to run these classes 34 * in our Eclipse rendering framework without bringing all the native code from the Android 35 * platform. 36 * 37 * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their 38 * native methods by "delegate calls". 39 * 40 * For example, a native method android.graphics.Matrix.init(...) will actually become 41 * a call to android.graphics.Matrix_Delegate.init(...). 42 * 43 * The Android java classes that use native code uses an int (Java side) to reference native 44 * objects. This int is generally directly the pointer to the C structure counterpart. 45 * Typically a creation method will return such an int, and then this int will be passed later 46 * to a Java method to identify the C object to manipulate. 47 * 48 * Since we cannot use the Java object reference as the int directly, DelegateManager manages the 49 * int -> Delegate class link. 50 * 51 * Native methods usually always have the int as parameters. The first thing the delegate method 52 * will do is call {@link #getDelegate(long)} to get the Java object matching the int. 53 * 54 * Typical native init methods are returning a new int back to the Java class, so 55 * {@link #addNewDelegate(Object)} does the same. 56 * 57 * The JNI references are counted, so we do the same through a {@link WeakReference}. Because 58 * the Java object needs to count as a reference (even though it only holds an int), we use the 59 * following mechanism: 60 * 61 * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes 62 * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming 63 * the delegate. 64 * 65 * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a 66 * {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically 67 * when nothing references it. This means that any class that holds a delegate (except for the 68 * Java main class) must not use the int but the Delegate class instead. The integers must 69 * only be used in the API between the main Java class and the Delegate. 70 * 71 * @param <T> the delegate class to manage 72 */ 73public final class DelegateManager<T> { 74 @SuppressWarnings("FieldCanBeLocal") 75 private final Class<T> mClass; 76 private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); 77 /** list used to store delegates when their main object holds a reference to them. 78 * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed 79 * @see #addNewDelegate(Object) 80 * @see #removeJavaReferenceFor(long) 81 */ 82 private final List<T> mJavaReferences = new ArrayList<T>(); 83 private int mDelegateCounter = 0; 84 85 public DelegateManager(Class<T> theClass) { 86 mClass = theClass; 87 } 88 89 /** 90 * Returns the delegate from the given native int. 91 * <p> 92 * If the int is zero, then this will always return null. 93 * <p> 94 * If the int is non zero and the delegate is not found, this will throw an assert. 95 * 96 * @param native_object the native int. 97 * @return the delegate or null if not found. 98 */ 99 @Nullable 100 public T getDelegate(long native_object) { 101 if (native_object > 0) { 102 T delegate = mDelegates.get(native_object); 103 104 if (Debug.DEBUG) { 105 if (delegate == null) { 106 System.out.println("Unknown " + mClass.getSimpleName() + " with int " + 107 native_object); 108 } 109 } 110 111 assert delegate != null; 112 return delegate; 113 } 114 return null; 115 } 116 117 /** 118 * Adds a delegate to the manager and returns the native int used to identify it. 119 * @param newDelegate the delegate to add 120 * @return a unique native int to identify the delegate 121 */ 122 public long addNewDelegate(T newDelegate) { 123 long native_object = ++mDelegateCounter; 124 mDelegates.put(native_object, newDelegate); 125 assert !mJavaReferences.contains(newDelegate); 126 mJavaReferences.add(newDelegate); 127 128 if (Debug.DEBUG) { 129 System.out.println("New " + mClass.getSimpleName() + " with int " + native_object); 130 } 131 132 return native_object; 133 } 134 135 /** 136 * Removes the main reference on the given delegate. 137 * @param native_object the native integer representing the delegate. 138 */ 139 public void removeJavaReferenceFor(long native_object) { 140 T delegate = getDelegate(native_object); 141 142 if (Debug.DEBUG) { 143 System.out.println("Removing main Java ref on " + mClass.getSimpleName() + 144 " with int " + native_object); 145 } 146 147 mJavaReferences.remove(delegate); 148 } 149} 150