1. 程式人生 > >Activity 從載入佈局檔案到顯示的過程分析

Activity 從載入佈局檔案到顯示的過程分析

Activity 生命週期函式執行過程詳解中介紹了ActivityThread、Instrumentation、ActivityManagerService啟動activity的過程,本文主要介紹Activity從載入佈局檔案到顯示的過程。

SetContentView

在activity的onCreate()生命週期函式中,會呼叫setContentView()來設定我們的佈局檔案,先來了解下setContentView()函式

  public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);//呼叫了getWindow()
initWindowDecorActionBar(); }

間接呼叫了getWindow()的setContentView()函式,來看看getWindow()

 public Window getWindow() {
        return mWindow;
    }

來看看mWindow是什麼時候初始化的

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);
        ...
mWindow = new PhoneWindow(this); ... }

在activity的attach()函式中初始化,是一個PhoneWindow,那接著而看看PhoneWindow的setContentView()方法

    @Override
    public void setContentView(int layoutResID) {

        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else
if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }

mContentParent為null時先呼叫 installDecor(),然後在通過mLayoutInflater.inflate(layoutResID, mContentParent);進行設定,先來看看installDecor()

private void installDecor() {
            //mDecor為null,進行初始化
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        //mContentParent為null,進行初始化
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }

                final int localFeatures = getLocalFeatures();
                for (int i = 0; i < FEATURE_MAX; i++) {
                    if ((localFeatures & (1 << i)) != 0) {
                        mDecorContentParent.initFeature(i);
                    }
                }

                mDecorContentParent.setUiOptions(mUiOptions);

                if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                        (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
                    mDecorContentParent.setIcon(mIconRes);
                } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                        mIconRes == 0 && !mDecorContentParent.hasIcon()) {
                    mDecorContentParent.setIcon(
                            getContext().getPackageManager().getDefaultActivityIcon());
                    mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
                }
                if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                        (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
                    mDecorContentParent.setLogo(mLogoRes);
                }


                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
                    invalidatePanelMenu(FEATURE_ACTION_BAR);
                }
            } else {
               // mContentParent不為null
                mTitleView = (TextView)findViewById(R.id.title);
                if (mTitleView != null) {
                    mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                     // 不顯示標題的時候隱藏
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        View titleContainer = findViewById(
                                R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        if (mContentParent instanceof FrameLayout) {
                            ((FrameLayout)mContentParent).setForeground(null);
                        }
                    } else {
                     // 設定標題
                        mTitleView.setText(mTitle);
                    }
                }
            }
        }
    }

DecorView為null時呼叫generateDecor()進行初始化DecorView,mContentParent為null時呼叫generateLayout(mDecor)初始化mContentParent,然後設定標題,若不顯示標題則隱藏。

先來看看generateDecor()

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

只是進行初始化DecorView,接著看generateLayout(mDecor)

   protected ViewGroup generateLayout(DecorView decor) {
        //...省略程式碼
        //通過主題載入不同的佈局檔案
        View in = mLayoutInflater.inflate(layoutResource, null);
        //設定DecorView
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
        //獲取contentParent
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //...省略程式碼
        return contentParent;

   }

我們再回到PhoneWindow的setContentView()函式中

  public void setContentView(int layoutResID) {

        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //佈局檔案被設定到ContentParent
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

通過轉載網上的一張圖片來說明

這裡寫圖片描述

1.activity在onCreate()中間接呼叫PhoneWindow的setContentView()
2.PhoneWindow根據主題進行初始化DecorView以及title和ContentParent
3.將對應的佈局檔案設定到ContentParent中

Activity 生命週期函式執行過程詳解中我們知道,View的顯示是在handleResumeActivity()中處理的,那麼我們接著看handleResumeActivity()

handleResumeActivity()

直接看handleResumeActivity()程式碼是如何展示view的


    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {

             //省略部分程式碼

            if (r.window == null && !a.mFinished && willBeVisible) {
                //獲取PhoneWindow
                r.window = r.activity.getWindow();
                //獲取PhoneWindow的DecorView
                View decor = r.window.getDecorView();
                //DecorView設定為不顯示INVISIBLE
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

                 //省略部分程式碼

                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        //將decorview加入到加入到ViewManager中
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    //顯示view
                    r.activity.makeVisible();
                }
            }
    }

1.將DecorView設定為Invisible
2.將DecorView加入到ViewManger中
3.通過activity的makeVisible()進行顯示

來看看makeVisible()函式

   void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

設定DecorView為visible,而呼叫setVisibility會間接觸發view的繪製流程,然後更新介面。
至於view的繪製流程以及ViewManager用單獨的文章介紹。

相關推薦

Activity 載入佈局檔案顯示過程分析

在Activity 生命週期函式執行過程詳解中介紹了ActivityThread、Instrumentation、ActivityManagerService啟動activity的過程,本文主要介紹Activity從載入佈局檔案到顯示的過程。 SetC

Android執行時ART載入OAT檔案過程分析

                        在前面一文中,我們介紹了Android執行時ART,它的核心是OAT檔案。OAT檔案是一種Android私有ELF檔案格式,它不僅包含有從DEX檔案翻譯而來的本地機器指令,還包含有原來的DEX檔案內容。這使得我們無需重新編譯原有的APK就可以讓它正常地在ART裡

LayoutInflater 載入佈局檔案原理,過程分析

       工作之餘,研究了研究,寫了一個外掛換膚的小框架,準備這段時間寫兩三篇文章做一下總結,有問題的話歡迎批評指正,因為侵入式外掛換膚的框架中有涉及到LayoutInflater載入佈局檔案的相關知識,所以,本篇文章先針對LayoutInflater載入佈局的過程以及原

C/C++程式編譯到最終生成可執行檔案過程分析

     *******************************************************篇一*******************************************************************************************

android Activity.this.getLayoutInflater()::動態載入佈局檔案,實現彈窗效

activity類的拓展方法1:getLayoutInflater():將layout的xml佈局檔案例項化為View類物件,實現動態載入佈局 MainActivity.java package com.example.dynamiclayout; import and

Mach-O文件格式和程序載入到運行過程

height star 也會 linked trail dylib 建立 helper cmd > 之前深入了解過。過去了一年多的時間。如今花些時間好好總結下,畢竟好記性不如爛筆頭。其次另一個目的,對於mach-o文件結構。關於動態載入信息那個數

android 界面顯示過程分析

2d繪圖 produce play str 開發 rap 消費者 sum 合數 android 系統提供了一系列的繪圖渲染api,這些api支持2D繪圖和3D繪圖;那麽理解這些api是如何工作的,還是十分重要的。應用開發者最常用的就是Canvas和OpenGL,Canva

LIVE555學習3:live555MediaServer講解——Live555啟動到響應Client過程分析

文章目錄 1 概述 2 程式碼分析 2.1 doEventLoop 2.2 計劃任務 2.3 RTSP服務 2.3.1 呼叫關係 2.3.2 Server監聽埠的建立 2.3.3 計劃任務

C# AE開發,載入sxd檔案顯示不了

問題:載入sxd時,執行結果不顯示內容,空白。 解決方法: 百度之後:蘇佔東001 2016-01-12 13:51 在你的SceneControl介面中拖入控制元件License Control試試 於是查詢如何操作,我使用的是VS2017+AE10.1版本,工具

Android 動態載入佈局檔案

本文轉自:原文地址 Android的基本UI介面一般都是在xml檔案中定義好,然後通過activity的setContentView來顯示在介面上,這是Android UI的最簡單的構建方式。其實,為了實現更加複雜和更加靈活的UI介面,往往需要動態生成UI介面,甚至根

安卓開發之非activity中呼叫佈局檔案

前提: MainActivity.javaTop.javaMapp.javaactivity_main.xmltop.xmlmapp.xml在activity_main中 <com.exampl

Android載入佈局檔案的兩種方式及區別。

第一種 LayoutInflater inflater = LayoutInflater.from(parent.getContext()); inflater.inflate(...);//有多種建構函式第二種 View infl

Web專案使用Spring框架伺服器啟動載入xml檔案過程學習

個人在Tomcat簡單部署了一個web專案,console啟動日誌。 2015-9-13 20:36:39 org.apache.catalina.core.AprLifecycleListener init 資訊: The APR based Apache Tomcat

Activity佈局資源layoutResId在setContentView載入過程分析

前言 記得剛開始學習安卓那會,感覺安卓真的很簡單,用xml寫一個佈局,然後再寫一個activity,接著呼叫一下在onCreate中呼叫下setContentView(resId)一個頁面就可以看到了,現在回想也才知道Android的牛逼,它降低了開發者的門檻

Activity 啟動到佈局繪製的簡單分析

這篇文章主要是配合原始碼簡單的介紹一下,程式的載入過程,Activity 中佈局的載入過程,能夠大體的瞭解整個過程。不過過度的追究細節,因為裡面任何一個細節可能都夠你研究一段時間的!先了解掌握大體過程,再慢慢來! 開始啟動 我們都知道,Activity 是有生命週期的,onCreate()、onStart

Android 載入鍵盤佈局檔案過程

二、WindowManagerService.java的建構函式,在載入鍵盤佈局方面做了兩件事情:1.初始化,構造一個InputManager例項;2.啟動,由InputManager.java start()函式實現 private WindowManagerService(Context context

瀏覽器中輸入url地址到瀏覽器中顯示網頁內容 的過程分析

此文是我總結了一些經驗和各種大神知識綜合而成的。  1.首先當然是瀏覽器紅輸入url地址,            但是當你輸入baidu 為什麼最終的URL地址是www.baidu.com呢?

Android6.0 按鍵kl檔案載入過程分析

在之前按鍵過程分析的幾篇部落格中,我分析過關於按鍵kl檔案的載入,但是講的不是非常詳細,這篇部落格主要把kl檔案載入過程單獨拉出來分析下。 1. 獲取InputDeviceIdentifier的name 以及 Device的建立 InputDeviceIdentifier

Cocos2d-x 3.9教程:10.使用CocosStudio的UI編輯器UI檔案載入佈局和控制元件

Cocos2d-x 3.9教程 10. 使用CocosStudio的UI編輯器從UI檔案中載入佈局和控制元件 1.1. 使用CocosStudio的UI編輯器 1.1.1. 安裝和啟動 從官網上下載2015年11月18日版本,Cocos studio 2.3.3。  

Android 中Activity生命週期分析AActivity 到BActivity過程分析

A啟動B  A生命週期全過程1.啟動A生命週期:Activity--A: onCreate---->呼叫成功生命週期:Activity--A: onStart---->呼叫成功生命週期:Activity--A: onResume---->呼叫成功2.在A中