Android LayoutInflater和findViewById 源码详解
LayoutInflater大家很熟悉,简单点说就是布局文件XML解析器,setContentView函数也是调用了LayoutInflater
用法:
View view = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
LayoutInflater lyInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
上面的用法本质上都是一样.
控制台打印 重复调用LayoutInflater.from(this) 同一个进程下面 实例是单例
com.android.internal.policy.impl.PhoneLayoutInflater@420dcf80
com.android.internal.policy.impl.PhoneLayoutInflater@420dcf80
com.android.internal.policy.impl.PhoneLayoutInflater@420dcf80
首先 LayoutInflater 是一个抽象类,实例化对象是从Service取出的。但是并没有到底层Binder去实例化对象。最终实例对象是PhoneLayoutInflater类
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 LayoutInflater;
}
从Activity父类可以找到 getSystemService方法
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(mBase).cloneInContext(this); //在这个地方实例化的,主要关联到mBase Context对象实例
}
return mInflater;
}
return mBase.getSystemService(name);
}
重点是Context的实例化对象 有个子类 ContextImpl.java 里面注册了相关Service
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
SYSTEM_SERVICE_MAP 是个hashMap 注册的服务都存在这个地方,对象被缓存。
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String, ServiceFetcher>(); abstract static class StaticServiceFetcher extends ServiceFetcher {
private Object mCachedInstance; @Override
public final Object getService(ContextImpl unused) {
synchronized (StaticServiceFetcher.this) {
Object service = mCachedInstance;
if (service != null) {
return service;
}
return mCachedInstance = createStaticService();
}
}
public abstract Object createStaticService(); //这个用于创建static服务
} registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { //创建独立的对象 用于LayoutInflater
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
/*package*/ static class ServiceFetcher {
int mContextCacheIndex = -1;
/**
* Main entrypoint; only override if you don't need caching.
*/
public Object getService(ContextImpl ctx) {// ContextImpl实例。
ArrayList<Object> cache = ctx.mServiceCache;
Object service;
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 < sNextPerContextServiceCacheIndex; i++) {
cache.add(null);
}
} else {
service = cache.get(mContextCacheIndex);//获取缓存是否存在
if (service != null) {
return service;
}
}
service = createService(ctx); //用来缓存服务实例
cache.set(mContextCacheIndex, service);
return 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");
}
}
com.android.internal.policy.PolicyManager
找到了LayoutInflater的实现类 这个类算是个代理类 本质上是
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy"; policy\src\com\android\internal\policy\impl 目录下 public LayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context); //实例化
} policy\src\com\android\internal\policy\impl\PhoneLayoutInflater 回到LayoutInflater 递归解析XML 使用的是XMl Pull Parser解析器 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { }
rInflate开始递归 都是XML解析
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
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) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else {
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (finishInflate) parent.onFinishInflate();//解析完了,这个方法我们自定义可以复写 可以会去掉 子View的信息
}
基本上布局都是ViewGroup的子类 里面都有child view, 我们常用的findViewById 就是遍历去找对应ID的View
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
直接看ViewGroup中的findViewTraversal @Override
protected View findViewTraversal(int id) {
if (id == mID) { //等于自己
return this;
} final View[] where = mChildren;
final int len = mChildrenCount; for (int i = 0; i < len; i++) {
View v = where[i]; if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { //IS_ROOT_NAMESPACE 可能是查询到最后一层了(猜的) 然后再遍历 查询
v = v.findViewById(id); if (v != null) {
return v;
}
}
} return null;
}
如果布局层级太深 解析XML和查找View都是耗费CPU的,所以做项目过程中 有必要进行布局优化。
本文源码: 4.1
Android LayoutInflater和findViewById 源码详解的更多相关文章
- Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解
Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解 今天主要理一下StreamingContext的启动过程,其中最为重要的就是Jo ...
- spring事务详解(三)源码详解
系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...
- 条件随机场之CRF++源码详解-预测
这篇文章主要讲解CRF++实现预测的过程,预测的算法以及代码实现相对来说比较简单,所以这篇文章理解起来也会比上一篇条件随机场训练的内容要容易. 预测 上一篇条件随机场训练的源码详解中,有一个地方并没有 ...
- [转]Linux内核源码详解--iostat
Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...
- saltstack源码详解一
目录 初识源码流程 入口 1.grains.items 2.pillar.items 2/3: 是否可以用python脚本实现 总结pillar源码分析: @(python之路)[saltstack源 ...
- Shiro 登录认证源码详解
Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...
- udhcp源码详解(五) 之DHCP包--options字段
中间有很长一段时间没有更新udhcp源码详解的博客,主要是源码里的函数太多,不知道要不要一个一个讲下去,要知道讲DHCP的实现理论的话一篇博文也就可以大致的讲完,但实现的源码却要关心很多的问题,比如说 ...
- Activiti架构分析及源码详解
目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...
- 源码详解系列(六) ------ 全面讲解druid的使用和源码
简介 druid是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能,另外,druid还扩展 ...
随机推荐
- GNU 网络程序
______________________________________________________________________________ | 版权声明 | | 1.本文可以转载.修 ...
- [BZOJ 1816] [Cqoi2010] 扑克牌 【二分答案】
题目链接:BZOJ - 1816 题目分析 答案具有可以二分的性质,所以可以二分答案. 验证一个答案 x 是否可行,就累加一下各种牌相对于 x 还缺少的量,如果总和超过了 x 或 m ,就不可行. 因 ...
- unix io 模型浅析
POSIX中对同步IO和异步IO的规定: 同步IO操作:引起进程的阻塞直到IO操作完成,异步IO操作:IO操作不会引起进程阻塞 在UNIX下,有5中操作模型: 阻塞IO,非阻塞IO,IO复用,信号驱动 ...
- 【面试】惠普IT电面
1.英文自我介绍 一定要事先准备,逻辑要清晰 2.英文项目介绍
- 通过 IDE 向 Storm 集群远程提交 topology
转载: http://weyo.me/pages/techs/storm-topology-remote-submission/ http://www.javaworld.com/article/20 ...
- 【转】图说Android的8年演变史
原文网址:http://tech.gmw.cn/2016-02/17/content_18916338_14.htm 图说Android的8年演变史 我有话说 0 2011年10月谷歌 ...
- iOS应用架构浅谈
(整理至http://www.cocoachina.com/ios/20150414/11557.html) 缘由 从事iOS工作一年多了,主要从事QQ钱包SDK开发和财付通app维护,随着对业务的慢 ...
- 总结linux无线命令wpa
关于wpa_supplicant: 用到wpa_cli命令 wpa_cli -iwlan0 add_network // wlan0 是无线网络设备的名字,增加一个网络,会返回 ...
- oracle 表查询(1)
oracle 表基本查询 介绍在我们讲解的过程中我们利用scott 用户存在的几张表(emp,dept)为大家演示如何使用select语句,select 语句在软件编程中非常有用,希望大家好好的掌握. ...
- Contains Duplicate II ——LeetCode
Given an array of integers and an integer k, find out whether there there are two distinct indices i ...