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