如何显示在Android的源代码打包成Android原理树的分支

侯 Sir说:&源码之下,了无秘密。& 但有些秘密还是要搞起来了看得更真切,仅从静态代码的体位很难体会到运动时的妙处。因此环境搭好了,下一步就是调试。gdbserver搭配gdb的调试环境走得很顺利,可是mmm出的C代码总是没有debugging symbol。在调研原因的过程中倒是熟悉了一些工具的用法,值得记录。
首先,关于mmm的代码,它是写在build/envsetup.sh中的函数,内部是对make的简单封装:指定要编译的模块,该函数会进入Android源码根目录,将指定模块的.mk文件传给make进行编译、链接。网上有对它的详细分析。
期初,我创建了external/helloworld,并在其下放了helloworld.c和Android.mk,执行
$ mmm external/helloworld
$ make snod
把生成的helloworld可执行文件打包到emulator的/system/bin里。当dbg提示(no debugging symbols found),我的第一反应是应该修改Android.mk文件,添加-g编译参数:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := helloworld
LOCAL_CFLAGS += -g -O0
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE)
可是没有任何效果,gdb还是那么提示。我怀疑是gdb的版本不匹配,我是用的是Mac平台,gdb是这么用的:
$ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-gdb
(gdb) file out/target/product/generic/system/bin/helloworld
Reading symbols from out/target/product/generic/system/bin/helloworld...(no debugging symbols found)...done.
怎么验证helloworld中是否存在调试符号表呢?能干这事的工具还挺多,我用了三个:
objdump工具,它可以用来反汇编binary文件,加参数-t显示symbol table:
$ ./prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9/bin/x86_64-linux-android-objdump -t out/target/product/generic/system/bin/helloworld
nm工具,它可以列出目标文件中的各种符号:
$ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-nm
out/target/product/generic/system/bin/helloworld
readelf工具,它可以解析elf格式的文件:
$ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-readelf -a out/target/product/generic/system/bin/helloworld
Linux下的工具真是好强大,而且在prebuilts/gcc下面都有对应的版本。不过这些强大的工具一致表明刚刚make出来的binary确实没有调试信息!
我又怀疑到自己的编译环境,难道是我的cc工具有问题?在.mk文件下添加如下输出,看$(CC)是啥:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
$(warning $(CC))
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := helloworld
LOCAL_CFLAGS += -O2 -fno-inline
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE)
输出的结果就是cc,再使用cc -g helloworld.c看生成的a.out文件是有调试信息的!
网上不少文章都提到不要在LOCAL_CFLAGS中加入优化/调试级别的参数:
Try not to change the optimization/debugging level in your Android.mk, this can be handled automatically for you by specifying the appropriate information in your Application.mk, and will let the NDK generate useful data files used during debugging.
这让我更加疑惑,这种纯C文件没有Application.mk怎么办呢?
晚上跟宋大侠一起review过程的时候,大侠关注到mmm最后有这么一段文字:
原来犯了个弱智错误,mmm说得很清楚:它同时生成了多个版本的helloworld,其中有原始版本,也有被阉割掉调试信息的strip版本,那么用于打包到system.img中的是不是阉割版?
赶快挨个验明正身:
$ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-nm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld
00003e90 d _DYNAMIC
00003fc8 d _GLOBAL_OFFSET_TABLE_
00000df8 t _Unwind_Complete
00000eac t _Unwind_DeleteException
00000e74 t _Unwind_GetLanguageSpecificData
00000e90 t _Unwind_GetRegionStart
00000c88 t _Unwind_RaiseException
00000dfc t _Unwind_Resume
t _Unwind_VRS_Get
............
果然是这样。所以不建议在LOCAL_CFLAGS中指定优化/调试等级。
阅读(...) 评论()&>&android 多级下拉菜单
android 多级下拉菜单
上传大小:198KB
android 实现多级下拉菜单,写的挺好的,转载的,思路很好,系统的拉伸空间拓展性不强
综合评分:4
{%username%}回复{%com_username%}{%time%}\
/*点击出现回复框*/
$(".respond_btn").on("click", function (e) {
$(this).parents(".rightLi").children(".respond_box").show();
e.stopPropagation();
$(".cancel_res").on("click", function (e) {
$(this).parents(".res_b").siblings(".res_area").val("");
$(this).parents(".respond_box").hide();
e.stopPropagation();
/*删除评论*/
$(".del_comment_c").on("click", function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_invalid/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parents(".conLi").remove();
alert(data.msg);
$(".res_btn").click(function (e) {
var parentWrap = $(this).parents(".respond_box"),
q = parentWrap.find(".form1").serializeArray(),
resStr = $.trim(parentWrap.find(".res_area_r").val());
console.log(q);
//var res_area_r = $.trim($(".res_area_r").val());
if (resStr == '') {
$(".res_text").css({color: "red"});
$.post("/index.php/comment/do_comment_reply/", q,
function (data) {
if (data.succ == 1) {
var $target,
evt = e || window.
$target = $(evt.target || evt.srcElement);
var $dd = $target.parents('dd');
var $wrapReply = $dd.find('.respond_box');
console.log($wrapReply);
//var mess = $(".res_area_r").val();
var mess = resS
var str = str.replace(/{%header%}/g, data.header)
.replace(/{%href%}/g, 'http://' + window.location.host + '/user/' + data.username)
.replace(/{%username%}/g, data.username)
.replace(/{%com_username%}/g, data.com_username)
.replace(/{%time%}/g, data.time)
.replace(/{%id%}/g, data.id)
.replace(/{%mess%}/g, mess);
$dd.after(str);
$(".respond_box").hide();
$(".res_area_r").val("");
$(".res_area").val("");
$wrapReply.hide();
alert(data.msg);
}, "json");
/*删除回复*/
$(".rightLi").on("click", '.del_comment_r', function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_comment_del/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parent().parent().parent().parent().parent().remove();
$(e.target).parents('.res_list').remove()
alert(data.msg);
//填充回复
function KeyP(v) {
var parentWrap = $(v).parents(".respond_box");
parentWrap.find(".res_area_r").val($.trim(parentWrap.find(".res_area").val()));
评论共有4条
不错的一个demo,现在我正学习这方面的,能参考下
要做多级下拉菜单的可以参考一下
不错!真的不错!
VIP会员动态
热门资源标签
CSDN下载频道资源及相关规则调整公告V11.10
下载频道用户反馈专区
下载频道积分规则调整V1710.18
spring mvc+mybatis+mysql+maven+bootstrap 整合实现增删查改简单实例.zip
资源所需积分/C币
当前拥有积分
当前拥有C币
输入下载码
为了良好体验,不建议使用迅雷下载
android 多级下拉菜单
会员到期时间:
剩余下载个数:
剩余积分:0
为了良好体验,不建议使用迅雷下载
积分不足!
资源所需积分/C币
当前拥有积分
您可以选择
程序员的必选
绿色安全资源
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
您的积分不足,将扣除 10 C币
为了良好体验,不建议使用迅雷下载
无法举报自己的资源
你当前的下载分为234。
你还不是VIP会员
开通VIP会员权限,免积分下载
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
若举报审核通过,可返还被扣除的积分
被举报人:
请选择类型
资源无法下载 ( 404页面、下载失败、资源本身问题)
资源无法使用 (文件损坏、内容缺失、题文不符)
侵犯版权资源 (侵犯公司或个人版权)
虚假资源 (恶意欺诈、刷分资源)
含色情、危害国家安全内容
含广告、木马病毒资源
*投诉人姓名:
*投诉人联系方式:
*版权证明:
*详细原因:
android 多级下拉菜单  在Android开发中为了inflate一个布局文件,大体有2种方式,如下所示:
// 1. get a instance of LayoutInflater, then do whatever you want
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 2. you're in some View class, then just call View's static inflate method
View.inflate(context, R.layout.xxx_xml, someViewGroup/null);
我们来看看这2种方式的具体源码:
&!-- View.java --&
* Inflate a view from an XML resource.
This convenience method wraps the {@link
* LayoutInflater} class, which provides a full range of options for view inflation.
* @param context The Context object for your activity or application.
* @param resource The resource ID to inflate
* @param root A view group that will be the parent.
Used to properly inflate the
* layout_* parameters.
* @see LayoutInflater
public static View inflate(Context context, int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
&!-- LayoutInflater.java --&
* Obtains the LayoutInflater from the given context.
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
return LayoutI
现在我们看到实质上都是方法1中的做法,View.inflate只是个helper方法而已(少敲几行代码)。那么我们就先来看看
Context.getSystemService的具体实现,这里我们直接去ContextImpl.java文件中的相关代码:
* Override this class when the system service constructor needs a
* ContextImpl.
Else, use StaticServiceFetcher below.
/*package*/ static class ServiceFetcher {
int mContextCacheIndex = -1;
* M only override if you don't need caching.
public Object getService(ContextImpl ctx) {
ArrayList&Object& cache = ctx.mServiceC
synchronized (cache) {
if (cache.size() == 0) {
// Initialize the cache vector on first access.
// At this point sNextPerContextServiceCacheIndex
// is the number of potential services that are
// cached per-Context.
for (int i = 0; i & sNextPerContextServiceCacheI i++) {
cache.add(null);
service = cache.get(mContextCacheIndex); // 先从cache中找,
if (service != null) { // 如果已经存在了直接返回
service = createService(ctx); // 否则创建并加入到cache中,只会调用1次
cache.set(mContextCacheIndex, service);
* Override this to create a new per-Context instance of the
* service.
getService() will handle locking and caching.
public Object createService(ContextImpl ctx) {
throw new RuntimeException("Not implemented");
* Override this class for services to be cached process-wide.
abstract static class StaticServiceFetcher extends ServiceFetcher {
private Object mCachedI
public final Object getService(ContextImpl unused) {
synchronized (StaticServiceFetcher.this) {
Object service = mCachedI
if (service != null) {
return mCachedInstance = createStaticService();
public abstract Object createStaticService(); // 它不需要ContextImpl参数
private static final HashMap&String, ServiceFetcher& SYSTEM_SERVICE_MAP =
new HashMap&String, ServiceFetcher&(); // 全局system service的map
private static int sNextPerContextServiceCacheIndex = 0;
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
SYSTEM_SERVICE_MAP.put(serviceName, fetcher); // 放到全局的静态map中
// 还有很多registerService的调用,这里都省略了,我们现在只关心LAYOUT_INFLATER_SERVICE
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) { // 我们前一篇文章中提到过会new一个
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); // PhoneLayoutInflater的对象返回
  到这里我们就清楚了Context.getSystemService方法的具体实现了,接下来我们将注意力转移到LayoutInflater类。关键代码如下:
* Obtains the LayoutInflater from the given context.
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
return LayoutI
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
* @param resource ID for an XML layout resource to load (e.g.,
&code&R.layout.main_page&/code&)
* @param root Optional view to be the parent of the generated hierarchy.
* @return The root View of the inflated hierarchy. If root was supplied,
this is the root V otherwise it is the root of the inflated
public View inflate(int resource, ViewGroup root) { // 实际上调用3个参数的版本,从这里我们可以看出客户端代码
return inflate(resource, root, root != null); // 没必要这样写(root!= null):inflate(resource, root, true);
* Inflate a new view hierarchy from the specified xml node. Throws
* {@link InflateException} if there is an error. *
* &em&&strong&Important&/strong&&/em&&&&For performance
* reasons, view inflation relies heavily on pre-processing of XML files
* that is done at build time. Therefore, it is not currently possible to
* use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
* @param parser XML dom node containing the description of the view
hierarchy.
* @param root Optional view to be the parent of the generated hierarchy.
* @return The root View of the inflated hierarchy. If root was supplied,
this is the root V otherwise it is the root of the inflated
public View inflate(XmlPullParser parser, ViewGroup root) { // 不太常用,我们一般使用layout文件的版本,但实质都一样
return inflate(parser, root, root != null);
// 下面的代码中inflate一个include tag时调用了此版本
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
* @param resource ID for an XML layout resource to load (e.g.,
&code&R.layout.main_page&/code&)
* @param root Optional view to be the parent of the generated hierarchy (if
&em&attachToRoot&/em& is true), or else simply an object that
provides a set of LayoutParams values for root of the returned
hierarchy (if &em&attachToRoot&/em& is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
the root parameter? If false, root is only used to create the
correct subclass of LayoutParams for the root view in the XML.
* @return The root View of the inflated hierarchy. If root was supplied and
attachToRoot is true, otherwise it is the root of
the inflated XML file.
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
* Inflate a new view hierarchy from the specified XML node. Throws
* {@link InflateException} if there is an error.
* &em&&strong&Important&/strong&&/em&&&&For performance
* reasons, view inflation relies heavily on pre-processing of XML files
* that is done at build time. Therefore, it is not currently possible to
* use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
* @param parser XML dom node containing the description of the view
hierarchy.
* @param root Optional view to be the parent of the generated hierarchy (if
&em&attachToRoot&/em& is true), or else simply an object that
provides a set of LayoutParams values for root of the returned
hierarchy (if &em&attachToRoot&/em& is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
the root parameter? If false, root is only used to create the
correct subclass of LayoutParams for the root view in the XML.
* @return The root View of the inflated hierarchy. If root was supplied and
attachToRoot is true, otherwise it is the root of
the inflated XML file.
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { // 这是最终调用的版本
synchronized (mConstructorArgs) { // 进入同步块
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mC
View result = // 此方法最后的返回值,初始化为传入的root
// Look for the root node.
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// 能走到这里,说明type是START_TAG 或 END_DOCUMENT
if (type != XmlPullParser.START_TAG) { // 如果一开始就是END_DOCUMENT,那说明xml文件有问题
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
// 能到这里,那type一定是START_TAG,也就是xml文件里的root node
final String name = parser.getName(); // 获得当前start tag的name
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
System.out.println("**************************");
if (TAG_MERGE.equals(name)) { // 处理merge tag的情况
if (root == null || !attachToRoot) { // root必须非空且attachToRoot为true,否则抛异常结束
throw new InflateException("&merge /& can be used only with a valid "
+ "ViewGroup root and attachToRoot=true"); // 因为merge的xml并不代表某个具体的view,只是将它
// 包起来的其他xml的内容加到某个上层ViewGroup中
rInflate(parser, root, attrs, false); // 递归的inflate
// Temp is the root view that was found in the xml
V // xml文件中的root view
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
temp = createViewFromTag(root, name, attrs); // 根据tag节点创建view对象
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs); // 根据root生成合适的LayoutParams实例
if (!attachToRoot) { // 如果不attach的话就调用view的setLayoutParams方法
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
if (DEBUG) {
System.out.println("-----& start inflating children");
// Inflate all children under temp
rInflate(parser, temp, attrs, true); // 递归inflate剩下的所有children
if (DEBUG) {
System.out.println("-----& done inflating children");
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) { // root非空且指定了要attachToRoot
root.addView(temp, params); // 将xml文件的root view 加到用户提供的root里
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = // 否则我们将返回xml里发现的root view:temp,而不是方法中传递进来的root对象
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastC
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return // 返回参数root或xml文件里的root view
接下来我们看看inflate各种不同节点的方法:
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException { // 深度优先inflate,所有才能保证你在onFinish
// Inflate()里可以通过findViewById找到已经创建完毕的孩子view
final int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() & depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
// 确保是一个START_TAG node
final String name = parser.getName(); // 拿到tagName
if (TAG_REQUEST_FOCUS.equals(name)) { // 处理REQUEST_FOCUS tag
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) { // 处理include tag
if (parser.getDepth() == 0) { // include节点不能是根节点,否则就抛异常了。。。
throw new InflateException("&include /& cannot be the root element");
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) { // merge节点必须是xml文件里的根节点,也就是说到这里的时候不应该再出现merge节点了
throw new InflateException("&merge /& must be the root element");
} else if (TAG_1995.equals(name)) {
final View view = new BlinkLayout(mContext, attrs);
final ViewGroup viewGroup = (ViewGroup)
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else { // 一般情况,各种Android view、widget或用户自定义的view节点
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup)
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
if (finishInflate) parent.onFinishInflate(); // parent的所有孩子节点都inflate完毕的时候,调用onFinishInflate回调
private void parseRequestFocus(XmlPullParser parser, View parent)
throws XmlPullParserException, IOException {
parent.requestFocus();
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || // 忽略此节点剩下的所有内容,直到下一个新的START_TAG
parser.getDepth() & currentDepth) && type != XmlPullParser.END_DOCUMENT) {
private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
throws XmlPullParserException, IOException {
if (parent instanceof ViewGroup) {
final int layout = attrs.getAttributeResourceValue(null, "layout", 0); // include节点中必须指定layout属性的值
if (layout == 0) {
final String value = attrs.getAttributeValue(null, "layout");
if (value == null) {
throw new InflateException("You must specifiy a layout in the"
+ " include tag: &include layout=\"@layout/layoutID\" /&");
throw new InflateException("You must specifiy a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
final XmlResourceParser childParser =
getContext().getResources().getLayout(layout);
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
throw new InflateException(childParser.getPositionDescription() +
": No start tag found!");
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) { // 处理include xml里包含merge的情况
// Inflate all children.
rInflate(childParser, parent, childAttrs, false);
} else { // 处理一般的include layout文件,创建此xml文件的root view
final View view = createViewFromTag(parent, childName, childAttrs);
final ViewGroup group = (ViewGroup)
// We try to load the layout params set in the &include /& tag. If
// they don't exist, we will rely on the layout params set in the
// included XML file.
// During a layoutparams generation, a runtime exception is thrown
// if either layout_width or layout_height is missing. We catch
// this exception and set localParams accordingly: true means we
// successfully loaded layout params from the &include /& tag,
// false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
params = group.generateLayoutParams(attrs);
} catch (RuntimeException e) {
params = group.generateLayoutParams(childAttrs);
} finally {
if (params != null) {
view.setLayoutParams(params); // 设置其layoutParams
// Inflate all children.
rInflate(childParser, view, childAttrs, true); // 递归inflate剩下的节点
// Attempt to override the included layout's android:id with the
// one set on the &include /& tag itself.
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.View, 0, 0);
int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
// While we're at it, let's try to override android:visibility.
int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
a.recycle();
if (id != View.NO_ID) {
view.setId(id); // override id,如果include节点提供了
switch (visibility) { // 同样的,override visibility,如果include节点提供了
view.setVisibility(View.VISIBLE);
view.setVisibility(View.INVISIBLE);
view.setVisibility(View.GONE);
group.addView(view); // 将include的xml文件里的root view加到上层group中
} finally {
childParser.close();
} else { // include节点必须是某个ViewGroup的子节点
throw new InflateException("&include /& can only be used inside of a ViewGroup");
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || // skip掉include节点剩下的内容
parser.getDepth() & currentDepth) && type != XmlPullParser.END_DOCUMENT) {
  最后我们看看根据节点创建对应View的相关方法:
* default visibility so the BridgeInflater can override it.
View createViewFromTag(View parent, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
if (DEBUG) System.out.println("******** Creating view: " + name);
// 这里我们忽略掉了各种factory的onCreateView,有兴趣的读者可自行研究
if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
else view = null;
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
if (view == null) {
if (-1 == name.indexOf('.')) { // 创建android.view.*里的任何view,如TextView,ImageView等等
view = onCreateView(parent, name, attrs); // 其子类PhoneLayoutInflater override了此方法用来
// 创建android.widget.*/android.webkit.*里的任何对象
view = createView(name, null, attrs); // 创建用户自定义的各种View(如com.xiaoweiz.browser.MyCustomView)
if (DEBUG) System.out.println("Created view is: " + view);
} catch (InflateException e) {
} catch (ClassNotFoundException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
* Low-level function for instantiating a view by name. This attempts to
* instantiate a view class of the given &var&name&/var& found in this
* LayoutInflater's ClassLoader.
* There are two things that can happen in an error case: either the
* exception describing the error will be thrown, or a null will be
* returned. You must deal with both possibilities -- the former will happen
* the first time createView() is called for a class of a particular name,
* the latter every time there-after for that class name.
* @param name The full name of the class to be instantiated.
* @param attrs The XML attributes supplied for this instance.
* @return View The newly instantiated view, or null.
public final View createView(String name, String prefix, AttributeSet attrs) // 用户自定义的view不需要prefix,因为
throws ClassNotFoundException, InflateException { // name中已经有所有需要的信息了;系统的prefix则是android.view.
Constructor&? extends View& constructor = sConstructorMap.get(name); // 或android.widget. 或 android.webkit.
Class&? extends View& clazz = null;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass( // 加载class文件
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
constructor = clazz.getConstructor(mConstructorSignature); // 拿到此类型的ctor
sConstructorMap.put(name, constructor);
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
Object[] args = mConstructorA // 需要2个参数Context,AttributeSet的版本,所以如果你不打算动态inflate
// 你的view,则没必要提供此版本的ctor。
final View view = constructor.newInstance(args); // new一个View(可能是其子类)的对象,可能为null
if (view instanceof ViewStub) { // ViewStub的特殊处理
// always use ourselves when inflating ViewStub later
final ViewStub viewStub = (ViewStub)
viewStub.setLayoutInflater(this);
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (prefix != null ? (prefix + name) : name));
ie.initCause(e);
} catch (ClassCastException e) {
// If loaded class is not a View subclass
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Class is not a View "
+ (prefix != null ? (prefix + name) : name));
ie.initCause(e);
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (clazz == null ? "&unknown&" : clazz.getName()));
ie.initCause(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  至此我们已经将LayoutInflater.inflate的关键代码分析完毕了。
阅读(...) 评论()

我要回帖

更多关于 Android系统源代码情景分析 的文章

 

随机推荐