我们知道,在Activity#setContentView()中会调用PhoneWindow#setContentView()。而在PhoneWindow#setContentView()中有这么一句mLayoutInflater.inflate(layoutResID, mContentParent)。这行代码的作用是将我们的activity_main.xml填充到mContentParent中去。详见:setContentView源码解析。在写adapter的时候,也经常写mInflater.inflate(layoutResID, null)。那么,这行代码怎么就将xml文件转换成了View或者ViewGroup了呢?

获取LayoutInflater对象无非以下两种方式:

LayoutInflater.from(Context context);

LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

其实这俩是同一种方式,首先看下LayoutInflater#from()

源码位置:frameworks/base/core/java/android/view/LayoutInflater.java

LayoutInflater#from()

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;

}

第一种获取LayoutInflater对象的方式,不过就是对第二种方式的一个简单封装。实际上还是一回事。Context的实现类是ContextImpl,跟进。

源码位置:frameworks/base/core/java/android/app/ContextImpl.java

ContextImpl#getSystemService()

@Override

public Object getSystemService(String name) {

return SystemServiceRegistry.getSystemService(this, name);

}

跟进。

源码位置:frameworks/base/core/java/android/app/SystemServiceRegistry.java

SystemServiceRegistry#getSystemService()

public static Object getSystemService(ContextImpl ctx, String name) {

ServiceFetcher> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);

return fetcher != null ? fetcher.getService(ctx) : null;

}

直接从全局变量SYSTEM_SERVICE_FETCHERS中依据名字就get到了fetcher,之后依据fetcher直接get到了LayoutInflater对象。大写的懵B~原来啊,在SystemServiceRegistry中有个静态代码块,先看下这部分。

static {

...

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,

new CachedServiceFetcher() {

@Override

public LayoutInflater createService(ContextImpl ctx) {

return new PhoneLayoutInflater(ctx.getOuterContext());

}});

...

}

private static void registerService(String serviceName, Class serviceClass,

ServiceFetcher serviceFetcher) {

SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);

SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);

}

static abstract class CachedServiceFetcher implements ServiceFetcher {

private final int mCacheIndex;

public CachedServiceFetcher() {

mCacheIndex = sServiceCacheSize++;

}

@Override

@SuppressWarnings("unchecked")

public final T getService(ContextImpl ctx) {

final Object[] cache = ctx.mServiceCache;

synchronized (cache) {

// Fetch or create the service.

Object service = cache[mCacheIndex];

if (service == null) {

service = createService(ctx);

cache[mCacheIndex] = service;

}

return (T)service;

}

}

public abstract T createService(ContextImpl ctx);

}

这里连续贴了两个方法和一个抽象内部类CachedServiceFetcher。由于在抽象方法CachedServiceFetcher#createService()的具体实现中返回的是PhoneLayoutInflater,所以后文中使用的一直是PhoneLayoutInflater的对象。获取LayoutInflater对象(其实是其子类PhoneLayoutInflater对象)之后,调用LayoutInflater#inflate()。跟进。

源码位置:frameworks/base/core/java/android/view/LayoutInflater.java

LayoutInflater#inflate()

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

return inflate(resource, root, root != null);

}

这里以setContentView中的mLayoutInflater.inflate(layoutResID, mContentParent)为例,顺带也会讲解adapter中mInflater.inflate(layoutResID,null)这种情况。也就是root参数为null和不为null两种情况。root==null,则第三个参数为false.root!=null,则第三个参数为true。跟进。

LayoutInflater#inflate()

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

final Resources res = getContext().getResources();

final XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot);

} finally {

parser.close();

}

}

跟进。

LayoutInflater#inflate()

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {

synchronized (mConstructorArgs) {

...

View result = root;

try {

...

// 获取根节点的字符串,例如LinearLayout

final String name = parser.getName();

// 根节点merge开头

if (TAG_MERGE.equals(name)) {

...

} else {

// 创建根视图View

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = null;

if (root != null) {

// 获取LayoutParams

params = root.generateLayoutParams(attrs);

if (!attachToRoot) {

// 应用LayoutParams到根节点View

temp.setLayoutParams(params);

}

}

// 遍历解析子View,并添加到根节点temp中

rInflateChildren(parser, temp, attrs, true);

// root不为空,直接将根节点View添加到root中

if (root != null && attachToRoot) {

root.addView(temp, params);

}

// root等于null,直接返回根节点temp

if (root == null || !attachToRoot) {

result = temp;

}

}

}catch (Exception e) {

...

}

return result;

}

}

上面每一步都有注释,下面重点看下生成根节点View的createViewFromTag()和遍历生成子View的rInflateChildren()方法。

LayoutInflater#createViewFromTag()

private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {

return createViewFromTag(parent, name, context, attrs, false);

}

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,

boolean ignoreThemeAttr) {

...

if (-1 == name.indexOf('.')) {

view = onCreateView(parent, name, attrs);

} else {

view = createView(name, null, attrs);

}

...

return view;

}

跟进。

LayoutInflater#createView()

public final View createView(String name, String prefix, AttributeSet attrs)

throws ClassNotFoundException, InflateException {

Constructor extends View> constructor = sConstructorMap.get(name);

Class extends View> clazz = null;

try {

if (constructor == null) {

clazz = mContext.getClassLoader().loadClass(

prefix != null ? (prefix + name) : name).asSubclass(View.class);

...

constructor = clazz.getConstructor(mConstructorSignature);

constructor.setAccessible(true);

sConstructorMap.put(name, constructor);

} else {

...

}

final View view = constructor.newInstance(args);

return view;

} catch (Exception e) {

...

}

}

sConstructorMap是个HashMap>对象。首先依据根节点的名字,例如LinearLayout去查找缓存的构造器,如果是第一次执行,肯定返回null。如果返回为null,则通过反射出构造方法,并强制设置可访问,之后存进sConstructorMap中。如果缓存中有构造器,那么直接取出。最后调用newInstance反射出根节点View实例。得到根节点View实例之后,接着设置属性,最后调用rInflateChildren()遍历创建子View。跟进。

LayoutInflater#rInflateChildren()

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,

boolean finishInflate) throws XmlPullParserException, IOException {

rInflate(parser, parent, parent.getContext(), attrs, finishInflate);

}

parent参数是根节点View。这里只是简单转发给rInflate()方法处理。跟进。

LayoutInflater#rInflateChildren()

void rInflate(XmlPullParser parser, View parent, Context context,

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)) {

...

}

...

} else {

final View view = createViewFromTag(parent, name, context, attrs);

final ViewGroup viewGroup = (ViewGroup) parent;

final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);

rInflateChildren(parser, view, attrs, true);

viewGroup.addView(view, params);

}

}

if (finishInflate) {

parent.onFinishInflate();

}

}

遍历体现在While循环上,name为子节点View的名称,例如:TextView,RelativeLayout等。几个以tag、include等开头的子节点走最上面几个if的逻辑,我们的重点在于寻常View走的else逻辑。可以看到:首先,和创建根节点View调用同一个方法createViewFromTag()创建子View,紧接着设置子View的参数,然后调用递归调用rInflateChildren()方法再去测量子节点的所有View,最后才将子节点添加到父布局,这个父布局可能是根节点,也可能是某个子节点。遍历结束之后,所有子View也添加到布局当中并设置好相应的布局参数。

至此,LayoutInflater.from().inflate()源码解析结束~

更多Framework源码解析,请移步 Framework源码解析系列[目录]

android service layoutinflater,Android LayoutInflater.from().inflate()源码解析相关推荐

  1. Android多页蒙版遮罩引导功能(源码+解析)

    #Android多页蒙版遮罩引导功能(源码+解析) 需求:博主前段时间做的教育类型APP,需要引导用户(低龄化小朋友),播放器的播放,页面可以左右滑动,以及右上方进入答题卡入口(小朋友都是很聪明的,引 ...

  2. Android View体系(五)从源码解析View的事件分发机制

    Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Sc ...

  3. Android View体系(六)从源码解析Activity的构成

    前言 本来这篇是要讲View的工作流程的,View的工作流程主要指的measure.layout.draw这三大流程,在讲到这三大流程之前我们有必要要先了解下Activity的构成,所以就有了这篇文章 ...

  4. Android之图片加载框架Picasso源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  5. android资源加载流程6,FrameWork源码解析(6)-AssetManager加载资源过程

    之前一段时间项目比较忙所以一直没有更新,接下来准备把插件化系列的文章写完,今天我们就先跳过ContentProvider源码解析来讲资源加载相关的知识,资源加载可以说是插件化非常重要的一环,我们很有必 ...

  6. Android LayoutInflater.inflate源码解析

    一年多以前看过源码,感觉了解比较透彻了,长时间不经大脑思考,靠曾经总结的经验使用inflate方法,突然发现不知道什么时候忘记其中的原理了,上网查了一些资料,还各有不同,反而把我搞糊涂了,还是自己看源 ...

  7. Android—内存泄漏、GC及LeakCanary源码解析

    内存抖动:内存频繁的分配和回收,频繁的GC会导致UI卡顿,严重的时候导致OOM. 内存泄露:程序在向系统申请分配内存空间后(new),在使用完毕后未释放.结果导致一直占据该内存单元,我们和程序都无法再 ...

  8. android动画如何更新UI(ValueAnimator源码解析)

    概述 android动画经常会碰到卡顿,或者阻塞主进程之类的问题. 为了排查此类问题,不得不对动画原理了解一二,于是作此文. 此文围绕两个主线问题展开: ui更新的频率是如何控制的? 比如,1秒内会更 ...

  9. Android qq消息气泡实现效果,BezierDemo源码解析-实现qq消息气泡拖拽消失的效果

    这篇文章中我们比较了DraggableFlagView和BezierDemo两个项目的区别,提到将对其中一个做源码分析,那么我们就来分析BezierDemo的源码吧,因为这个项目的源码最简单,可以更直 ...

  10. android sdk 源码解析

    AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...

最新文章

  1. 应有尽有!这可能是最全的 AI 面试笔记了
  2. 中国最大AI芯片发布,顺手拿下四个国内第一,带Benchmark的那种
  3. 数据更新播报php程序,使用thinkPHP实现数据更新一例【原创】
  4. 方差分析 球形检验_两因素重复测量设计做方差分析时,球形检验没有结果怎么回事?...
  5. 戴尔新版bios设置中文_戴尔电脑装机过程
  6. js获取当前月的第一天和最后一天
  7. 机器学习:监督学习和无监督学习
  8. linux c++ 实现http请求
  9. Baidu-Rpc中Pb结构转为Json
  10. android 绘制按钮,将背景可绘制的状态设置为android按钮
  11. Digilent提供的Pmod AD5驱动程序
  12. Flash-制作空心文字
  13. datasupport类删除_关于xcode:我可以从iOS DeviceSupport删除数据吗?
  14. 20120902 07
  15. 新建的web项目为什么默认访问index.jsp
  16. 数据分析概念与职业操守
  17. 关于activity的生命周期1
  18. odoo15全面解决财务会计管理、核算、分析解决方案
  19. 网站dns服务器不能用,Win7网络诊断“DNS服务器可能不可用”怎么解决?
  20. 监控视频行为分析算法

热门文章

  1. 【51单片机】基于51单片机的时钟电子锁设计
  2. You can't specify target table 'UpdateInfo' for update in FROM clause
  3. excel多元线性拟合_使用Excel数据分析工具进行多元回归分析的方法
  4. 5G垂直领域:华为智慧园区
  5. C#中new一个对象的过程说明
  6. 技术经济与企业管理复习知识点总结
  7. dell台式计算机恢复出厂设置,戴尔Win10电脑怎么恢复出厂设置?
  8. unity3d:路径点移动,使用dotween(模拟蝴蝶飞舞)
  9. android工程如何创建数据库,安卓项目-利用Sqlite数据库,开发新闻发布系统
  10. Lync 2013正式版评估及2013版独立客户端下载