頁面加載流程碧绞、setcontentview與inflate過程解析優(yōu)化

頁面的加載中,必不可少的是setcontentview吱窝,如果是通過資源r.layout來進(jìn)行布局加載讥邻,那么一定繞不開inflate 資源文件。那么setcontentview中到底進(jìn)行了哪些操作癣诱?官方推出的asyncinflatelayout到底優(yōu)化哪些地方计维?哪些地方是能再進(jìn)行一次優(yōu)化的?
activity中inflate操作簡化流程圖:


image

從setcontent來看撕予,分為幾個步驟

1.將docview鲫惶,theme級別配置設(shè)置到docview和window中 
2.docview removeall所有的子view 
3.開始從xml中、view 來addview都docview中实抡;
getDelegate().setContentView();
public void setContentView(int resId) {
    ensureSubDecor();//theme設(shè)置到content,
    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
   mOriginalWindowCallback.onContentChanged();
}
在ensuresubdocor方法中欠母,有值得注意的點:
private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        mSubDecor = createSubDecor(); //創(chuàng)建docview同時添加到window中;
        ...
        applyFixedSizeWindow();//contentviewtheme設(shè)置吆寨;
        ....
    }
}
關(guān)鍵點:
1.createSubDecor()創(chuàng)建docview赏淌, 判斷類型創(chuàng)建相應(yīng)的docview set到window中;

之后到xml解析和view生成的邏輯中:

inflate 調(diào)用到 android.view.LayoutInflater#inflate(int, android.view.ViewGroup)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    if (DEBUG) {
        Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                + Integer.toHexString(resource) + ")");
    }
    //從resource拿到目標(biāo)資源文件
    final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}
//從resource拿到目標(biāo)資源文件  具體解析
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
    return loadXmlResourceParser(id, "layout");
}
//指定文件的xml解析器
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
        throws NotFoundException {
    final TypedValue value = obtainTempTypedValue();
    try {
        final ResourcesImpl impl = mResourcesImpl;
        impl.getValue(id, value, true);
        if (value.type == TypedValue.TYPE_STRING) {
            return impl.loadXmlResourceParser(value.string.toString(), id,
                    value.assetCookie, type);
        }
        ....報錯
    } finally {
        releaseTempTypedValue(value);
    }
}
//loadXmlResourceParser 處理類啄清; 
XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
        @NonNull String type)
        throws NotFoundException {
    if (id != 0) {
        try {
            synchronized (mCachedXmlBlocks) {
                final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies; //緩存塊為4
                final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles; //緩存塊為4
                final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;//緩存塊為4
                // 首先看是否在緩存中六水,F(xiàn)irst see if this block is in our cache.
                final int num = cachedXmlBlockFiles.length;
                for (int i = 0; i < num; i++) {
                    if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
                            && cachedXmlBlockFiles[i].equals(file)) {
                        return cachedXmlBlocks[i].newParser();
                    }
                }
                //不在緩存中的,創(chuàng)建一個新塊放到下一個插槽
                // Not in the cache, create a new block and put it at the next slot in the cache.
                final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
                if (block != null) {
                    final int pos = (mLastCachedXmlBlockIndex + 1) % num;//有限的塊緩存只在4塊內(nèi)進(jìn)行緩存
                    mLastCachedXmlBlockIndex = pos;
                    final XmlBlock oldBlock = cachedXmlBlocks[pos];
                    if (oldBlock != null) {
                        oldBlock.close();//釋放掉之前占位的塊辣卒;
                    }
                    cachedXmlBlockCookies[pos] = assetCookie;//緩存type
                    cachedXmlBlockFiles[pos] = file;//緩存filename
                    cachedXmlBlocks[pos] = block;  //這個地方注意一下掷贾,極限優(yōu)化中可以用到;
                    return block.newParser();//返回塊中的解析結(jié)果
                }
            }
        } catch (Exception e) {
           ...拋出錯誤
        }
    }
    ...拋出錯誤
}
//xml parser之后成為擁有start-end-attrs-name的基本數(shù)據(jù)結(jié)構(gòu)荣茫,再通過inflate轉(zhuǎn)化成view實體
//從parser中的基本數(shù)據(jù)想帅,構(gòu)建view
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 {
            // Look for the root node. 查找root節(jié)點
            int type;
            .....查找root節(jié)點
            final String name = parser.getName();
           
            if (TAG_MERGE.equals(name)) {
               //merge標(biāo)簽驗證是否有attachroot
                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml  通過tag生成view
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                ViewGroup.LayoutParams params = null;
                if (root != null) {
                   ...
                    params = root.generateLayoutParams(attrs);//生成params,布局參數(shù)
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }
                ...
                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);
                ...
                // We are supposed to attach all the views we found (int temp)to root. Do that now.
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
                // Decide whether to return the root that was passed in or the top view found in xml.
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }
        } catch (XmlPullParserException e) {
           ....exception deal
        } finally {
            // Don't retain static reference on context.上下文中不要保留靜態(tài)引用
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        return result;
    }
}

以上的流程中啡莉,window是在activity中new出來港准,docview是根據(jù)設(shè)置的theme來進(jìn)行inflate旨剥,setcontentview res方式也是通過inflate方法;通過inflate就不可避免的走到讀取文件--->格式化解析---->反射出view實體類 這幾步浅缸;
如果做極限優(yōu)化轨帜,那么我們根據(jù)inflate相應(yīng)的代碼可以得出一個優(yōu)化點,可以在子線程嘗試對xml進(jìn)行加載衩椒,只要inflate執(zhí)行到存儲到block緩存中阵谚,那么就可以再下次使用相同文件的時候加快加載速度;同時在抖音的優(yōu)化策略中烟具,有對class的提前l(fā)oad來減小主頁上加載速度的優(yōu)化梢什;
還有公司開發(fā)組另辟蹊徑,把xml 轉(zhuǎn)view的其中幾個步驟進(jìn)行優(yōu)化朝聋,例如讀取xml文件的io耗時嗡午,那么我們直接再編譯期間將xml文件讀取轉(zhuǎn)化成class文件,setcontentview變成代碼動態(tài)生成布局越過xml的讀取解析兩部分冀痕,直接到inflate的convert步驟中荔睹;

asyncinflatelayout是什么框架?其中做了哪些事能異步加載布局言蛇?
顧名思義是一個異步加載布局的框架僻他,官方出品。從源碼來看不是很重腊尚,只是一個thread inflate布局的框架吨拗,主要解決inflate中io文件操作,和反射操作阻塞主線程的問題婿斥,讓infalte在子線程進(jìn)行劝篷,成功之后回調(diào)給主線程進(jìn)行后續(xù)處理;

public AsyncLayoutInflater(@NonNull Context context) {
    mInflater = new BasicInflater(context);
    mHandler = new Handler(mHandlerCallback);
    mInflateThread = InflateThread.getInstance();
}
//構(gòu)造函數(shù)中的實體類就是整個asyncinflate核心民宿,handler進(jìn)行回調(diào)娇妓,thread負(fù)責(zé)子線程infalte,infalte負(fù)責(zé)具體加載活鹰;

可能是個輕量且優(yōu)化不是很大哈恰,不能作為kpi來進(jìn)行,所以在asyncinflate的封裝上沒有考慮得特別完善志群。從子線程未用線程池着绷,異步加載未考慮調(diào)用infalte和加載view不同類的情況,都會不太細(xì)膩有限制赖舟;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蓬戚,一起剝皮案震驚了整個濱河市夸楣,隨后出現(xiàn)的幾起案子宾抓,更是在濱河造成了極大的恐慌子漩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件石洗,死亡現(xiàn)場離奇詭異幢泼,居然都是意外死亡,警方通過查閱死者的電腦和手機讲衫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門缕棵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涉兽,你說我怎么就攤上這事招驴。” “怎么了枷畏?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵别厘,是天一觀的道長。 經(jīng)常有香客問我拥诡,道長触趴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任渴肉,我火速辦了婚禮冗懦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仇祭。我一直安慰自己披蕉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布乌奇。 她就那樣靜靜地躺著嚣艇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪华弓。 梳的紋絲不亂的頭發(fā)上食零,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音寂屏,去河邊找鬼贰谣。 笑死,一個胖子當(dāng)著我的面吹牛迁霎,可吹牛的內(nèi)容都是我干的吱抚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼考廉,長吁一口氣:“原來是場噩夢啊……” “哼秘豹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起昌粤,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤既绕,失蹤者是張志新(化名)和其女友劉穎啄刹,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凄贩,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡誓军,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疲扎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昵时。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖椒丧,靈堂內(nèi)的尸體忽然破棺而出壹甥,到底是詐尸還是另有隱情,我是刑警寧澤壶熏,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布盹廷,位于F島的核電站,受9級特大地震影響久橙,放射性物質(zhì)發(fā)生泄漏俄占。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一淆衷、第九天 我趴在偏房一處隱蔽的房頂上張望缸榄。 院中可真熱鬧,春花似錦祝拯、人聲如沸甚带。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹰贵。三九已至,卻和暖如春康嘉,著一層夾襖步出監(jiān)牢的瞬間碉输,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工亭珍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敷钾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓肄梨,卻偏偏與公主長得像阻荒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子众羡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355