Home | History | Annotate | only in /frameworks/base/tools/layoutlib/create
Up to higher level directory
NameDateSize
.classpath23-Apr-2015526
.project23-Apr-2015375
.settings/23-Apr-2015
Android.mk23-Apr-2015936
create.iml23-Apr-2015883
manifest.txt23-Apr-201552
README.txt23-Apr-201512.4K
src/23-Apr-2015
tests/23-Apr-2015

README.txt

      1 # Copyright (C) 2008 The Android Open Source Project
      2 
      3 
      4 - Description -
      5 ---------------
      6 
      7 Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform
      8 layout.
      9 
     10 
     11 - Usage -
     12 ---------
     13 
     14  ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar
     15 
     16 
     17 - Design Overview -
     18 -------------------
     19 
     20 Layoutlib_create uses a few jars from the framework containing the Java code used by Android as
     21 generated by the Android build, right before the classes are converted to a DEX format.
     22 
     23 These jars can't be used directly in Eclipse as:
     24 - they contains references to native code (which we want to avoid in Eclipse),
     25 - some classes need to be overridden, for example all the drawing code that is replaced by Java 2D
     26   calls in Eclipse.
     27 - some of the classes that need to be changed are final and/or we need access to their private
     28   internal state.
     29 
     30 Consequently this tool:
     31 - parses the input JAR,
     32 - modifies some of the classes directly using some bytecode manipulation,
     33 - filters some packages and removes those we don't want in the output JAR,
     34 - injects some new classes,
     35 - generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform
     36   rendering.
     37 
     38 The ASM library is used to do the bytecode modification using its visitor pattern API.
     39 
     40 The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
     41 is done in the main() method and the CreateInfo structure is expected to change with the Android
     42 platform as new classes are added, changed or removed. Some configuration that may be platform
     43 dependent is also present elsewhere in code.
     44 
     45 The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
     46 provides all the necessary missing implementation for rendering graphics in Eclipse.
     47 
     48 
     49 
     50 - Implementation Notes -
     51 ------------------------
     52 
     53 The tool works in two phases:
     54 - first analyze the input jar (AsmAnalyzer class)
     55 - then generate the output jar (AsmGenerator class),
     56 
     57 
     58 - Analyzer
     59 ----------
     60 
     61 The goal of the analyzer is to create a graph of all the classes from the input JAR with their
     62 dependencies and then only keep the ones we want.
     63 
     64 To do that, the analyzer is created with a list of base classes to keep -- everything that derives
     65 from these is kept. Currently the one such class is android.view.View: since we want to render
     66 layouts, anything that is sort of a view needs to be kept.
     67 
     68 The analyzer is also given a list of class names to keep in the output. This is done using
     69 shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**"
     70 ("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
     71 almost but not quite request the inclusion of full packages.
     72 
     73 The analyzer is also given a list of classes to exclude. A fake implementation of these classes is
     74 injected by the Generator.
     75 
     76 With this information, the analyzer parses the input zip to find all the classes. All classes
     77 deriving from the requested bases classes are kept. All classes whose name match the glob pattern
     78 are kept. The analysis then finds all the dependencies of the classes that are to be kept using an
     79 ASM visitor on the class, the field types, the method types and annotations types. Classes that
     80 belong to the current JRE are excluded.
     81 
     82 The output of the analyzer is a set of ASM ClassReader instances which are then fed to the
     83 generator.
     84 
     85 
     86 - Generator
     87 -----------
     88 
     89 The generator is constructed from a CreateInfo struct that acts as a config file and lists:
     90 - the classes to inject in the output JAR -- these classes are directly implemented in
     91   layoutlib_create and will be used to interface with the renderer in Eclipse.
     92 - specific methods to override (see method stubs details below).
     93 - specific methods for which to delegate calls.
     94 - specific methods to remove based on their return type.
     95 - specific classes to rename.
     96 - specific classes to refactor.
     97 
     98 Each of these are specific strategies we use to be able to modify the Android code to fit within the
     99 Eclipse renderer. These strategies are explained below.
    100 
    101 The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
    102 to produce a byte array suitable for the final JAR file.
    103 
    104 The first step of the transformation is to implement the method delegates.
    105 
    106 The TransformClassAdapter is then used to process the potentially renamed class.  All protected or
    107 private classes are market as public. All classes are made non-final. Interfaces are left as-is.
    108 
    109 If a method has a return type that must be erased, the whole method is skipped.  Methods are also
    110 changed from protected/private to public. The code of the methods is then kept as-is, except for
    111 native methods which are replaced by a stub. Methods that are to be overridden are also replaced by
    112 a stub.
    113 
    114 Finally fields are also visited and changed from protected/private to public.
    115 
    116 The next step of the transformation is changing the name of the class in case we requested the class
    117 to be renamed. This uses the RenameClassAdapter to also rename all inner classes and references in
    118 methods and types. Note that other classes are not transformed and keep referencing the original
    119 name.
    120 
    121 The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the
    122 references in all classes. This is used to update the references of classes in the java package that
    123 were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are
    124 modified to update all references to these non-desktop classes. An alternate implementation of
    125 these (com.android.tools.layoutlib.java.*) is injected.
    126 
    127 RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the
    128 class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the
    129 class was originally compiled with Java 7 (version 51). This is because we don't currently generate
    130 the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have
    131 valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
    132 Mac has horrible font rendering support.
    133 
    134 ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
    135 DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
    136 changes the calls to a method in each class instead of changing the implementation of the method.
    137 This is useful for methods in the Java namespace where we cannot add delegates. The configuration
    138 for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter.
    139 
    140 The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
    141 Transformation chains in the asm user guide, link in the References.) The order of execution of
    142 these is:
    143 ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
    144 RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter
    145 
    146 - Method stubs
    147 --------------
    148 
    149 As indicated above, all native and overridden methods are replaced by a stub.  We don't have the
    150 code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the
    151 method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register
    152 listeners from these overridden method calls based on the method signatures.
    153 
    154 The listeners are currently pretty basic: we only pass the signature of the method being called, its
    155 caller object and a flag indicating whether the method was native. We do not currently provide the
    156 parameters. The listener can however specify the return value of the overridden method.
    157 
    158 This strategy is now obsolete and replaced by the method delegates.
    159 
    160 
    161 - Strategies
    162 ------------
    163 
    164 We currently have 6 strategies to deal with overriding the rendering code and make it run in
    165 Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse)
    166 and the generator.
    167 
    168 
    169 1- Class Injection
    170 
    171 This is the easiest: we currently inject the following classes:
    172 - OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to
    173   some specific methods that are stubbed out and change their return value.
    174 - CreateInfo class, which configured the generator. Not used yet, but could in theory help us track
    175   what the generator changed.
    176 - AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are
    177   injected. The implementation for these classes has been taken from Android's libcore
    178   (platform/libcore/luni/src/main/java/java/...).
    179 - Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are
    180   added to the Dalvik VM for performance reasons. An implementation that is very close to the
    181   original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees
    182   were in part of the java package, where we can't inject classes, all references to these have been
    183   updated (See strategy 4- Refactoring Classes).
    184 
    185 
    186 2- Overriding methods
    187 
    188 As explained earlier, the creator doesn't have any replacement code for methods to override. Instead
    189 it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The
    190 bridge then registers a listener on the method signature and can provide an implementation.
    191 
    192 This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method
    193 Delegates).
    194 
    195 
    196 3- Renaming classes
    197 
    198 This simply changes the name of a class in its definition, as well as all its references in internal
    199 inner classes and methods. Calls from other classes are not modified -- they keep referencing the
    200 original class name. This allows the bridge to literally replace an implementation.
    201 
    202 An example will make this easier: android.graphics.Paint is the main drawing class that we need to
    203 replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own
    204 replacement version of Paint which will be used by the rest of the Android stack. The replacement
    205 version of Paint can still use (either by inheritance or delegation) all the original non-native
    206 code of _original_Paint if it so desires.
    207 
    208 Some of the Android classes are basically wrappers over native objects and since we don't have the
    209 native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't
    210 work as some native methods are static and we don't control object creation.
    211 
    212 This won't rename/replace the inner static methods of a given class.
    213 
    214 
    215 4- Refactoring classes
    216 
    217 This is very similar to the Renaming classes except that it also updates the reference in all
    218 classes. This is done for classes which are added to the Dalvik VM for performance reasons but are
    219 not present in the Desktop VM. An implementation for these classes is also injected.
    220 
    221 
    222 5- Method erasure based on return type
    223 
    224 This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some
    225 inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and
    226 all the original implementation is native.
    227 
    228 In this case we have a strategy that tells the generator that anything returning, for example, the
    229 inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own
    230 implementation.
    231 
    232 
    233 6- Method Delegates
    234 
    235 This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1
    236 or 2 methods are generated:
    237 a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original
    238 method as-is from the reader. This step is omitted if the method is native, since it has no Java
    239 implementation.
    240 b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method
    241 named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in
    242 layoutlib_bridge.
    243 
    244 The delegate method is a static method. If the original method is non-static, the delegate method
    245 receives the original 'this' as its first argument. If the original method is an inner non-static
    246 method, it also receives the inner 'this' as the second argument.
    247 
    248 
    249 
    250 - References -
    251 --------------
    252 
    253 
    254 The JVM Specification 2nd edition:
    255   http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
    256 
    257 Understanding bytecode:
    258   http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
    259 
    260 Bytecode opcode list:
    261   http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
    262 
    263 ASM user guide:
    264   http://download.forge.objectweb.org/asm/asm4-guide.pdf
    265 
    266 
    267 --
    268 end
    269