Jetpack Compose What and Why, 6個問題


Jetpack Compose What and Why, 6個問題

1.這個技術出現的背景, 初衷, 要達到什么樣的目標或是要解決什么樣的問題.

Jetpack Compose是什么?
它是一個聲明式的UI工具包(declarative UI toolkit for Android).

它的主要目的就是改變之前命令式地(imperatively)寫UI的方法, 改成聲明式(declarative)的.

命令式

Android之前的寫法就叫命令式: view hierarchy是一個UI widgets構成的tree, 當app的狀態改變時, UI需要更新. 通常的做法是通過findViewById()等手段找到要更新的節點, 通過setText(), addChild(), setImageBitmap()等方法更新控件的內部狀態. 每個控件都有自己的內部狀態, 並且暴露getter/setter, 允許程序邏輯和控件交互.

命令式為什么不好呢:

  • 這樣手動地更新view會增加犯錯的可能性. 如果某一數據在多個地方被渲染, 那么很可能就忘了更新其中的某個view.
  • 很容易創建非法狀態, 比如兩個更新以不期望的方式沖突了. (比如: 一個要更新值, 另一個要移除節點.)
  • 維護的復雜度隨着需要更新的view個數而增加.

聲明式

所以, 在過去的幾年里, 業界一直在探索並且開始轉向一種聲明式的UI模型. 目的就是簡化構建和更新UI.

Jetpack Compose是一個聲明式的UI framework. 該技術的原理是從頭開始重新生成整個屏幕, 然后僅應用必要的更改. 這種方法避免了手動更新stateful view的復雜度.

在Compose的聲明式解決方案里, widgets相對來說是stateless的, 不暴露getter/setter.
實際上, widgets根本不是以對象的形式來暴露的.
當更新UI的時候, 實際上是傳不同的參數調用了composable方法. 當數據改變時, composable負責把當前的應用狀態轉化為UI.

2.這個技術的優勢和劣勢分別是什么, 或者說, 這個技術的trade-off是什么.

Compose的優勢:

  • 目前Google投入了大量的資源(學習資源, 社區挑戰賽, IDE和編譯器支持, 推出desktop版本等)來推廣Compose, 我們有理由相信Google官方會繼續發力, Compose會成為Android未來的UI標准開發形式.
  • Compose的聲明式寫法和Flutter, SwiftUI, React契合, 在支持多個平台的團隊里, 有利於架構思想的統一.
  • 聲明式UI, 寫起來更快, 不容易出錯.
  • 因為所有的代碼都是Kotlin寫的(真100%kotlin), 利用了Kotlin的強類型安全, 編譯器會提示很多錯誤.
  • 修復和更新了一些舊的API: (Buggy Android Views: Picker, Spinner, EditText, 有一些edge cases. 因為要更改舊的總是很難, 不如創建一個新的. )
  • 基於組合的Composable, 比基於繼承的View體系, 更加靈活, 易於復用. 比如可以通過組合來達到復用多個源, 不再受單繼承限制.
    以Button為例, 在傳統的UI里, 它是單繼承體系下的一個類: Button -> TextView -> View.
    而在Compose的世界里, 它只是一個@Composable的方法, 里面包含了其他composable, surface, row等.
    舉例: list->detail兩個界面, 可以通過提取方法參數來達到兩個界面的composable復用.
  • 支持和View-based UI系統的互相調用. 這樣有兩個優點: 有利於已有項目app的混用和逐步遷移; 當Compose滿足不了需求的時候可以用傳統View作為第二選擇.
  • 和Jetpack系列的其他庫都能完美結合. (ViewModel, Kotlin Flow, Coroutines, Paging, Hilt, Navigation...)

劣勢:

  • 需要用Canary版本的Android Studio, 目前(2021.5)IDE還是存在一些問題的.
  • Gradle也要升級到最新版(7.0.0-alpha15), 不是很穩定, 還有一些問題.
  • 版本更新頻繁. Compose alpha版本的一些API已經變了. -> 但是目前已經beta, 最新的Google I/0說7月就會有穩定版發布.
  • 相比Android View, 社區支持不夠多. -> 但是Google已經投入了大量的資源來推廣, 社區支持正在飛速增長.
  • 雖然Google封裝了material的compose庫, 但還是有一部分View控件沒有辦法提供, 比如WebView, MapView等.

3.這個技術使用的場景.

Jetpack Compose的使用場景是取代原來的Android View寫法(xml, 在代碼里實例化View對象再添加到View hierarchy里等),
Jetpack Compose用全新的方式來寫UI, 即將成為Google Android標准寫法.

注意這里的改變除了UI寫法的改變, 還是一種狀態管理思想的轉變.
聲明式, 單向數據流, 單個數據源.
Android內部的模式近年來一直追求的無外乎就是數據和View的分離, View的無知與自動更新, 清晰的邏輯管理和分離, 可測試性等等.

除了與View強相關的這一層(ViewModel)外, 其他的業務邏輯, 數據交互等被影響不大, 所以即便app決定逐漸遷移到Compose, 也只用管View繪制以及和View相鄰的這一層.

4.技術的組成部分和關鍵點.

Jetpack compose的總體特點:

  • 聲明式UI. f(state)=UI.
  • Built on Kotlin.
  • Unbundled: 不是和系統綁定的, 而是和Jetpack中的庫一樣, 版本獨立, 可以自己更新維護版本, 也保證了多個系統上的行為一致.
  • Built for interop. 可以和已有View互相調用, 適用於現有項目的逐漸遷移.
  • 內置控件和theming, 支持material design.

Composable functions

Compose的使用方法:
定義一系列的composable functions: 接收數據, 發射UI元素.

@Composable
fun Greeting(names: String) {
    Text("Hello $name")
}

Composable方法的特點:

  • 所有的方法都必須有@Composable注解.
  • 方法參數是數據.
  • 通過調用其他composable方法來發射UI元素.
  • 方法不返回值, 因為它們在描述屏幕狀態, 而不是構建UI widgets.
  • 方法要快, 具有冪等性(調用多少次結果都一樣), 沒有副作用. (fast, idempotent, and side-effect free.) 這就要求方法不會修改外部屬性或者全局變量, 也沒有Random的調用等.

還有一個小區別就是不同於普通的kotlin方法命名規范, composable方法名的首字母要大寫, 因為它此時代表的是一種widget.

Recomposition

Compose framework會很機智地選出有變化, 需要重新繪制的部分.

在Compose中, 如果調用composable function, 傳入了新數據, 會使得方法被recomposed: 這個方法發射的widgets會根據需要進行重新繪制.

因為重新繪制整個UI tree會花銷比較大, 所以實際上composable function只有input改變的才會被重新繪制, 對於那些參數沒有變化的方法和lambda, 其實是skip掉的, 這樣才能提高recompose的效率.

所以, 永遠不要依賴於執行composable function的side-effects, 因為recomposition有可能會被skip掉.

side-effects包括:

  • 更新共享對象.
  • 更新ViewModel中的observable.
  • 更新shared preferences.

由於composable functions有可能會被逐幀執行(比如動畫期間), 所以它應該足夠的快, 如果需要耗時的操作, 可以考慮后台的coroutine.

5.技術的底層原理和關鍵實現.

Compose的幾個特點:

  • Composable functions可以以任何順序執行.
  • Composable functions可以並行執行.
  • Recomposition會盡可能地skip多的composable functions. 如果真的需要side-effects, 考慮用callback.
  • Recomposition是樂觀的, 它認為在本次重繪期間不會有新的狀態改變, 如果有新的狀態改變, 它可能會取消當前繪制, 以新的參數重新開始繪制.
  • Composable function有可能會被執行得很頻繁(比如動畫), 所以避免耗時操作.

關於Compose的底層原理, 目前還沒找到一個官方的文檔或者架構圖.
因為可能大家都還在學習怎么使用, 這項技術的底層實現細節還沒有被熱烈討論起來.
這里有個問題: https://stackoverflow.com/questions/58558163/how-does-jetpack-compose-work-under-the-hood

從使用者的角度揣測一下Compose的原理:
雖然Composable使用了注解@Composable, 但是卻沒有添加注解處理器(kapt), 所以並不是依靠注解在編譯期生成代碼.

在添加依賴的時候需要在build.gradle里添加:

    composeOptions {
        kotlinCompilerExtensionVersion compose_version
    }

所以它和kotlin的編譯器有關系.

這個視頻: Understanding Compose (Android Dev Summit '19)在17:18開始講實現原理.

@Composable更像是一個語言的關鍵字. 可以類比suspend, 有以下幾個共同點:

  • 改變了方法的類型.
  • 強制了方法的調用上下文.

部分更新

用了定制化的Kotlin編譯器插件, 數據改變時, 受數據影響的composable的方法會被重新調用.

6.已有的實現和它之間的對比.

Android View和Jetpack Compose的對比.

Android View和Jetpack Compose的相似點:

  • 都是Android UI的寫法.

Jetpack Compose改進了View系統的哪些地方:

  • 不再需要擔心深層嵌套. Compose UI不允許多遍的測量(multi-pass measurement), 改善了效率.

Flutter和Jetpack Compose的對比.

Jetpack Compose和Flutter的相似點:

  • 聲明式UI, UI = f(state).
  • 都有web和desktop版本.
  • 官方都提供了一些material的控件和資源, 比如都有個Scaffold, 提供腳手架.
  • 都可以和原生混用, 雖然程度不一樣, Compose的混用粒度更細一點.
  • 都可以一邊改UI一邊預覽.

Jetpack Compose比Flutter好的地方:

  • 基於Kotlin, 相比於Flutter的dart來說, 對Android開發者更友好一些.
  • 接着上一點, 因為Compose只改了UI, 所以其他部分的代碼庫仍然是Android原生代碼的邏輯, 趁手的第三方庫比較多. 而Flutter, 就得利用其它package來做json解析, 數據庫等等.
  • Flutter的stateful widget改狀態必須要在setState里, 使用上不是很方便, 容易出錯.
  • Jetpack Compose的Recomposition看上去更智能一些, 完全自動, 不像Flutter一樣需要開發者自己設置一些flag來表明哪個部分不需要重繪.
  • 安裝包體積應該有優勢? 因為Flutter SDK包含了自己的圖像引擎, 而Compose是沒有native層面的東西的.

Flutter比Jetpack Compose好的地方:

  • Flutter還支持iOS.
  • Flutter相比Jetpack Compose稍微成熟一些(Flutter出來的早一些), 是經過一些app的實際上線驗證的.
  • Flutter是有自己的圖形引擎Skia的, 繪制會更有效率? (沒有驗證, 純猜測)

Reference


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM