主頁 > 移動端開發 > ??Androd 性能優化之布局優化??

??Androd 性能優化之布局優化??

2021-10-21 08:15:43 移動端開發

作者:帥次

作者簡介:CSDN博客專家,歡迎點贊、收藏、評論

粉絲福利:公眾號「帥次」一個分享Android 體系技術·相關知識·面試題庫·技識訓助·干貨·資訊·高薪職位·教程的地方,

Android的布局管理器本身就是個UI組件,所有的布局管理器都是ViewGroup的子類,而ViewGroup是View的子類,所以布局管理器可以當成普通的UI組件使用,也可以作為容器類使用,可以呼叫多個多載addView()向布局管理器中添加組件,并且布局管理器可以互相嵌套,當然不推薦過多的嵌套 (如果要兼容低端機型,最好不要超過5層)

🔥 布局層級管理

讓咱們一起了解一下每當系統繪制一個布局時,都會發生一些什么,這一程序由兩個步驟完成:

💥 繪制(Measurement)

  • 1:根布局測量自身,

  • 2:根布局要求它內部所有子組件測量自身,

  • 3:所有自布局都需要讓它們內部的子組件完成這樣的操作,直到遍歷完視圖層級中所有的View,

💥 擺放(Positioning)

  • 1:當布局中所有的View都完成了測量,根布局則開始將它們擺放到合適的位置,

  • 2:所有子布局都需要做相同的事情,直到遍歷完視圖層級中所有的View,

💥 背景設定產生的過度繪制

  • 組件背景:每個組件每設定一次背景, 該組件的區域就會增加一層繪制 , 如 LinearLayout 設定背景顏色 , 里面的 TextView 設定背景顏色 , 都會增加該組件區域內的過渡繪制 ;

  • 主題背景:Activity 界面的主題背景,會增加一次 GPU 繪制 ;

不要隨意給布局中的 UI 組件設定背景 ,如 ImageView 設定一張圖片,會增加一次繪制 ,再給該 ImageView 組件設定背景顏色, 那么又會增加一次繪制, 那么該 ImageView 組件肯定過渡繪制了,

💥 小結

當某個View的屬性發生變化(如:TextView內容變化或ImageView影像發生變化),View自身會呼叫View.invalidate()方法(必須從 UI 執行緒呼叫),自底向上傳播該請求,直到根布局(根布局會計算出需要重繪的區域,進而對整個布局層級中需要重繪的部分進行重繪),布局層級越復雜,UI加載的速度就越慢,因此,在撰寫布局的時候,盡可能地扁平化是非常重要的,

FrameLayout和TableLayout有各自的特殊用途,LinearLayout 和 RelativeLayout 是可以互換的,ConstraintLayout和RelativeLayout類似,也就是說,在撰寫布局時,可以選擇其中一種,咱們可以以不同的方式來撰寫下面這個簡單的布局,

🔥 小實驗(多種方式實作同一布局)

💥 LinearLayout

第一種方式是使用LinearLayout,雖然可讀性比較強,但是性能比較差,由于嵌套LinearLayout會加深視圖層級,每次擺放子組件時,相對需要消耗更多的計算,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/color_666666"/>
    <View
        android:id="@+id/view_top_2"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:background="@color/teal_200"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <View
            android:id="@+id/view_top_3"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/color_FF773D"/>
        <View
            android:id="@+id/view_top_4"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/purple_500"/>
    </LinearLayout>
</LinearLayout>

LinearLayout視圖層級如下所示:

💥 使用RelativeLayout

第二種方法使用RelativeLayout,在這種情況下,你不需要嵌套其他ViewGroup,因為每個子View可以相當于其他View,或相對與父控制元件進行擺放,

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/color_666666"/>
    <View
        android:id="@+id/view_top_2"
        android:layout_width="200dp"
        android:layout_below="@id/view_top_1"
        android:layout_height="100dp"
        android:background="@color/teal_200"/>
    <View
        android:id="@+id/view_top_3"
        android:layout_width="100dp"
        android:layout_below="@id/view_top_2"
        android:layout_height="100dp"
        android:background="@color/color_FF773D"/>
    <View
        android:id="@+id/view_top_4"
        android:layout_width="100dp"
        android:layout_below="@id/view_top_2"
        android:layout_toRightOf="@id/view_top_3"
        android:layout_height="100dp"
        android:background="@color/purple_500"/>
</RelativeLayout>

RelativeLayout視圖層級如下所示:

通過兩種方式,可以很容易看出,第一種方式LinearLayout需要3個視圖層級和6個View,第二種方式RelativeLayout僅需要2個視圖層級和5個View,

當然,雖然RelativeLayout效率更高,但不是所有情況都能通過相對布局的方式來完成控制元件擺放,所以通常情況下,這兩種方式需要配合使用,

注意:為了保證應用程式的性能,在創建布局時,需要盡量避免重繪,布局層級應盡可能地扁平化,這樣當View被重繪時,可以減少系統花費的時間,在條件允許的情況下,盡量的使用RelativeLayout和ConstraintLayout,而非LinearLayout,或者用GridLayoutl來替換LinearLayout

咱們最常使用的是ViewGroup是LinearLayout,只是因為它很容易看懂,撰寫起來簡單,所以它就成了Android開發的首選,出于這個原因,Google推出了一個全新的ViewGroup,在適當的時候時候使用它,可以減少冗余,它就是網格布局GridLayout下,

🔥 布局復用(<include/>和 <merge/> )

Android 提供了一個非常有用的標簽,在某些情況下,當你希望在其他布局中用一些已存在的布局時, 標簽可通過制定相關參考ID,將一個布局添加到另一個布局,

比如自定義一個標題欄,那么可以按照下面的方式,創建一個可重復用的布局檔案,

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/color_666666"/>
</RelativeLayout>

接著,將標簽放入相應的布局檔案中,替換掉對應的 View:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include layout="@layout/include_layout"/>
    ...
</RelativeLayout>

這么一來,當你希望重用某些View時,就不用復制/粘貼的方式來實作,只需要定義一個layout檔案,然后通過 參考即可,

但是這樣做,可能會引入一個冗余的ViewGroup(重用的布局檔案的根視圖),為此,Android 提供了另一個標簽,用來幫我們減少布局冗余,讓層級變得更加扁平化,我們只需要將可重用的根視圖,替換為 標簽即可,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/color_666666"/>
</merge>

如此一來,就沒有了冗余的視圖控制元件,因為系統會忽略標簽,并將標簽中的視圖直接放置在相應的布局檔案中,替換標簽,

注意:使用此標簽時,需要記住它的兩個主要限制:

1、它只能作為布局檔案的跟來使用,

2、每次呼叫LayoutInflater.inflate()時,必須為布局檔案提供一個View,作為它的父容器:LayoutInflater.from(this).inflate(R.layout.merge_layout,parent,true);

🔥 ViewStub

ViewStub是一個不可見的零大小View,可以作為一個節點被加入布局檔案,但他關聯的布局,知道運行時通過呼叫 ViewStub.inflate() 或 View.setVisibility(View.VISIBLE) 方法,才會被繪制,

先看效果圖:

💥 ViewStub 設定

        <ViewStub android:id="@+id/viewStub"
            android:inflatedId="@+id/subTree"
            android:layout="@layout/activity_imageview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

💥 顯示

上方 ViewStub 所關聯的布局 activity_imageview 并不會被實體化(不要呼叫布局內的控制元件,因為還沒加載會報空指標例外),只有程式在運行期間呼叫了以下方法:

findViewById(R.id.viewStub).setVisibility(View.VISIBLE);

((ViewStub)findViewById(R.id.viewStub)).inflate();

在這期間不要呼叫關聯布局內的控制元件,因為還沒唄加載沒有

一旦 ViewStub 變成 visible 或者被 inflate,它便不再可用(Id:viewStub沒了),因為它在布局層級中的位置已經實體化出來的布局所替代,因為不能被訪問,而應該使用 android:inflatedId 屬性中的ID,如下:

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_view:
                break;
            case R.id.btn_scheme:
                //加載,選擇一種即可,
                findViewById(R.id.v_stud).setVisibility(View.VISIBLE);
                //((ViewStub)findViewById(R.id.v_stud)).inflate();

                //加載后layout所用ID
                subTree  = findViewById(R.id.subTree);
                findViewById(R.id.btn_iv_basis).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this,"我是 ViewStud 加載的控制元件",Toast.LENGTH_SHORT).show();
                    }
                });
                break;
            case R.id.btn_invisible:
                subTree.setVisibility(View.INVISIBLE);
                break;
            case R.id.btn_visible:
                subTree.setVisibility(View.VISIBLE);
                break;
            case R.id.btn_init:
                //ViewStub變為可見后再次呼叫會報空指標,因為id:viewStub 已經不存在了,
                View viewStub  = findViewById(R.id.viewStub);
                viewStub.setVisibility(View.GONE);
                break;
        }
    }

💥 小結

ViewStub 非常有用,我們可以通過 ViewStub 來延遲部分 View 的加載,縮短首次加載時間,以及減少一些不必要的記憶體分配,

🔥 自定義組件優化

在自定義View時需要注意,避免犯以下的性能錯誤:

  • 在非必要時,對View進行重繪,

  • 繪制一些不被用戶所看到的的像素,也就是過度繪制,(被覆寫的地方)

  • 在繪制期間做了一些非必要的操作,導致記憶體資源的消耗,

💥 優化

  • View.invalite()是最最廣泛的使用操作,因為在任何時候都是重繪和更新視圖最快的方式,

在自定義View時要小心避免呼叫非必要的方法,因為這樣會導致重復強行繪制整個視圖層級,消耗寶貴的幀繪制周期,檢查清楚View.invalite()和View.requestLayout()方法呼叫時間位置,因為這會影響整個UI,導致GPU和它的幀速率變慢,

  • 避免過渡重繪,為了避免過渡重繪,我們可以利用Canvas方法,只繪制控制元件中所需要的部分,整個一般在重疊部分或控制元件時特別有用,相應的方法是Canvas.clipRect()(指定要被繪制的區域);

  • 在實作View.onDraw()方法中,不應該在方法內及呼叫的方法中進行任何的物件分配,在該方法中進行物件分配,物件會被創建和初始化,而當View.onDraw()方法執行完畢時,垃圾回收器會釋放記憶體,如果View帶影片,那么View在一秒內會被重繪60次,所以要避免在View.onDraw()方法中分配記憶體,

永遠不要在View.onDraw()方法中及呼叫的方法中進行記憶體分配,避免帶來負擔,垃圾回收器多次釋放記憶體,會導致卡頓,最好的方式就是在View被首次創建出來時,實體化這些物件,

布局優化到這里就結束了,還是那句話后面如果有更好的方案會及時的添加進去,如果有老大有其他方案也可以留言哈,感謝!最后別忘記關注我撒

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/328043.html

標籤:其他

上一篇:Android學習指南 — Java知識點匯總

下一篇:Pandasread_csv壞行--字串中的分隔符引號不均勻

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more