源碼分析之LayoutInflater

轉載自:http://blog.csdn.net/u013356254/article/details/55052363

android交流:364595326

<h3>源碼分析之LayoutInflater</h3>

<h4>簡介</h4>

<ul>

<li>

基於5.0的framework源碼進行分析,通過這篇文章我們能瞭解:

<ul>

<li>LayoutInflater的系統級服務的註冊過程</li>

<li>inflate填充的過程</li>

<li>ViewStub,merge,include的加載過程</li>

</ul>

</li>

</ul>

<h4>LayoutInflater系統服務的註冊過程</h4>

<ul>

<li>

<p>我們經常調用</p>

<pre><code>context.getSystemService(Context.LAYOUT_INFLATE_SERVICE)

</code></pre>


<p>獲得<strong>LayoutInflater</strong>對象。那麼這個對象是什麼時候註冊到Context中的呢?這個對象的具體實現類是誰?</p>

<ul>

<li>LayoutInflater這個服務,是在創建Activity的時候,作爲<strong>baseContext</strong>傳遞給Activity的。接下來我們看源碼過程。</li>

<li>

<p>我們知道<strong>Activity</strong>的創建過程是在<strong>ApplicationThread<strong>的</strong>performLaunchActivity</strong>方法中。那麼接下來我們分析這個方法</p>

<pre><code> private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    /*/

    Activity activity = null;

    try{

        // 通過Instrumentation類創建Activity

         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

activity = mInstrumentation.newActivity(

        cl, component.getClassName(), r.intent);


        }catch(Exeception e){}

    /*/

    // 創建Context過程,也就是baseContext

     Context appContext = createBaseContextForActivity(r, activity);

    // 關聯activity和baseContext

    activity.attach(appContext, this, getInstrumentation(), r.token,

            r.ident, app, r.intent, r.activityInfo, title, r.parent,

            r.embeddedID, r.lastNonConfigurationInstances, config,

            r.referrer, r.voiceInteractor, window);


 }  

</code></pre>


</li>

<li>

<p>那麼接下來我們只要分析</p>

<pre><code> Context appContext = createBaseContextForActivity(r, activity);

</code></pre>


<p>這個方法即可,源碼繼續</p>

<pre><code> private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {

    // 通過調用ContextImpl的靜態方法創建baseContext對象

    ContextImpl appContext = ContextImpl.createActivityContext(

            this, r.packageInfo, r.token, displayId, r.overrideConfig);

    appContext.setOuterContext(activity);

    return baseContext;

}

</code></pre>


</li>

<li>

<p>接下來分析</p>

<pre><code>ContextImpl.createActivityContext(

                this, r.packageInfo, r.token, displayId, r.overrideConfig);

        appContext.setOuterContext(activity);

</code></pre>


<p>接下來我們分析下ContextImpl這個類,發現其有一個成員變量</p>

<pre><code>    // 在這裏註冊系統級別的服務

// The system service cache for the system services that are cached per-ContextImpl.

final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

</code></pre>


</li>

</ul>

<p>SystemServiceRegistry類有個靜態代碼塊,完成了常用服務的註冊,代碼如下 </p>

<pre><code>static{

    // 註冊LayoutLAYOUT_INFLATER_SERVICE系統服務,具體實現類是PhoneLayoutInflater

     registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,

            new CachedServiceFetcher&lt;LayoutInflater&gt;() {

        @Override

        public LayoutInflater createService(ContextImpl ctx) {

            return new PhoneLayoutInflater(ctx.getOuterContext());

        }});

    // 註冊AM 

     registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,

        new CachedServiceFetcher&lt;ActivityManager&gt;() {

    @Override

    public ActivityManager createService(ContextImpl ctx) {

        return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());

    }});

    // 註冊WM

     registerService(Context.WINDOW_SERVICE, WindowManager.class,

        new CachedServiceFetcher&lt;WindowManager&gt;() {

    @Override

    public WindowManager createService(ContextImpl ctx) {

        return new WindowManagerImpl(ctx);

    }});

    // 等等

      }

</code></pre>


</li>

<li>

<p>接下來我們看inflate過程,下面是整個inflate過程</p>

<pre><code>public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {

synchronized (mConstructorArgs) {

    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");


    final Context inflaterContext = mContext;

    final AttributeSet attrs = Xml.asAttributeSet(parser);

    Context lastContext = (Context) mConstructorArgs[0];

    mConstructorArgs[0] = inflaterContext;

    View result = root;


    try {

        // 循環找到第一個view節點,

        int type;

        while ((type = parser.next()) != XmlPullParser.START_TAG &amp;&amp;

                type != XmlPullParser.END_DOCUMENT) {

            // Empty

        }

        // 這裏判斷是否是第一個view節點

        if (type != XmlPullParser.START_TAG) {

            throw new InflateException(parser.getPositionDescription()

                    + ": No start tag found!");

        }


        final String name = parser.getName();

       // 解析merge標籤

        if (TAG_MERGE.equals(name)) {

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

                throw new InflateException("&lt;merge /&gt; can be used only with a valid "

                        + "ViewGroup root and attachToRoot=true");

            }

            // 通過rInflate方法將merge標籤下的孩子直接合併到root上,這樣減少一層佈局,達到減少viewTree的目的

            rInflate(parser, root, inflaterContext, attrs, false);

        } else {

            // 調用反射創建view對象

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


            ViewGroup.LayoutParams params = null;


            if (root != null) {

                // Create layout params that match root, if supplied

                params = root.generateLayoutParams(attrs);

                if (!attachToRoot) {

                    // 如果view的父容器不爲null,並且attachToRoot未true得話,這裏只是讓剛剛通過反射創建的view使用root(父容器的佈局參數)

                    temp.setLayoutParams(params);

                }

            }



            // 通過深度遍歷temp下的節點,之後將節點依次添加到剛剛通過反射創建的temp對象上,因爲採用的是深度優先遍歷算法,因此viewTree的層級很深的話,會影響遍歷的性能

            rInflateChildren(parser, temp, attrs, true);

           // 判斷剛剛創建的temp對象是否添加到父節點上.

            // 滿足兩個條件1 父節點(root)不爲null,2 attachToRoot=true

            if (root != null &amp;&amp; attachToRoot) {

                root.addView(temp, params);

            }


            // 設置result

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

                result = temp;

            }

        }


    } catch (XmlPullParserException e) {

        final InflateException ie = new InflateException(e.getMessage(), e);

        ie.setStackTrace(EMPTY_STACK_TRACE);

        throw ie;

    } catch (Exception e) {


    } finally {

        // Don't retain static reference on context.

        mConstructorArgs[0] = lastContext;

        mConstructorArgs[1] = null;


        Trace.traceEnd(Trace.TRACE_TAG_VIEW);

    }

    // 返回


    return result;

}

</code></pre>


<p>}</p>

<ul>

<li>通過上面分析,我們對inflate的整體過程有了一個瞭解,也見到了merge標籤(經常作爲佈局文件根節點,來達到減少viewTree的層次)</li>

<li>接下來,我們分析4個方法</li>

<li>rInflate(parser, root, inflaterContext, attrs, false);,其實不管是根節點爲merge還是普通的view(最終都會用這個方法),深度遍歷添加view</li>

<li>

<p>下面是代碼</p>

<pre><code>    // 深度遍歷添加孩子

 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() &gt; depth) &amp;&amp; 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_TAG.equals(name)) {

        // 如果我們調用了View.setTag(),將會執行下面代碼

            parseViewTag(parser, parent, attrs);

        // include不能作爲根節點

        } else if (TAG_INCLUDE.equals(name)) {

            if (parser.getDepth() == 0) {

                throw new InflateException("&lt;include /&gt; cannot be the root element");

            }

            // 這裏解析include標籤代碼

            parseInclude(parser, context, parent, attrs);

        } else if (TAG_MERGE.equals(name)) {

            // merge一定是根節點

            throw new InflateException("&lt;merge /&gt; must be the root element");

        } else {

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

            final ViewGroup viewGroup = (ViewGroup) parent;

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

            // 遞歸,因爲rInflateChildren最終還會調用rInflate(parser, parent, parent.getContext(), attrs, finishInflate);方法

            rInflateChildren(parser, view, attrs, true);

            viewGroup.addView(view, params);

        }

    }


    if (finishInflate) {

        // viewTree填充完畢,回調自定義view經常使用的onFinishInflate方法

        parent.onFinishInflate();

    }

}

</code></pre>


</li>

<li>

<p>rInflateChildren(parser, view, attrs, true);方法</p>

<pre><code>    // 直接調用rInflate()實現ViewTree

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

    boolean finishInflate) throws XmlPullParserException, IOException {

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

</code></pre>


<p>}</p>

</li>

<li>

<p>createViewFromTag(root, name, inflaterContext, attrs);方法,這個方法其實處理了自定義view和系統view的創建。最終調用了下面方法</p>

<pre><code>     View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,

        boolean ignoreThemeAttr) {

    if (name.equals("view")) {

        name = attrs.getAttributeValue(null, "class");

    }


    // 設置view默認樣式

    if (!ignoreThemeAttr) {

        final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);

        final int themeResId = ta.getResourceId(0, 0);

        if (themeResId != 0) {

            context = new ContextThemeWrapper(context, themeResId);

        }

        ta.recycle();

    }


    try {

        View view;

        if (view == null) {

            final Object lastContext = mConstructorArgs[0];

            mConstructorArgs[0] = context;

            try {//創建系統view的方法,因爲系統view的標籤不是完整類名,需要會在 onCreateView中完成拼接(拼接出系統view的完整類名)

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

                    view = onCreateView(parent, name, attrs);

                } else {

                    //自定義view的創建

                    view = createView(name, null, attrs);

                }

            } finally {

                mConstructorArgs[0] = lastContext;

            }

        }


        return view;

    } catch (InflateException e) {

        throw e;


    } catch (ClassNotFoundException e) {

        final InflateException ie = new InflateException(attrs.getPositionDescription()

                + ": Error inflating class " + name, e);

        ie.setStackTrace(EMPTY_STACK_TRACE);

        throw ie;


    } catch (Exception e) {

        final InflateException ie = new InflateException(attrs.getPositionDescription()

                + ": Error inflating class " + name, e);

        ie.setStackTrace(EMPTY_STACK_TRACE);

        throw ie;

    }

}

</code></pre>


</li>

<li>

<p>接下來我們分析 createView(String name, String prefix, AttributeSet attrs)方法,系統view的創建,最終也會調用createView方法。只不過在前面拼接上了系統view的包名。</p>

<pre><code>     public final View createView(String name, String prefix, AttributeSet attrs)

        throws ClassNotFoundException, InflateException {

    // 獲取view的構造方法

    Constructor&lt;? extends View&gt; constructor = sConstructorMap.get(name);

    // 驗證

    if (constructor != null &amp;&amp; !verifyClassLoader(constructor)) {

        constructor = null;

        sConstructorMap.remove(name);

    }

    Class&lt;? extends View&gt; clazz = null;


    try {

        if (constructor == null) {


            clazz = mContext.getClassLoader().loadClass(

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


            if (mFilter != null &amp;&amp; clazz != null) {

                boolean allowed = mFilter.onLoadClass(clazz);

                if (!allowed) {

                    failNotAllowed(name, prefix, attrs);

                }

            }

            constructor = clazz.getConstructor(mConstructorSignature);

            constructor.setAccessible(true);

            // 將view的構造方法緩存起來

            sConstructorMap.put(name, constructor);

        } else {

          /*/

        }


        Object[] args = mConstructorArgs;

        args[1] = attrs;

        // 反射創建view對象

        final View view = constructor.newInstance(args);

        // 對viewStub進行處理

        if (view instanceof ViewStub) {

            // 給ViewStub設置LayoutInfalter.什麼時候inflate,什麼時候viewStub的內容才顯示,(比GONE性能好)

            final ViewStub viewStub = (ViewStub) view;

            viewStub.setLayoutInflater(cloneInContext((Context) args[0]));

        }

        return view;


    } catch (NoSuchMethodException e) {



    } catch (ClassCastException e) {


    } catch (ClassNotFoundException e) {


    } catch (Exception e) {


    } finally {


    }

}

</code></pre>


</li>

</ul>

</li>

</ul>

<h4>總結</h4>

<ul>

<li>系統服務的填充過程,是在ContextImpl中完成註冊的</li>

<li>LayoutInflater的實現類是PhoneLayoutInflater</li>

<li>如果僅僅使用父容器的佈局參數,可以使用inflater.inflate(layoutId,parent,false);</li>

<li>onFinishInflate()方法是在viewTree遍歷完成之後,調用的</li>

<li>merge標籤只能是根節點,include標籤不能是根節點。</li>

<li>

佈局優化

<ul>

<li>view的inflate的過程是深度遍歷,因此應該儘量減少viewTree的層次,可以考慮使用merge標籤</li>

<li>如果我們不知道view什麼時候填充的時候,可以使用ViewStub標籤,什麼時候用什麼時候填充</li>

<li>include是提升複用的</li></ul></li></ul>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章