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