Home | History | Annotate | Download | only in impl
      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 
     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      */
     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      */
    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      */
    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