Skip navigation links

Package com.android.ide.common.symbols

Symbols

See: Description

Package com.android.ide.common.symbols Description

Symbols

The Symbols package contains classes used for parsing and processing android resources and generating the R.java files.

The Symbol class is used to represent a single android resource by a resource type, a name, a java type and a value. A set of Symbols with unique type/name pairs can be represented by a SymbolTable.

Resource parsers

Various parsers in this package were introduced to enable resource parsing without the use of AAPT for libraries. They provide means to scan through the resource directory, parse XML files in search for declared resources and find non-XML files, such as drawables.

The parsers' flow is as follows:

Library resources are passed to a ResourceDirectoryParser. There the parser goes through each of the directories and takes different paths depending on the directories' names and their files' types:

  1. If we are in a values directory (directory name starts with a "values" prefix and is followed by optional qualifiers, like "-v21" or "-w820dp"), all files inside are XML files with declared values inside of them (for example values/strings.xml). Parse each file with a ResourceValuesXmlParser.
  2. If we are in a non-values directory, create a Symbol for each file inside the directory, with the Symbol's name as the filename without the optional extension and the Symbol's type as the directory's name without extra qualifiers. For example for file drawable-v21/a.png we would create a new Symbol with name "a" and type "drawable".
  3. Additionally, if we are in a non-values directory and are parsing a file that is an XML file, we will parse the contents of the file in search of inline declared values. For example, a file layout/activity_main.xml could contain an inline declaration of an id such as {code android:id="@+id/activity_main"}. From such a line a new Symbol should be created with a name "activity_main" and type "id". Such inline declarations are identified by the "@+" prefix and follow a "@+type/name" pattern. This is done by calling the parse method in ResourceExtraXmlParser

The ResourceDirectoryParser collects all Symbols from aforementioned cases and collects them in a SymbolTable which is later used to create the R.txt and R.java files for the library as well as R.java files for all the libraries it depends on.

It is worth mentioning that with this new flow, the new pipeline needs to also create minify rules in the aapt_rules.txt file since we are not calling AAPT anymore. It is done by parsing the library's android manifest, creating keep rules and writing the file in method SymbolUtils.generateMinifyKeepRules(com.android.ide.common.xml.ManifestData, java.io.File).

Naming conventions:

  1. Resource names declared in XML files inside the values directories are allowed to contain lower- and upper-case letters, numbers and the underscore character. Dots and colons are allowed to accommodate AAPT's old behaviour, but are deprecated and the support for them might end in the near future.
  2. File names are allowed to contain lower- and upper-case letters, numbers and the underscore character. A dot is only allowed to separate the name from the extension (for example "a.png"), the usage of two dots is only allowed for 9-patch image extension (for example "a.9.png"). It is also worth noting that some resources can be declared with a prefix like aapt: or android:. Following aapt's original behaviour, we strip the type names from those prefixes. This behaviour is deprecated and might be the support for it might end in the near future.

Example:

Assume in the resources directory we have the following sub-directories and files:

 +---.drawable
 |   +---.a.png
 +---.layout
 |   +---.activity_main.xml
 +---.values
 |   +---.colors.xml
 

Contents of activity_main,xml include a FloatingActionButton:

     (...)
     
     (...)
 

And colors.xml contains:

 
     #3F51B5
     #303F9F
     #FF4081
 
 

Then the parsers would create a following SymbolTable:

Symbol table
Java type Resource type Resource name ID
int drawable a 1
int layout activity_main 2
int id fab 3
int color colorPrimary 4
int color colorPrimaryDark 5
int color colorAccent 6

See ResourceValuesXmlParserTest and ResourceDirectoryParserTest for more examples of the parsers' behaviour.

Partial R files generation

Historically, processing resources using AAPT was a one-step process: the android gradle plugin merged the values files, handled overlays and placed all merged files into an intermediate directory, which was later passed to AAPT 'package' command.

Now, AAPT2 is used by default. Instead of one call to 'package', a two-step process is used: first during merging the resources are compiled by AAPT2 using the 'compile' command (this is where PNG crunching and pseudo-localization take place) and then all compiled resources are passed to AAPT2 for the 'link' command to create the AP_ file (APK file with the manifest and the resources only). R.txt and R.java files are generated at this point.

In oder to generate R.java earlier in the build process (and, in turn, to faster start java compilation tasks) the concept of partial R files was introduced. The idea is to generate a partial R.txt file for each resource during compilation and then merge them to generate the R.txt and R.java files before we reach the linking stage.

There will be two ways of generating the partial R.txt files:

  1. Non-XML files - they do not define any resources other than themselves, so we can add them to the non-XML R.txt file early and send off to compile in parallel (for example: PNG files).
  2. XML files - they can define other resources inside of them, so we need to compile them with AAPT2 and receive the partial R.txt using the '--output-text-symbols' flag.

The line format of the partial R.txt files will be: <access qualifier> <java type> <symbol type> <resource name>.

For example: default int[] styleable MyStyleable.

Access qualifiers can be one of: default, public, private.

For more information about java types and symbol types look at ResourceValuesXmlParser.

Resources defined with the public keyword will have the access qualifier public in the partial R.txt, resources defined using java-symbol will have the private access qualifier, and all other resources will have the default qualifier.

If a string is declared in values/strings.xml as <string name="s1">v1</string> then the partial R.txt will contain default int string s1. If the same string is also marked as public in the values/public.xml as: <public type="string" name="foo" id="0x7f000001"/> then the partial R.txt for that file will contain: public int string foo. If both are in the same file, then the partial R.txt will contain public int string foo as the resource will be already marked as public.

The resource IDs will be skipped as this is only for compilation, proper IDs will be generated at linking phase.

More information about resource accessibility can be found in the javadoc for ResourceAccessibility.

Partial R files merging

After the partial R files generation phase, we should have a set of partial R files for each source-set.

The merging strategies will depend on the type of the original resource:

  1. Non-Xml files - it should be fairly simple to merge non-xml-R.txt files, a simple sum of all the resources should be sufficient since none of those resources can define other resources inside of them.
  2. Non-values XML files - each of those files is a resource itself and can define ID resources inside itself. Therefore if, for example, a layout is overridden then the IDs defined in the original file need to be replaced by the ones from the new layout. Note: we can do the replacement at the original file level without looking at the file contents (if two files from different source-sets have the same name, choose the one from the 'overriding' source-set). Then the contents of those partial R files can be merged by a simple sum like in non-XML files.
  3. Values XML files - are a bit tricky, since the strategy is a mix of the two previous ones. For non-styleable resources the strategy is to simply sum all of the inputs, but for styleable resources (from declare-styleables and its' children) we need to use the 'newest' strategy. When a declare-styleable is overridden then all of its children are overridden by the new children as well. Also, if a resource is marked both as default and private (or public) then it should be de-duplicated and only exist as private (or public).

To make all of these cases work, the algorithm is as follows:

  1. Start with the highest-priority source-set (in this case the opposite of the base source-set) and start adding symbols from the partial files into the Symbol Table. After we're done with the current source-set, move on to an older one until we have nothing else left to do.
  2. Before adding resources from a non-values XML file, add the filename to a set of visited file names.
    1. If the filename wasn't present in the set yet, add all of the resources to the symbol table.
    2. If the filename was already present in the set, then the resource is overridden and we should ignore its contents.
  3. If a resource is already present in the table when we want to add it, compare the access modifiers of the two symbols (declare-styleable's children should be nested under the styleable's Symbol).
    1. If they are the same, ignore and move on.
    2. If one of them is default and the other one is private (or public), keep the non-default one.
    3. If one is public and the other private, abort with error. A resource cannot be both private and public.
  4. All other resource validation will be left to AAPT2 during linking stage.
  5. At the end we can grab all public resources from the table to generate the public.txt file, private resources for the private.txt and all of the resources regardless of the access modifier to generate the R.txt.
Skip navigation links