• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(int native_object)97     public T getDelegate(int 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 int addNewDelegate(T newDelegate) {
120         int 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(int native_object)136     public void removeJavaReferenceFor(int 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