Android compiled resources - resources.arsc
TL;DR: With the help of android asset packagin tool(aapt), xml nodes get translated to Java classes and the corresponding xml attributes get translated to numerical Ids. Android run-time works with these numeric ids to instantiate classes and create the views
TL;R
Run this command to dump the binary xmlaapt d xmltree apk_file_name res/layout/activity_main.xml
(aapt can be found in android-sdk-dir/build-tools/23.0.2/aapt.exe)
This will show the xml nodes (e.g. LinearLayout
, RelativeLayout
, etc) with their attributes(e.g. android:layout_width, android:layout_height
) and their values. Note that, the constants match_parent
(numeric value 0xffffffff
or -1
) or wrap_content
(numeric value 0xfffffffe
or -2
) can be seen there.
As a matter of fact, you can use this command on any other xml files in the apk e.g. AndroidManifest.xml or other layout files
The apk file is just a zip archive containing all the java class files(classes.dex
), all the compiled resource files and a file named resources.arsc
.
This resource.arsc
file contains all the meta-information about the resources. Some of those are...
- the xml nodes(e.g.
LinearLayout
,RelativeLayout
, etc), - the attributes(e.g.
android:layout_width
), - the resource
id
's.
The resource id
's refer to the real resources in the apk-file. The attributes are resolved to a value at runtime. The resolution process is smart about any re-direction (@dimen/...
as opposed to 4dp
or @color/...
as opposed to "#FFaabbcc"
) and returns a usable value(a dimen
value is resolved differently than a color
value).
Whats a compiled XML file:
A compiled XML file is just the same XML file with the resource references changed to their corresponding ids
. For example, a reference @string/ok
will be replaced by 0x7f000001
. Moreover, the attributes from android
namespace is changed to their respective integer values(e.g. wrap_content
is changed to 0xfffffffe
or -2
)
How Android resolves resources at runtime:
The methodinflater.inflate()
parses a compiled xml file and creates a view hierarchy by instantiating the xml nodes. Each of the xml nodes is instantiated by a java class(e.g. LinearLayout.java, RelativeLayout.java). To instantiate, the inflater parses the compiled xml file, collects all the attributes of a node and creates a packed structure of type AttributeSet
. This AttributeSet
is passed to the class constructor. The class constructor has the responsibility of walking the AttributeSet
and resolving each of the attribute values.
For example, for a layout containing RelativeLayout
, the inflater
will pack layout_width
and layout_height
into a AttributeSet
and pass it to the constructor
RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes).
In this case, some of the attributes are resolved by RelativeLayout.initFromAttributes(). The rest of the attributes are resolved by the parent ViewGroup.initFromAttributes(). The attribute android:id
of a view is just another attribute. After inflating, the inflater stores the id of each view by calling setId(id)
on that view after instantiation
Now to answer your question
R.id
is a java array and R.id.my_textview
is an integer in that array. The id
of the view my_textview
is this integer(starts with 0x7f). The method findViewById()
does a depth-first search on that view hierarchy to find the respective view.
Hope this helps. The link you provided in your question already answers how the ids are generated by aapt.
Its a wonderful system of managing resources for devices with multiple dimensions of variations. Moreover, the implementation is really fast !! With this as the foundation, it allows to implement higher level functionality(e.g. Runtime Resource Overlay)
LayoutInflater inflate view by using XML strings. XML strings compiled into resource file as you mentioned in your question.
Please check these code snippets of AOSP:
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
Resources.getLayout
loads XML resource parser
public XmlResourceParser getLayout(int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
XmlResourceParser loadXmlResourceParser(int id, String type)
throws NotFoundException {
synchronized (mAccessLock) {
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException(
"Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
getValue
uses AssetManager's getResourceValue and it calls loadResourceValue
native method. This native method calls ResTable
's getResource method to get XML strings stored in resource file.