
背景
上一篇介紹了 Android Jetpack 組件 LiveData,LiveData是在Lifecycle 的幫助下,實作了生命周期管理的一致性,將資料變更的通知控制在生命周期活躍狀態 STARTED、RESUMED(注意這是Lifecycle 中的 State)時進行通知,該通知成為資料的唯一可信來源,也就是視圖獲取資料的唯一入口,LiveData 經常和 ViewModel 一起配合使用,
定義
下面我們來介紹下一個 Android Jetpack 的下一個組件 ViewModel,先來看官方的定義:ViewModel 類旨在以注重生命周期的方式存盤和管理界面相關的資料,ViewModel類讓資料可在發生螢屏旋轉等配置更改后繼續留存,
在我看來,ViewModel類讓資料可在螢屏發生等配置更改后繼續留存,比如在界面因配置改變重新創建后 ViewModel 依舊持有原先的資料,這個功能當然很重要,但還有一個同樣重要的功能是在 Fragment 之間共享資料,最后由于其管理方式(單向依賴,只有 Activity/Fragment 持有 ViewModel),也避免了記憶體泄漏的發生,同時 ViewModel 配合 kotlin 協程,將加載器替換為 ViewModel,這些也是 ViewModel 能發揮重要作用的地方,
基本使用
匯入依賴
// LiveData & ViewModel 因為這兩者通常都一起使用
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
簡單使用
Android Jetpack 架構組件為界面控制器提供了 ViewModel 輔助程式類,該類負責為界面準備資料,在配置更改期間會自動保留 ViewModel 物件,以便他們存盤的資料立即可供下一個 Activity/Fragment 實體使用,下面來看個例子:
ViewModel 代碼如下:
class AccountViewModel: ViewModel() {
private val _userAgeLiveData: MutableLiveData<String> = MutableLiveData()
val userAgeLiveData: LiveData<String>
get() = _userAgeLiveData
fun loadUserName(userId: String){
val accountRepository = AccountRepository()
Log.i("ViewModel=====", "loadUserName: ")
viewModelScope.launch {
//suspend 方法,2s 后獲取用戶資訊
val result = accountRepository.requestUserInfo(userId)
// 賦值
_userAgeLiveData.value = result
}
}
override fun onCleared() {
super.onCleared()
Log.i("ViewModel=====", "onCleared: ")
}
}
Activity 代碼如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txtUserName = findViewById(R.id.txtUserName)
Log.i(TAG, "onCreate: ")
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
findViewById<View>(R.id.btnGetUserInfo).setOnClickListener {
accountViewModel.loadUserName("jackie")
}
accountViewModel.userAgeLiveData.observe(this, Observer {
//通知UI,UI 進行操作
Log.i(TAG, "onCreate: ========observe change=========")
txtUserName.text = it
})
}
點擊按鈕后獲取用戶資訊,執行了 ViewModel 中的方法loadUserName
jetpackdemo I/ViewModel=====: loadUserName:
jetpackdemo I/AccountActivity=====: onCreate: ========observe change=========
配置變化后(螢屏旋轉后),日志列印如下:
jetpackdemo I/AccountActivity=====: onPause:
jetpackdemo I/AccountActivity=====: onDestroy:
jetpackdemo I/AccountActivity=====: onCreate:
jetpackdemo I/AccountActivity=====: onStart:
jetpackdemo I/AccountActivity=====: onCreate: ========observe change=========
可以看到 Activity 重新創建了,我們并沒有點擊按鈕執行loadUserName方法,但是卻回呼了Observer 的 onChange 方法;而且 ViewModel 的 onClear方法并沒有執行,說明 ViewModel 并沒有銷毀重建,
注意
ViewModel 的創建方式使用的是
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
而不是
val accountViewModel = AccountFactory().create(AccountViewModel::class.java)
class AccountFactory: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AccountViewModel::class.java)){
return AccountViewModel() as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Fragment 間資料共享
Activity 中的兩個或更多 Fragment 互相通信是一種很常見的需求,假設你有一個 Fragment,在該 Fragment 中,用戶從串列中選擇一項,還有另一個 Fragment,用于顯示選定項的內容,這種情況不太容易處理,因為這兩個 Fragment 都需要定義某種介面描述,并且所有者 Activity 必須將兩者系結在一起,此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未創建或不可見的情況,
可以使用 ViewModel物件解決這個常見的難點,這兩個 Fragment 可以使用其 Activity 范圍共享 ViewModel來處理此類通信,代碼如下:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}
這兩個 Fragment 都會檢索包含它們的 Activity,這樣,當這兩個 Fragment 各種獲取 ViewModelProvider時,他們會收到相同的SharedViewModel實體(其范圍限定為該 Activity),
此方法具有以下優勢:
- Activity 不需要執行任何操作,也不需要對此通信有任何了解,
- 除了
SharedViewModel約定之外,Fragment 不需要相互了解,如果其中一個 Fragment 消失,另一個 Fragment 將繼續照常作業, - 每個 Fragment 都有自己的生命周期,而不受另一個 Fragment 的生命周期的影響,如果一個 Fragment 替換另一個 Fragment,界面將繼續作業而沒有任何問題,
ViewModel 的生命周期
ViewModel物件存在的時間范圍是獲取ViewModel時傳遞給 VIewModelProvider 的 Lifecycle,也就是上面我們呼叫的代碼:
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
這里的this是Activity實體物件,因為我們的Activity實作了 Lifecycle 的關聯,ViewModel將一直留在記憶體中,直到限定其存在時間范圍的 Lifecycle永久消失:對于 Activity,是在 Activity 完成時;而對于 Fragment,是在 Fragment 分離時,
下圖是 Activity 在螢屏旋轉而后結束時所處的各種生命周期狀態,該圖還在關聯 Activity 生命周期的旁邊顯示了 ViewModel的生命周期,這些基本狀態同樣適用于 Fragment 的生命周期,

通常是在系統首次呼叫 Activity 物件的 onCreate()時請求ViewModel,ViewModel存在的時間范圍是從首次請求ViewModel直到 Activity 完成并銷毀,
原始碼分析
在分析原始碼前,我先提出兩個問題,ViewModel 是如何做到讓資料可在發生螢屏旋轉等配置更改后繼續留存(也就是說ViewModel 實體依然存在)?Fragment 之間是如何通過 ViewModel 共享資料的?
先來看看ViewModel類
public abstract class ViewModel {
···
private volatile boolean mCleared = false;
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
//該方法將會在 ViewModel 被清除時呼叫,可以在這個方法里做一些取消注冊,防止記憶體泄漏
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
···
onCleared();
}
···
}
ViewModel 只是一個抽象類,clear()方法會在 ViewModel 被清除時呼叫有,用戶可以通過重寫 onCleared()方法來處理一些額外的操作,
再從呼叫處開始分析
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
我們再來看看ViewModelProvider這個類:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
最終呼叫到的構造器都是第三個構造器,再來看看ViewModelStore和Factory是什么,因為我們傳入的是 Activity(是ViewModelStoreOwner和HasDefaultViewModelProviderFactory的實作者),所以才會有owner.getViewModelStore(),第二個引數是getDefaultViewModelProviderFactory(),
ViewModelStoreOwner可以理解為 ViewModelStore(ViewModel 存盤器)的擁有者,也就是說我們的 Activity/Fragment 是 ViewModel 存盤器的擁有者,
然后我們來看看看ViewModelStore是什么?
// 存盤 ViewModel
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
可以看到ViewModelStore的代碼很簡單,就是用一個 HashMap 存盤了key(String)和value(ViewModel),這里的 key 的名字為
// key 的名字
DEFAULT_KEY + ":" + canonicalName
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
String canonicalName = modelClass.getCanonicalName();
因為第二個引數是通過getDefaultViewModelProviderFactory()獲取到的,前面說過Activity是 HasDefaultViewModelProviderFactory的實作類,我們再來看看Activity中的getDefaultViewModelProviderFactory()方法
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
可以看到是通過一個SavedStateViewModelFactory來獲取ViewModelProvider.Factory,命名也很清晰直觀,就是保存狀態的 ViewModel 工廠,
下面我們再來看看get(AccountViewModel::class.java)方法
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass); //key的名字
}
@SuppressWarnings("unchecked")
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //從 hashmap 中獲取 viewmodel 實體
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//使用 Factory 創建
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//存入 viewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
簡單來說就是從mViewModelStore獲取 ViewModel,如果沒有獲取到,就使用 Factory 創建,然后存入mViewModelStore,
這樣邏輯就清楚了,ViewModelProvider(this).get(AccountViewModel::class.java)會把ViewModel存入ViewModelStore中,
因為 Activity 實作了ViewModelStoreOwner 介面,可以理解為 ViewModelStore(ViewModel 存盤器)的擁有者,也就是說我們的 Activity/Fragment 是 ViewModel 存盤器的擁有者,
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
然后我們來看看該方法在 Activity 中的實作
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
//從getLastNonConfigurationInstance 獲取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//恢復viewmodelstore
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果還是獲取不到,就新建一個
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
從該方法中可以看到,Activity(ViewModelStoreOwner)內部最侄訓創建一個ViewModelStore,用來存盤ViewModel,接下來我們來看getLastNonConfigurationInstance方法
//ComponentActivity.java
NonConfigurationInstances mLastNonConfigurationInstances;
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
為什么使用 getLastNonConfigurationInstance 方法呢,我們先來看看 Activity 狀態保存和恢復:
onSaveInstanceState和onRetainNonConfigurationInstance 的使用場景區別
我們知道,在螢屏旋轉的時候會執行保存狀態和恢復狀態的方法
@Override
protected void onSaveInstanceState(Bundle outBundle) { //保存
super.onSaveInstanceState(outBundle);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) { //恢復
super.onRestoreInstanceState(savedInstanceState);
}
//注意,如果你是繼承 AppcompactActiviy,該方法已經在它的父類 ComponentActivity 定義為 final,子類無法重寫
//繼承 Activity 就可以
public Object onRetainNonConfigurationInstance() {
// TODO Auto-generated method stub
// 在這里設定需要保存的內容,在切換時不是bundle了,我們可以直接通過object來代替,
return super.onRetainNonConfigurationInstance();
}
@Nullable
@Override
public Object getLastNonConfigurationInstance() {
return super.getLastNonConfigurationInstance();
}
在 Android 10,他們的執行順序都在onStop和onDestory之間,而且onSaveInstanceState比onRetainNonConfigurationInstance先執行,一般情況我們保存的資料不是太大,適合放在 Bundle 中,這個時候使用onSaveInstanceState比較合適,如果要保存的資料不適合放在 Bundle 中(比如: 一個socket)或是資料比較大(比如 Bitmap),那么這個時間我們就應該使用onRetainNonConfigurationInstance(),而且我們使用onRetainNonConfigurationInstance()可以保存任何型別的物件,像AsyncTask和SQLiteDatabse,我們都可以進行保存,這些型別的資料可能會被一個新的Activity重新使用,
也就是說 Bundle 中只能放一些特定的型別,比如基本資料型別,陣列,Serialable 物件,而onRetainNonConfigurationInstance中只要是個 Object 物件就可以了,
同時當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,而onRetainNonConfigurationInstance更多的是時候是在配置改變時操作的,這個時候保存一些不會因為配置改變而發生改變的東西,而且onSaveInstanceState資料是序列化保存到磁盤中,而onRetainNonConfigurationInstance保存的資料是存在記憶體中,
所以這里我們的 ViewModel 肯定是放在onRetainNonConfigurationInstance方法中,再來看看
//ComponentActivity.java
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//新建 NonConfigurationInstances,將 viewModelStore 保存
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
可以看到在該方法中保存了viewModelStore,保存在了NonConfigurationInstances,而該getLastNonConfigurationInstance的真正實作是在 Activity.java 類中
//Activity.java
//靜態內部類
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
NonConfigurationInstances mLastNonConfigurationInstances;
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
而mLastNonConfigurationInstances的賦值是在attach方法中的mLastNonConfigurationInstances = lastNonConfigurationInstances;,
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,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
···
mLastNonConfigurationInstances = lastNonConfigurationInstances;
···
為什么ViewModel 在配置變化后依舊存在?
而attach中的傳入的引數lastNonConfigurationInstances是ActivityClientRecord的一個變數,而ActivityClientRecord是存在ActivityThread的mActivities(ArrayMap)中,ActivityThread中的ActivityClientRecord不受activity重建的影響,所以ActivityThread中的lastNonConfigurationInstances同樣也不受影響,所以ComponentActiviy中的NonConfigurationInstances也無影響,所以ViewModelStore不受影響,最終ViewModel在Activity重新創建后呼叫getLastNonConfigurationInstance獲取得到,這也是ViewModel一直存在的原因,
在更早之前版本保存 ViewModel 是使用HolderFragment的,Fragment 中的setRetainInstance(boolean)在設定為 true 時可以是當前的 Fragment 在 Activity 重建時存盤下來,所以可以在 Activity 中注入一個 Fragment,這樣就可以達到保存 ViewModel 的功能了,詳情可以參考這篇文章,
Fragment 之間是如何通過 ViewModel 共享資料的?
從上面的分析,我們知道ViewModel存在Activity的ViewModelStore中,多個 Fragment 依賴于同一個 Activity,這個時候拿到同一個ViewModel自然就不是問題了,
為什么旋轉后所有的 LiveData 會重新執行一次通知
原因很簡單,因為 LiveData 的事件是粘性事件,也就是說當 Activity 銷毀后,因為 ViewModel 中的 LiveData 并沒有銷毀(有具體的值),在 Activity 重新創建后,LiveData 會將該值發送給當前 Activity 界面,達到恢復 Activity 界面狀態的效果,
ViewModel 如何避免記憶體泄漏
在ComponentActivity的構造器中可以看出,getLifecycle().addObserver添加了一個觀察者觀察界面是否銷毀,一旦銷毀,就清空ViewModelStore中的所有ViewModel,
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear(); //界面執行 onDestroy 方法是,清空 viewmodel
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
ViewModel 和協程一起使用
ViewModel 支持協程,ViewMdoelScope 為應用中的每個ViewModel定義了ViewMdoelScope,如果 ViewModel 已清除,則在此范圍內啟動的協程都會自動取消,如果您具有僅在 ViewModel 處于活動狀態時才需要完成的作業,此時協程非常有用,例如,如果要為布局計算某些資料,則應將作業范圍限定至 ViewModel,以便在 ViewModel 清除后,系統會自動取消作業以避免消耗資源,
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
Android Jetpack 的這些架構組件配合起來會非常強大,大大節省開發者的開發時間,同時還能輕松避免記憶體泄漏,簡直就是開發利器,
總結
ViewModel 是個非常好用的 Android Jetpack 組件,可在發生螢屏旋轉等配置更改后繼續留存,同時還能在不同 Fragment 之間共享,在配合協程使用時也非常方便,還能輕松避免記憶體泄漏,
需要注意的是,不少開發者會將 ViewModel 實作 LifecycleObserver 介面,把 ViewModel 當做一個能感應生命周期變化的組件,在感知到生命周期的方法中執行loadData之類的操作,然后通過 LiveData 去通知 UI 做出對應的改變,這樣的做法喪失了官方為我們設計的 ViewModel 的初衷,反而有點不倫不類了,而且 ViewModel 配合協程也非常方便,也有很多人將網路請求也放到 ViewModel 去呼叫,個人感覺也是很不合適的,可以參考官方的Demo進行設計,
文末的話
最近火熱的Jetpack Compose是谷歌在2019Google i/o大會上發布的新的庫,是用于構建原生Android UI的現代工具包,他有強大的工具和直觀的Kotlin API,簡化并加速了Android上的UI開發,可以幫助開發者用更少更直觀的代碼創建View,還有更強大的功能,以及還能提高開發速度,
客觀地講,Compose 確實是一套比較難學的東西,因為它畢竟太新也太大了,它是一個完整的、全新的框架,確實讓很多人感覺「學不動」,這也是個事實,
如果你是因為缺少學習資料,而我正好薅到這本谷歌內部大佬根據實戰撰寫的《Jetpack Compose最全上手指南》,從入門到精通,教程通俗易懂,實體豐富,既有基礎知識,也有進階技能,能夠幫助讀者快速入門,是你學習Jetpack Compose的葵花寶典,快收藏起來!!!
由于篇幅原因,如有需要以下完整學習筆記PDF,可以點擊我的GitHub免費下載獲取!
第一章 初識 Jetpack Compose
1. 為什么我們需要一個新的UI 工具?
2. Jetpack Compose的著重點
- 加速開發
- 強大的UI工具
- 直觀的Kotlin API

3. API 設計

4. Compose API 的原則
- 一切都是函式
- 頂層函式(Top-level function)
- 組合優于繼承
- 信任單一來源

5. 深入了解Compose
- Core
- Foundation
- Material

- 插槽API
第二章 Jetpack Compose構建Android UI
1. Android Jetpack Compose 最全上手指南
- Jetpack Compose 環境準備和Hello World
- 布局
- 使用Material design 設計
- Compose 布局實時預覽
……

2. 深入詳解 Jetpack Compose | 優化 UI 構建
- Compose 所解決的問題
- Composable 函式剖析
- 宣告式 UI
- 組合 vs 繼承
- 封裝
- 重組
……

3. 深入詳解 Jetpack Compose | 實作原理
- @Composable 注解意味著什么?
- 執行模式
- Positional Memoization (位置記憶化)
- 存盤引數
- 重組
……

第三章 Jetpack Compose 專案實戰演練(附Demo)
1. Jetpack Compose應用1
- 開始前的準備
- 創建DEMO
- 遇到的問題

2. Jetpack Compose應用2
3. Jetpack Compose應用做一個倒計時器
- 資料結構
- 倒計時功能
- 狀態模式
- Compose 布局
- 繪制時鐘

4. 用Jetpack Compose寫一個玩安卓App
- 準備作業
- 引入依賴
- 新建 Activity
- 創建 Compose
- PlayTheme
- 畫頁面
- 底部導航欄
- 管理狀態
- 添加頁面

5. 用Compose Android 寫一個天氣應用
- 開篇
- 畫頁面
- 畫背景
- 畫內容
……

6. 用Compose快速打造一個“電影App”
- 成品
- 實作方案
- 實戰
- 不足
……

由于篇幅原因,如有需要以上完整學習筆記PDF,可以點擊我的GitHub免費下載獲取!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/281309.html
標籤:其他
上一篇:負載均衡演算法居然有這么多種!!!負載均衡演算法總結
下一篇:二叉樹系列匯總,持續更新!
