Android業務架構 · 基礎篇 · Jetpack四件套

語言: CN / TW / HK

highlight: a11y-dark theme: orange


【小木箱成長營】Android業務架構系列文章:

Android業務架構 · 提高篇 · MVC、MVP、MVVM和MVI四劍客

Android業務架構 · 實踐篇 · MVI+Jetpack+Kotlin手把手搭建直播應用App

Tips: 關注小木箱成長營公眾號, 回覆"業務架構"可免費獲取Android業務架構思維導圖。

一、序言

Hello,我是小木箱,歡迎來到小木箱成長營業務架構系列教程,今天分享的內容是業務架構 · 基礎篇 · Jetpack四件套。

2017年,Google發佈了Android Architecture Components,包括Room、LiveData、ViewModel和Paging等組件,旨在幫助開發者更輕鬆地實現MVVM架構。

2018年,Google在I/O大會上推出的一套Android開發組件庫,旨在幫助開發者更輕鬆、更高效地構建Android應用。

隨着時間的推移,Android Jetpack不斷地更新和增加新的組件,使得Android應用的開發更加高效、穩定和可維護。

今天的主題主要分為三個維度。第一個維度是4W2H分析Jetpack,第二個維度是Jetpack四件套。第三個維度是總結與展望。

其中,4W2H分析Jetpack主要針對Jetpack提出了6個高價值問題。

其中,Jetpack四件套列舉了LifeCycle、LiveData、ViewModel和DataBing四種常見的Jetpack工具包。

如果學完業務架構系列教程,那麼任何人都能完整構建一套適合企業業務背景的架構設計。

# 二、4W2H分析Jetpack

2.1 What: Jetpack是什麼?

Android Jetpack是一組Android軟件組件、工具和指南,它們可以幫助開發者構建高質量、穩定的Android應用程序。Jetpack中包含多個庫,它們旨在解決Android應用程序開發中的常見問題,並提供一致的API和開發體驗。

Jetpack中包含的庫包括: 1. ViewModel:幫助管理UI組件的生命週期並存儲和管理UI相關的數據。 1. LiveData:提供了響應式編程的功能,可以讓數據在數據源發生變化時自動更新UI。 1. Room:提供了一個抽象層,可以讓開發者方便地訪問和管理SQLite數據庫。 1. Navigation:提供了一種簡單、一致的方式來處理應用程序的導航。 1. WorkManager:提供了一種簡單、可靠的方式來管理後台任務。 除此之外,Jetpack還包括了諸如Paging、Data Binding、Preferences、Security等庫,這些庫都旨在簡化開發過程並提高應用程序的性能和可靠性。

2.2 Where: 什麼場景下使用Jetpack?

Jetpack適用於開發各種類型的Android應用程序,包括單頁面應用程序、多頁面應用程序、後台任務應用程序等。下面是一些適合使用Jetpack的場景: 1. 構建大型應用程序:Jetpack提供了一些庫,如ViewModel、LiveData和Navigation,可以幫助開發者更好地管理應用程序的生命週期、狀態和導航,使得構建大型應用程序更加容易。

  1. 處理後台任務:Jetpack中的WorkManager庫提供了一種簡單、可靠的方式來管理後台任務,如數據同步、推送通知、文件上傳等。
  2. 數據庫訪問:Jetpack中的Room庫提供了一個抽象層,可以讓開發者方便地訪問和管理SQLite數據庫,使得數據存儲和訪問更加容易。
  3. 響應式編程:Jetpack中的LiveData和Data Binding庫提供了響應式編程的功能,可以讓數據在數據源發生變化時自動更新UI,提高應用程序的性能和可靠性。
  4. 代碼重用:Jetpack中的各種庫都旨在解決Android應用程序開發中的常見問題,並提供一致的API和開發體驗,使得代碼重用更加容易。

2.3 Why: 為什麼使用Jetpack?

以下是使用Jetpack的一些好處: 1. 更好的代碼組織和可維護性:Jetpack提供了一組庫,這些庫旨在解決Android應用程序開發中的常見問題,如生命週期管理、數據存儲、後台任務處理等。使用Jetpack可以使代碼更加模塊化,易於維護。 1. 更好的代碼可讀性:Jetpack提供了一致的API和開發體驗,可以使代碼更加易於理解和閲讀。 1. 更好的開發效率:Jetpack提供了一些庫和工具,如Navigation和Data Binding,可以幫助開發者更快地開發Android應用程序,提高開發效率。 1. 更好的性能和可靠性:Jetpack中的一些庫,如LiveData和WorkManager,提供了響應式編程和後台任務處理的功能,可以提高應用程序的性能和可靠性。 1. 更好的向後兼容性:Jetpack中的一些庫,如ViewModel和LiveData,提供了對Android不同版本的向後兼容性支持,可以使開發者更容易地編寫適用於不同版本的Android系統的應用程序。 綜上所述,Jetpack可以幫助開發者更好地組織代碼、提高開發效率、提高應用程序的性能和可靠性,並提供了對不同版本的Android系統的向後兼容性支持。

2.4 Who: 什麼樣的團隊該使用Jetpack?

Jetpack適用於各種規模的Android開發團隊,特別是那些希望提高應用程序質量、開發效率和可維護性的團隊。以下是一些團隊適合使用Jetpack的場景:

  1. 大型團隊:Jetpack提供了一致的API和開發體驗,可以使大型團隊更容易協作開發,提高團隊的開發效率和代碼質量。
  2. 跨職能團隊:Jetpack提供了一些庫和工具,如Navigation和Data Binding,可以幫助開發者更快地開發Android應用程序,使得跨職能團隊之間更容易協作。
  3. 需要提高應用程序性能和可靠性的團隊:Jetpack中的一些庫,如LiveData和WorkManager,提供了響應式編程和後台任務處理的功能,可以提高應用程序的性能和可靠性。
  4. 需要提高代碼可維護性的團隊:Jetpack提供了一些庫,如ViewModel和Room,可以幫助開發者更好地管理應用程序的狀態和數據,使得代碼更易於維護。
  5. 需要保持向後兼容性的團隊:Jetpack中的一些庫,如ViewModel和LiveData,提供了對Android不同版本的向後兼容性支持,可以使開發者更容易地編寫適用於不同版本的Android系統的應用程序。   綜上所述,Jetpack適合各種規模和類型的Android開發團隊,特別是那些希望提高應用程序質量、開發效率和可維護性的團隊。 ##   2.5 How: 怎樣使用Jetpack?   以下是使用Jetpack的一般步驟:
  6. 添加Jetpack庫:Jetpack庫可以通過在build.gradle文件中添加依賴項的方式進行添加。例如,添加Lifecycle庫的依賴項: ```

    dependencies { def lifecycle_version = "2.3.1"// ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"// LiveData implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"// Lifecycle implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" } ``` 使用Jetpack庫:在添加Jetpack庫後,就可以在應用程序中使用Jetpack庫提供的功能了。例如,使用ViewModel庫創建一個ViewModel類:

```

import androidx.lifecycle.ViewModel

class MyViewModel : ViewModel() {
    // Add ViewModel logic here
}

```

結合Jetpack組件使用:Jetpack庫提供的組件可以結合使用,以提高應用程序的開發效率和可維護性。例如,使用ViewModel庫和LiveData庫實現一個響應式的用户界面:

```

class MyActivity : AppCompatActivity() {

    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        // Get a reference to the ViewModel
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // Observe a LiveData object in the ViewModel
        viewModel.someData.observe(this, Observer {
            // Update the UI with the new data
        })
    }
}

``` 以上是使用Jetpack的一般步驟。需要根據具體的Jetpack庫和應用程序需求進行相應的配置和代碼實現。

2.6 How Much: 使用Jetpack業務價值

使用Jetpack可以帶來以下業務價值:

  1. 提高開發效率:Jetpack提供了一些開發工具和庫,例如ViewModel、LiveData和Room,可以減少重複的編寫代碼,簡化開發流程,並提高開發效率。
  2. 提高應用程序質量:Jetpack提供了一致的API和開發體驗,可以減少由於人為因素引起的代碼錯誤,提高應用程序的質量。
  3. 提高應用程序性能:Jetpack中的一些庫,例如Lifecycle和WorkManager,提供了響應式編程和後台任務處理的功能,可以提高應用程序的性能和響應速度。
  4. 簡化應用程序架構:Jetpack提供了一些組件和庫,例如ViewModel和Data Binding,可以幫助開發者更好地管理應用程序的狀態和數據,並簡化應用程序的架構。
  5. 支持向後兼容性:Jetpack中的一些庫,例如ViewModel和LiveData,提供了對Android不同版本的向後兼容性支持,可以使開發者更容易地編寫適用於不同版本的Android系統的應用程序。

綜上所述,使用Jetpack可以帶來多種業務價值,可以提高應用程序的質量、性能和開發效率,同時簡化應用程序架構和支持向後兼容性,可以使應用程序更易於維護和升級。

三、Jetpack四件套

3.1 LifeCycle

3.1.1 LifeCycle基礎定義

Android Jetpack Lifecycle是Android Jetpack組件庫中的一部分,Lifecycle是基於Android Framework中的Lifecycle概念而構建的。

Lifecycle提供了一種輕鬆管理組件(如Activity和Fragment)生命週期的方式,同時也支持自定義組件的生命週期。

Jetpack Lifecycle提供了一組類和接口,使得開發者可以在組件的生命週期各個階段執行相應的操作。

這些類和接口包括:

  • LifecycleOwner: 擁有生命週期的對象,通常是Activity和Fragment。
  • LifecycleObserver: 監聽組件的生命週期事件的觀察者對象。
  • Lifecycle: 組件的生命週期,包括CREATED、STARTED、RESUMED、PAUSED、STOPPED、DESTROYED等狀態。
  • LiveData: 一個可觀察的數據容器,可以在組件生命週期的不同階段更新數據。

使用Jetpack Lifecycle,可以更容易地避免內存泄漏和其他生命週期相關的問題。

例如,可以在組件被銷燬時自動釋放資源、取消網絡請求等操作。

此外,Jetpack Lifecycle還提供了一種方式來創建自定義的生命週期狀態,以更好地滿足App的需求。

總之,Jetpack Lifecycle是Android Jetpack組件庫中的一個重要組件,可以幫助開發者更輕鬆地管理組件的生命週期,從而提高App的質量和性能。

3.1.2 LifeCycle基礎使用

在App的主Activity中實現一個簡單的計時器,當Activity處於前台時,計時器會不斷遞增,當Activity被銷燬時,計時器將停止。

具體實現步驟如下:

  1. 在gradle文件中添加Jetpack組件庫的依賴。

```

dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.4.0" } ```

  1. 創建一個名為Timer的Java類,並實現LifeCycleObserver接口。

```

public class Timer implements LifecycleObserver { private Handler handler; private int seconds = 0;

@OnLifecycleEvent(Lifecycle.Event.ON_START)public void startTimer() {
    handler = new Handler();
    handler.post(new Runnable() {
        @Overridepublic void run() {
            Log.d("Timer", "Seconds: " + seconds);
            seconds++;
            handler.postDelayed(this, 1000);
        }
    });
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)public void stopTimer() {
    handler.removeCallbacksAndMessages(null);
    handler = null;
}

} ```

  1. 在MainActivity中添加LifecycleOwner,並在onCreate方法中添加Observer。

```

public class MainActivity extends AppCompatActivity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 獲取LifecycleOwner對象LifecycleOwner lifecycleOwner = this;

    // 將Timer實例添加為Observer
    getLifecycle().addObserver(new Timer());

    // ...
}

// ...

} ```

這樣,當Activity處於前台時,Timer實例中的startTimer方法會被調用,計時器會開始遞增;

當Activity被銷燬時,Timer實例中的stopTimer方法會被調用,計時器會停止。

這個例子展示瞭如何使用Jetpack LifeCycle組件來管理App組件的生命週期。

當App中存在需要在組件生命週期不同階段執行的操作時,使用LifeCycle可以更方便地實現這些操作,同時避免了一些常見的生命週期問題。

3.1.3 LifeCycle優勢劣勢

優勢

  1. 管理生命週期方便

使用LifeCycle組件可以更方便地管理App組件的生命週期,避免了一些常見的生命週期問題,如內存泄漏和空指針異常等。

  1. 模塊化編程

使用LifeCycle組件可以將App的業務邏輯分解為模塊化的組件,每個組件負責管理自己的生命週期,便於代碼複用和維護。

  1. 規範化編程

使用LifeCycle組件可以規範化App的開發,使代碼更易於閲讀、理解和維護。

  1. 支持多個組件

LifeCycle組件支持多個組件進行生命週期管理,可以輕鬆地在多個組件之間共享狀態和數據。

劣勢

  1. 需要繼承LifecycleObserver:在實現LifeCycle功能的時候,需要繼承LifecycleObserver接口,這會導致代碼的繼承關係稍微有點複雜。
  2. 需要添加註釋:在使用LifeCycle組件的時候,需要添加一些註釋來指示方法是在什麼時候被調用,否則可能會出現一些難以診斷的問題。

3.1.4 LifeCycle應用場景

Jetpack LifeCycle組件的實際開發應用場景包括:

  1. Activity和Fragment生命週期管理:使用LifeCycle組件可以更方便地管理Activity和Fragment的生命週期,避免了一些常見的生命週期問題,如內存泄漏和空指針異常等。
  2. 後台服務管理:使用LifeCycle組件可以更方便地管理後台服務的生命週期,可以在App退出後自動停止後台服務,避免了一些不必要的資源浪費。
  3. 數據庫連接管理:使用LifeCycle組件可以更方便地管理數據庫連接的生命週期,可以在App退出時自動關閉數據庫連接,避免了一些不必要的資源浪費。
  4. 網絡請求管理:使用LifeCycle組件可以更方便地管理網絡請求的生命週期,可以在Activity或Fragment銷燬時自動取消網絡請求,避免了一些不必要的網絡請求。
  5. 視圖控制器管理:使用LifeCycle組件可以更方便地管理視圖控制器的生命週期,可以在Activity或Fragment銷燬時自動清除視圖控制器的狀態,避免了一些不必要的狀態保存和恢復操作。

3.1.5 LifeCycle原理分析

類圖

LifecycleOwner表示擁有生命週期的組件,比如Activity和Fragment。

Lifecycle表示組件的生命週期,LifecycleObserver表示一個組件的生命週期觀察者。

LifecycleRegistry是Lifecycle接口的一個實現類,它維護了一個生命週期狀態機,用於記錄組件的生命週期狀態和生命週期事件。

LifecycleRegistry提供了一系列方法,用於管理組件的生命週期狀態和生命週期事件。當組件的生命週期事件發生變化時,LifecycleRegistry會自動更新狀態機,並通知所有的LifecycleObserver觀察者對象,以便它們可以相應地更新自己的狀態。

LifecycleOwner可以通過getLifecycle()方法獲取到一個Lifecycle對象,然後將自己的生命週期觀察者對象添加到Lifecycle對象中,從而實現對組件生命週期的監聽。

當組件的生命週期事件發生變化時,Lifecycle會自動通知所有的生命週期觀察者對象,以便它們可以相應地更新自己的狀態。

image.png

源碼

Lifecycle庫的核心是Lifecycle接口和LifecycleObserver接口。

Lifecycle接口定義了一組方法,用於將LifecycleOwner與LifecycleObserver進行關聯。

``` public abstract class Lifecycle { //添加觀察者 @MainThread public abstract void addObserver(@NonNull LifecycleObserver observer); //移除觀察者 @MainThread public abstract void removeObserver(@NonNull LifecycleObserver observer); //獲取當前狀態 public abstract State getCurrentState();

//生命週期事件,對應Activity生命週期方法 public enum Event { ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY //可以響應任意一個事件 }

//生命週期狀態. (Event是進入這種狀態的事件)
public enum State {
    DESTROYED,
    INITIALIZED,
    CREATED,
    STARTED,
    RESUMED;

    //判斷至少是某一狀態
    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
}

```

LifecycleObserver接口定義了一組回調方法,用於接收LifecycleOwner的生命週期事件。

在Lifecycle庫的實現中,Lifecycle接口有兩個重要的實現類,分別是LifecycleRegistry和LifecycleOwner。

LifecycleRegistry實現了Lifecycle接口,並提供了一組方法,用於管理LifecycleOwner的生命週期狀態。

LifecycleOwner是一個接口,用於標識擁有生命週期狀態的對象,通常是Activity或Fragment。

``` //androidx.activity.ComponentActivity,這裏忽略了一些其他代碼,我們只看Lifecycle相關 public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner{ ...

private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedStateRegistryController.performRestore(savedInstanceState);
    ReportFragment.injectIfNeededIn(this); //使用ReportFragment分發生命週期事件
    if (mContentLayoutId != 0) {
        setContentView(mContentLayoutId);
    }
}
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    Lifecycle lifecycle = getLifecycle();
    if (lifecycle instanceof LifecycleRegistry) {
        ((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
    }
    super.onSaveInstanceState(outState);
    mSavedStateRegistryController.performSave(outState);
}

@NonNull
@Override
public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}

} ```

在LifecycleRegistry中,有一個名為mObserverMap的成員變量,用於存儲LifecycleObserver對象和其關聯的EventObserver對象。

當LifecycleOwner的生命週期狀態更改時,LifecycleRegistry會自動調用mObserverMap中與之相關聯的EventObserver對象的相應方法,以便它們可以執行適當的操作。

``` //LifecycleRegistry.java //系統自定義的保存Observer的map,可在遍歷中增刪 private FastSafeIterableMap mObserverMap = new FastSafeIterableMap<>();

public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
    State next = getStateAfter(event);//獲取event發生之後的將要處於的狀態
    moveToState(next);//移動到這個狀態
}

private void moveToState(State next) {
    if (mState == next) {
        return;//如果和當前狀態一致,不處理
    }
    mState = next; //賦值新狀態
    if (mHandlingEvent || mAddingObserverCounter != 0) {
        mNewEventOccurred = true;
        return;
    }
    mHandlingEvent = true;
    sync(); //把生命週期狀態同步給所有觀察者
    mHandlingEvent = false;
}

    private void sync() {
    LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
    if (lifecycleOwner == null) {
        throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                + "garbage collected. It is too late to change lifecycle state.");
    }
    while (!isSynced()) {  //isSynced()意思是 所有觀察者都同步完了
        mNewEventOccurred = false;
        //mObserverMap就是 在activity中添加observer後 用於存放observer的map
        if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
            backwardPass(lifecycleOwner);
        }
        Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
        if (!mNewEventOccurred && newest != null
                && mState.compareTo(newest.getValue().mState) > 0) {
            forwardPass(lifecycleOwner);
        }
    }
    mNewEventOccurred = false;
}
...

 static State getStateAfter(Event event) {
    switch (event) {
        case ON_CREATE:
        case ON_STOP:
            return CREATED;
        case ON_START:
        case ON_PAUSE:
            return STARTED;
        case ON_RESUME:
            return RESUMED;
        case ON_DESTROY:
            return DESTROYED;
        case ON_ANY:
            break;
    }
    throw new IllegalArgumentException("Unexpected event value " + event);
}

```

LifecycleRegistry還提供了一組方法,如handleLifecycleEvent()、getCurrentState()、addObserver()、removeObserver()等,用於管理組件的生命週期狀態和LifecycleObserver對象。

在Lifecycle庫的實現中,還有一些其他的類和接口,如GenericLifecycleObserver、FullLifecycleObserver、LifecycleEvent、EventObserver等,它們都是用於管理和處理組件生命週期事件的。

3.1.6 LifeCycle注意事項

3.1.6.1 不要在 onCreate() 方法中使用 Lifecycle 組件

Lifecycle 組件在 onCreate() 方法中尚未初始化完成,因此在該方法中使用它們可能會導致崩潰或不可預測的行為。建議在 onStart() 方法中使用 Lifecycle 組件。

3.1.6.2 不要手動調用 onDestroy() 方法

手動調用 onDestroy() 方法會破壞 Lifecycle 組件的生命週期,從而導致應用程序行為異常。Lifecycle 組件應該由系統自動管理,應該避免手動干預。

3.1.6.3 避免在 Fragment 中使用多個 LifecycleOwner

Fragment 自身就是一個 LifecycleOwner,因此不應該在 Fragment 中創建其他的 LifecycleOwner。這樣會導致多個 LifecycleOwner 之間的狀態不同步,從而導致應用程序出現問題。

3.2 LiveData

3.2.1 LiveData基礎定義

Android Jetpack LiveData是一種用於管理應用程序界面和數據交互的組件。

LiveData是一種可觀察的數據持有者,用於在應用程序組件(如Activity、Fragment和Service)之間共享數據,並在數據發生更改時通知觀察者。

LiveData可以確保UI與數據的同步更新,避免了一些常見的錯誤,如內存泄漏和UI組件無法正確更新的問題。

LiveData具有生命週期感知功能,可以自動感知應用程序組件的生命週期,並在組件處於活動狀態時更新UI,而在組件處於非活動狀態時停止更新,從而有效地減少了資源消耗。

LiveData還提供了線程安全的訪問數據的機制,避免了多線程併發訪問的問題。

3.2.2 LiveData基礎使用

如何 TextView 控件的顯示內容呢?

首先,在 XML 佈局文件中添加一個 TextView 控件:

<TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" />

然後,在 Activity 或 Fragment 中創建一個 LiveData 對象,用於更新 TextView 的顯示內容:

```

public class MyActivity extends AppCompatActivity {

private LiveData<String> mLiveData;
private TextView mTextView;

@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextView = findViewById(R.id.text_view);

    mLiveData = new MutableLiveData<>();
    mLiveData.observe(this, new Observer<String>() {
        @Overridepublic void onChanged(String s) {
            mTextView.setText(s);
        }
    });
}

} ```

在上述代碼中,我們創建了一個 LiveData 對象,並將其與一個 TextView 控件關聯。當 LiveData 對象中的值發生變化時,我們使用 Observer 來監聽這個變化,然後更新 TextView 的顯示內容。

最後,我們可以在代碼中通過 setValue() 或 postValue() 方法來更新 LiveData 對象的值,從而更新 TextView 的顯示內容。例如:

```

((MutableLiveData) mLiveData) .setValue("Hello, world!"); ```

以上就是一個簡單的 LiveData 案例,用於更新 TextView 控件的顯示內容。當 LiveData 中的值發生變化時,TextView 控件會自動更新顯示內容。

3.2.3 LiveData優勢劣勢

優勢

  1. 生命週期感知:LiveData 可以感知組件(如 Activity 或 Fragment)的生命週期,從而避免了由於 UI 組件的生命週期變化而引發的空指針異常和內存泄漏等問題。
  2. 數據更新通知:LiveData 可以在數據發生變化時自動通知所有觀察者更新數據,從而實現數據的實時更新和響應。
  3. 數據一致性:LiveData 可以確保在配置更改時保持數據的一致性,避免了數據丟失和重複加載等問題。
  4. 線程安全:LiveData 可以確保在主線程中更新 UI 界面,並支持在工作線程中進行異步操作,從而避免了多線程數據競爭問題。
  5. 與 ViewModel 結合使用:LiveData 可以與 ViewModel 結合使用,實現數據與 UI 界面的分離,從而提高了代碼的可維護性和可測試性。

劣勢

  1. 適用場景有限:LiveData 適用於數據變化頻繁、需要實時更新的場景,對於數據變化較少的場景,使用 LiveData 可能會增加代碼複雜性。
  2. API 限制:LiveData 是 Android Jetpack 組件,只能在支持 Jetpack 的 Android 版本上使用,對於一些較老的 Android 版本可能需要使用其他技術方案。

3.2.4 LiveData應用場景

LiveData的應用場景包括但不限於以下幾個方面:

  1. 數據庫操作:LiveData可以與Room持久化庫結合使用,當數據庫中的數據發生變化時,LiveData會自動通知UI組件進行更新。
  2. 網絡請求:LiveData可以與Retrofit網絡請求庫結合使用,當網絡請求的結果返回時,LiveData會自動通知UI組件進行更新。
  3. 數據共享:LiveData可以在不同的組件之間共享數據,例如,當一個Activity和一個Fragment需要共享數據時,可以將LiveData對象設置為一個公共的數據持有者。
  4. 資源釋放:LiveData可以在UI組件不再處於活動狀態時自動釋放資源,避免出現內存泄漏等問題。
  5. 代碼簡潔:LiveData可以減少代碼複雜度,通過數據觀察者的方式,避免手動編寫繁瑣的數據更新代碼。

3.2.5 LiveData原理分析

LiveData是一種可以感知生命週期的數據持有者,它可以讓數據更新時通知UI界面進行更新,同時也能夠避免因為生命週期問題帶來的內存泄漏。

類圖

LiveData的簡化類圖:

image.png

在上面的類圖中,Observer是LiveData的觀察者接口,LiveData是可觀察數據的持有者類。LiveData具有生命週期感知能力,可以根據其生命週期狀態自動管理數據的訂閲和取消訂閲。MutableLiveData是LiveData的可變子類,允許更新LiveData持有的數據。LiveData的observe()方法用於註冊觀察者,setValue()和postValue()方法用於更新LiveData數據。

源碼

LiveData的核心代碼在androidx.lifecycle.LiveData類中,下面對LiveData的源碼進行簡要分析:

  1. LiveData的基本結構

LiveData類是一個抽象類,它有一個泛型類型T,表示LiveData中存儲的數據類型。LiveData類內部維護了一個數據源(mData)和一個觀察者列表(mObservers),當LiveData中的數據發生改變時,會通知所有註冊的觀察者進行UI更新。

```

public abstract class LiveData { private static final Object NOT_SET = new Object(); private Object mData; private boolean mDispatchingValue; private int mActiveCount; private volatile Object mPendingData = NOT_SET; private volatile Object mVersion = new Object(); private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); private final SafeIterableMap, ObserverWrapper> mObservers = new SafeIterableMap<>();

// ...

} ```

  1. 觀察者註冊

LiveData中的觀察者是通過observe()方法進行註冊的,這個方法接受一個LifecycleOwner對象和一個Observer對象。LifecycleOwner是一個具有生命週期的對象,當LifecycleOwner的生命週期結束時,LiveData會自動解註冊所有與該LifecycleOwner相關的觀察者,避免內存泄漏。

```

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { if (owner.getLifecycle().getCurrentState() == DESTROYED) { // ignorereturn; } // ...LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // ... } ```

  1. 觀察者解註冊

解註冊則是通過removeObserver()方法進行的,該方法接受一個Observer對象,用於從觀察者列表中刪除相應的觀察者。

```

public void removeObserver(@NonNull Observer<? super T> observer) { assertMainThread("removeObserver"); // ...mObservers.remove(observerWrapper); // ... } ```

  1. 數據更新和通知觀察者

LiveData中的數據更新是通過setValue()和postValue()方法進行的。setValue()方法是在主線程中進行調用的,它會直接更新LiveData中的數據並通知所有的觀察者進行UI更新;而postValue()方法是在異步線程中進行調用的,它會將要更新的數據封裝成PendingPost對象,並提交給主線程的Handler進行處理。

```

protected void setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); }

protected void postValue(T value) { if (Looper.myLooper() != Looper.getMainLooper()) { // ... return; } mPendingData = value; if (mPendingData == NOT_SET) { // ignore return; } // ... mMainThreadExecutor.execute(mPostValueRunnable); } ```

當LiveData中的數據發生更新時,LiveData會通知所有的觀察者進行UI更新。LiveData使用了模板方法

設計模式中的觀察者模式,它將數據源和觀察者進行了解耦。LiveData類中的dispatchingValue()方法就是通知觀察者進行UI更新的核心方法。

```

private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { considerNotify(initiator); initiator = null; } else { Iterator, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); while (iterator.hasNext()) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; } ```

dispatchingValue()方法中使用了一個迭代器遍歷所有的觀察者,然後調用considerNotify()方法進行UI更新。

```

private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { return; } if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData); } ```

considerNotify()方法首先判斷觀察者是否處於活躍狀態,如果不是則直接返回;接着判斷觀察者是否應該處於活躍狀態,如果不是則調用activeStateChanged()方法將觀察者狀態更新為非活躍狀態;最後判斷數據版本號是否發生變化,如果發生變化則調用觀察者的onChanged()方法進行UI更新。

  1. 其他方法

除了以上核心方法之外,LiveData還提供了其他方法,例如getValue()方法用於獲取LiveData中存儲的數據;hasActiveObservers()方法用於判斷是否存在處於活躍狀態的觀察者等等。

```

public T getValue() { Object data = mData; if (data != NOT_SET) { return (T) data; } return null; }

public boolean hasActiveObservers() { return mActiveCount > 0; } ```

以上是對LiveData源碼的簡要分析,LiveData使用了觀察者模式,可以感知生命週期並進行UI更新,避免了因為生命週期問題帶來的內存泄漏。

3.2.6 LiveData注意事項

數據倒灌

要解決LiveData數據倒灌問題,可以使用以下方法:

使用MediatorLiveData代替LiveData:MediatorLiveData可以作為一箇中間層,將多個LiveData對象的數據源合併,從而避免數據倒灌問題。在UI組件的生命週期結束時,可以調用MediatorLiveData的removeSource()方法,將LiveData的數據源從MediatorLiveData中移除。

在ViewModel中使用LiveData:將LiveData對象作為ViewModel中的成員變量,並在ViewModel中進行數據更新和觀察,可以避免LiveData數據倒灌問題。

在UI組件中使用自定義Observer:可以在自定義Observer中進行生命週期判斷,當UI組件的生命週期已經結束時,不再更新UI界面。

下面是使用自定義Observer解決LiveData數據倒灌問題的示例代碼:

```

public class MyObserver<T> implements Observer<T> {

    private boolean mIsStarted = false;
    private Observer<T> mObserver;

    public MyObserver(Observer<T> observer) {
        mObserver = observer;
    }

    public void start() {
        mIsStarted = true;
    }

    public void stop() {
        mIsStarted = false;
    }

    @Overridepublic void onChanged(T t) {
        if (mIsStarted) {
            mObserver.onChanged(t);
        }
    }
}

```

在UI組件中使用自定義Observer時,需要在UI組件的生命週期開始和結束時,調用MyObserver的start()和stop()方法,從而控制數據更新的時機,避免LiveData數據倒灌問題。

```

public class MyActivity extends AppCompatActivity {

    private LiveData<Integer> mLiveData;
    private MyObserver<Integer> mObserver;

    @Overrideprotected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mLiveData = new MutableLiveData<>();
        mObserver = new MyObserver<>(integer -> {
            // 更新UI界面
        });
        mLiveData.observe(this, mObserver);
    }

    @Overrideprotected void onStart() {
        super.onStart();
        mObserver.start();
    }

    @Overrideprotected void onStop() {
        super.onStop();
        mObserver.stop();
    }
}

```

以上是解決LiveData數據倒灌問題的一些方法,也可以通過hook修改livedata源碼observer.mLastVersion的值,使得if (observer.mLastVersion >= mVersion)成立,就不會導致沒有註冊觀察者,還能接收到消息

#### setValue 不起效

調用LiveData的setValue()方法時,如果LiveData的觀察者處於激活狀態,那麼LiveData會將最新的數據推送給觀察者,並在觀察者的回調方法中更新UI界面。但是,如果LiveData的觀察者處於非激活狀態,那麼LiveData不會將數據推送給觀察者,也不會更新UI界面。因此,如果調用setValue()方法後,UI界面沒有發生更新,可能是因為LiveData的觀察者處於非激活狀態。

LiveData的觀察者處於非激活狀態的原因可能有以下幾種:

  1. 觀察者沒有與LiveData建立連接:在調用LiveData的observe()方法時,需要將觀察者與LiveData建立連接,只有建立連接後,LiveData才能將數據推送給觀察者。如果沒有建立連接,那麼即使調用了setValue()方法,也無法更新UI界面。
  2. 觀察者的生命週期已經結束:LiveData的觀察者必須在其生命週期的活動狀態中才能接收到數據更新。如果觀察者的生命週期已經結束,那麼即使調用了setValue()方法,也無法更新UI界面。
  3. 觀察者處於非激活狀態:LiveData的觀察者在其生命週期的激活狀態中才能接收到數據更新。如果觀察者處於非激活狀態,那麼即使調用了setValue()方法,也無法更新UI界面。

因此,如果調用setValue()方法後,UI界面沒有發生更新,可以檢查觀察者的連接狀態和生命週期狀態,確保LiveData的觀察者處於激活狀態,並且已經與LiveData建立連接。如果仍然無法解決問題,可以考慮使用postValue()方法,該方法可以在UI線程空閒時更新LiveData的數據,並在觀察者處於激活狀態時通知觀察者進行UI更新。

內存泄漏

LiveData的觀察者(Observer)默認是弱引用,但是如果觀察者沒有及時取消觀察,可能會導致內存泄漏。

#### 多次觀察

如果一個LiveData對象被多個觀察者同時觀察,那麼每個觀察者都會收到相同的數據更新,可能會導致UI界面多次更新,造成性能問題。

#### 生命週期不一致

LiveData是與生命週期相關聯的,如果UI組件的生命週期結束了,但是LiveData仍在發送數據更新,那麼就會引發異常。

線程安全

LiveData默認在主線程進行數據更新,如果需要在後台線程進行數據更新,就需要使用LiveData的postValue()方法,而不是setValue()方法。

避免重複觀察

如果在一個UI組件中多次觀察同一個LiveData對象,可能會導致重複觀察,造成性能問題和數據不一致問題。因此,可以使用ViewModel中的getLiveData()方法,保證同一個LiveData對象只被觀察一次。

3.2.7 EventBus vs Otto vs RxJava vs LiveData

image.png

3.3 ViewModel

3.3.1 ViewModel基礎定義

Android Jetpack ViewModel 是一種用於管理 UI 相關數據的組件,它可以在屏幕旋轉等配置更改時保存數據並重新創建 Activity 或 Fragment。ViewModel 是一個以生命週期感知的方式來保存和管理 UI 相關數據的類。

ViewModel 是一個專門用於存儲和管理與 UI 相關的數據的類,它們可以在 Activity 或 Fragment 重新創建時保持數據的完整性,並且可以避免因為 Activity 或 Fragment 生命週期的變化而導致的數據丟失。ViewModel 還提供了一種在 Activity 和 Fragment 之間共享數據的機制。

在 ViewModel 中存儲的數據是不受 Activity 或 Fragment 生命週期的影響的,這意味着在旋轉屏幕或者配置更改時,ViewModel 中的數據將不會被清除,可以保持完整性,從而可以在 Activity 或 Fragment 重新創建時繼續使用。

ViewModel 通常與 LiveData 或 RxJava 等響應式編程庫一起使用,以實現數據的實時更新和響應。

3.3.2 ViewModel基礎使用

如何在 ViewModel 中保存和管理數據,並在 Activity 或 Fragment 重新創建時保持數據的完整性。

  1. 創建一個名為MyViewModel的ViewModel類

```

public class MyViewModel extends ViewModel {

private MutableLiveData<Integer> count = new MutableLiveData<>();

public void setCount(int count) {
    this.count.setValue(count);
}

public LiveData<Integer> getCount() {
    return count;
}

} ```

在這個 ViewModel 類中,我們創建了一個 MutableLiveData 類型的 count 變量,並在 setCount() 方法中設置它的值,同時在 getCount() 方法中將其作為 LiveData 返回,以實現數據的實時更新和響應。

  1. 在 Activity或Fragment中使用ViewModel

```

public class MyActivity extends AppCompatActivity {

private MyViewModel viewModel;

@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    viewModel.getCount().observe(this, count -> {
        // 更新 UI
    });
}

public void onButtonClicked(View view) {
    int count = viewModel.getCount().getValue() + 1;
    viewModel.setCount(count);
}

} ```

在這個 Activity 中,我們使用 ViewModelProviders.of() 方法獲取 MyViewModel 的實例,並將其與該 Activity 綁定。

然後,我們可以使用 getCount() 方法獲取 count 變量的值,並在 setCount() 方法中設置其值。

通過調用 getCount().observe() 方法,我們可以在 LiveData 數據變化時更新 UI。

這個案例演示瞭如何使用 Android Jetpack ViewModel 來管理 UI 相關的數據,並在 Activity 或 Fragment 重新創建時保持數據的完整性。

使用 ViewModel 可以幫助我們更好地管理和保持與 UI 相關的數據,並提高應用程序的穩定性和可靠性。

3.3.3 ViewModel優勢劣勢

優勢

  1. 保持數據的完整性:ViewModel 可以在屏幕旋轉等配置更改時保存數據並重新創建 Activity 或 Fragment,從而保持數據的完整性,避免因為生命週期變化而導致的數據丟失。
  2. 簡化代碼:使用 ViewModel 可以將 UI 相關的數據與 UI 控件分離,避免了在 Activity 或 Fragment 中處理數據邏輯的繁瑣代碼,使代碼更加清晰、簡潔。
  3. 支持數據共享:ViewModel不會隨着Activity的屏幕旋轉而銷燬,減少了維護狀態的代碼成本(數據的存儲和讀取、序列化和反序列化);
  4. 支持響應式編程:ViewModel 可以與 LiveData 或 RxJava 等響應式編程庫一起使用,實現數據的實時更新和響應。

劣勢

  1. 不適合處理長時間運行的任務:ViewModel 主要用於管理 UI 相關的數據,如果需要處理長時間運行的任務,需要在 ViewModel 中使用異步任務或者單獨使用 Service。
  2. 數據持久化需要額外處理:ViewModel 中保存的數據只是暫時性的,如果需要長期保存數據,需要將數據保存到數據庫或者 SharedPreferences 中。
  3. 需要了解生命週期:ViewModel 是以生命週期感知的方式來保存和管理數據的,因此需要了解 Activity 或 Fragment 的生命週期以便正確使用 ViewModel。

3.3.4 ViewModel應用場景

下面是一些實際開發過程中可能使用 ViewModel 的使用場景:

  1. 數據持久化:如果需要在應用程序的不同頁面之間共享數據,並且希望在 Activity 或 Fragment 重新創建時保持數據的完整性,可以使用 ViewModel 來保存和管理數據。

  2. 處理屏幕旋轉等配置更改:當應用程序的屏幕旋轉或其他配置更改時,Activity 或 Fragment 可能會被銷燬並重新創建,此時可以使用 ViewModel 來保存和恢復數據,避免因為生命週期變化而導致的數據丟失。

  3. 分離 UI 與數據:將 UI 相關的數據與 UI 控件分離,避免在 Activity 或 Fragment 中處理數據邏輯的繁瑣代碼,使代碼更加清晰、簡潔。

  4. 實現響應式編程:ViewModel 可以與 LiveData 或 RxJava 等響應式編程庫一起使用,實現數據的實時更新和響應,從而提高應用程序的性能和用户體驗。

  5. 避免內存泄漏:將數據存儲在 ViewModel 中可以避免由於對 Activity 或 Fragment 的引用而導致的內存泄漏,從而提高了應用程序的穩定性和可靠性。

3.3.5 ViewModel原理分析

類圖

  • ViewModel類是一個抽象類,包含了一個onCleared()方法,該方法會在ViewModel不再被使用時被調用,用於釋放資源和清除狀態。
  • AndroidViewModel是ViewModel的一個子類,它包含一個Application對象,用於在ViewModel中訪問應用程序的上下文。
  • ViewModelProvider是一個幫助類,用於獲取ViewModel實例。
  • ViewModelProvider.Factory是一個接口,用於創建ViewModel實例。

ViewModel.png

原理

Jetpack ViewModel 源碼位於 androidx.lifecycle.ViewModel 包中,它包含了兩個類:ViewModel 和 ViewModelProvider。

每個Activity都會綁定一個ViewModelStore,ViewModelStore通過HashMap保存ViewModel和String名稱。ViewModel的持久化依賴ViewModelStore的存儲和獲取。

ViewModel 類的主要作用是定義一個用於存儲 UI 組件數據的容器,並在 UI 組件生命週期變化時進行管理。具體來説,ViewModel 類繼承了 Android 的 ViewModel 類,並添加了一些額外的方法,用於實現以下功能:

  1. 緩存 UI 組件數據
  2. 在 UI 組件銷燬後清理數據
  3. 在 UI 組件重建時恢復數據

ViewModelProvider 類的主要作用是創建 ViewModel 實例,併為其提供一個唯一的 key,用於在 Activity 或 Fragment 重建時找回已經存在的 ViewModel 實例。ViewModelProvider 類的實現方式比較簡單,主要是通過一個 HashMap 來存儲 ViewModel 實例,並提供了一些方法,用於創建和查找 ViewModel 實例。

下面是 ViewModel 類的源碼分析:

```

open class ViewModel : ViewModelStoreOwner { private val mViewModelStore = ViewModelStore()

@CallSuperoverride fun onCleared() {
    mViewModelStore.clear()
}

fun getViewModelStore(): ViewModelStore {
    return mViewModelStore
}

} ```

ViewModel 類實現了 ViewModelStoreOwner 接口,並在內部維護了一個 ViewModelStore 對象,用於存儲 UI 組件數據。在 ViewModel 被銷燬時,會調用 onCleared() 方法來清空 ViewModelStore 中的數據。getViewModelStore() 方法用於返回 ViewModelStore 對象。

ViewModelStoreOwner 接口的實現代碼如下:

```

interface ViewModelStoreOwner { fun getViewModelStore(): ViewModelStore } ```

ViewModelProvider 類的源碼分析如下:

```

class ViewModelProvider private constructor( private val mFactory: Factory, private val mViewModelStore: ViewModelStore ) { // 緩存 ViewModel 實例的 HashMapprivate val mViewModels = HashMap()

companion object {
    private val sCache = HashMap<String, ViewModelProvider>()

    /**
     * 返回當前 Activity 或 Fragment 的 ViewModelProvider 實例
     */@MainThreadfun of(owner: ViewModelStoreOwner): ViewModelProvider {
        return of(owner, FactoryHolder.DEFAULT_FACTORY)
    }

    /**
     * 返回當前 Activity 或 Fragment 的 ViewModelProvider 實例,可指定 Factory
     */@MainThreadfun of(owner: ViewModelStoreOwner, factory: Factory): ViewModelProvider {
        val store = owner.getViewModelStore()

        // 先從緩存中查找 ViewModelProvider 實例var viewModelProvider = sCache[store.mKey]
        if (viewModelProvider == null) {
            viewModelProvider = ViewModelProvider(factory, store)
            sCache[store.mKey] = viewModelProvider
        }

        return viewModelProvider
    }
}

/**
 * 返回指定 key 的 ViewModel 實例,如果不存在則通過 Factory
 */

fun get(key: String, modelClass: Class): T { var viewModel = mViewModels[key] if (modelClass.isInstance(viewModel)) { @Suppress("UNCHECKED_CAST") return viewModel as T }

// 創建新的 ViewModel 實例,並將其添加到緩存中
viewModel = mFactory.create(modelClass)
mViewModels[key] = viewModel

return viewModel

}

/* * Factory 接口,用於創建 ViewModel 實例 / interface Factory { fun create(modelClass: Class): T }

/* * FactoryHolder 類,用於提供默認的 Factory 實現 / private object FactoryHolder { val DEFAULT_FACTORY: Factory = object : Factory { override fun create(modelClass: Class): T { try { return modelClass.newInstance() } catch (e: IllegalAccessException) { throw RuntimeException(e) } catch (e: InstantiationException) { throw RuntimeException(e) } } } } } ```

ViewModelProvider 類包含了一個 HashMap,用於緩存 ViewModel 實例。

它的 of() 方法用於創建 ViewModelProvider 實例,並通過 ViewModelStore 對象來區分不同的 Activity 或 Fragment。get() 方法用於返回指定 key 的 ViewModel 實例,並在緩存中查找是否存在對應的實例。如果緩存中不存在該實例,則調用 Factory 接口來創建一個新的 ViewModel 實例,並將其添加到緩存中。

Factory 接口提供了一個 create() 方法,用於創建 ViewModel 實例。FactoryHolder 類用於提供默認的 Factory 實現,它使用 Java 的反射機制來創建 ViewModel 實例。 最後,需要注意的是,ViewModelStore 和 ViewModelProvider 都是線程安全的,可以在多個線程中同時使用。

這意味着在多線程環境下,可以使用 ViewModel 來緩存和共享數據,從而減少數據的重複加載和提升應用程序的性能。

3.3.6 ViewModel注意事項

使用了錯誤的 ViewModel:

在某些情況下,可能會需要多個 ViewModel 來管理不同的 UI 數據。如果使用了錯誤的 ViewModel 來管理數據,可能會導致數據丟失或邏輯錯誤。因此,在使用 ViewModel 時應該清楚每個 ViewModel 的作用,避免出現混淆。

ViewModel 與生命週期的關係

ViewModel 的生命週期不同於 Activity 和 Fragment,它是被系統緩存的,因此可能會出現數據被清除的情況。在使用 ViewModel 時應該注意它的生命週期,及時保存數據並恢復數據。

使用無效的上下文

ViewModel 需要一個有效的上下文來創建實例,如果使用無效的上下文可能會導致 ViewModel 創建失敗。因此,在使用 ViewModel 時應該注意上下文的有效性,避免出現創建失敗的情況。

使用了錯誤的作用域

ViewModel 的作用域應該與需要管理的 UI 組件的生命週期相同,如果使用了錯誤的作用域,可能會導致數據被清除或者生命週期不一致。因此,在使用 ViewModel 時應該選擇正確的作用域,避免出現問題。

3.4 DataBinding

3.4.1 DataBinding基礎定義

DataBinding提供了一種聲明性的方式將佈局文件中的UI組件和應用程序的數據模型綁定在一起。

通過DataBinding,開發者可以將UI組件的值綁定到數據模型中的屬性,使得在更新UI時不需要手動更新每個組件的值。

DataBinding庫通過生成一個綁定類來實現UI和數據模型之間的綁定。

這個綁定類是在編譯時自動生成的,它使用了數據模型和UI組件之間的綁定表達式,以便在運行時執行數據綁定。

3.4.2 DataBinding基礎使用

技術需求

使用DataBinding實現一個簡單的計數器功能。在每次更新計數器的值時,DataBinding會自動更新TextView的值,使得開發者不需要手動更新UI組件的值,可以更加專注於業務邏輯的實現

XML

```

    <TextView
        android:id="@+id/tvCounter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{counter.toString()}" />

    <Button
        android:id="@+id/btnIncrease"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="+" />

    <Button
        android:id="@+id/btnDecrease"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="-" />
</LinearLayout>

```

Code

創建DataBinding實例,並將activity_main.xml佈局文件與DataBinding實例綁定。然後,將計數器的值賦給DataBinding實例的counter變量,併為增加和減少按鈕設置點擊事件。在點擊事件中,更新計數器的值,並將新的值賦給DataBinding實例的counter變量。最後,調用DataBinding實例的executePendingBindings()方法,將UI組件的值更新到最新的值

``` class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

private var counter = 0

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // 創建DataBinding實例並與佈局文件綁定
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

    // 設置初始值
    binding.counter = counter

    // 為按鈕設置點擊事件
    binding.btnIncrease.setOnClickListener {
        counter++
        binding.counter = counter
        binding.executePendingBindings()
    }

    binding.btnDecrease.setOnClickListener {
        counter--
        binding.counter = counter
        binding.executePendingBindings()
    }
}

} ```

3.4.3 DataBinding優勢劣勢

優勢

  1. 簡化代碼

    使用DataBinding可以減少findViewById()方法的調用,使得代碼更加簡潔和易讀。開發者可以通過DataBinding直接訪問佈局文件中的UI組件,而不需要手動查找和設置UI組件的屬性。

  2. 雙向綁定

    DataBinding支持雙向綁定,可以將UI組件的值綁定到數據模型中的屬性,也可以將數據模型的屬性綁定到UI組件的值。這種雙向綁定可以幫助開發者更加方便地實現UI和數據模型之間的同步。

  3. 減少錯誤

    DataBinding可以減少代碼中的錯誤,因為使用DataBinding可以消除某些手動編寫的代碼。例如,DataBinding可以自動生成一些代碼,幫助開發者綁定UI組件和數據模型,減少手動編寫代碼的機會。

  4. 性能優化

    DataBinding可以帶來一些性能優化,例如通過生成綁定類來減少代碼的執行時間。DataBinding還支持懶加載,可以在需要時才加載數據模型和UI組件。

劣勢

  1. 兼容性問題

    DataBinding庫需要使用較新的Android Gradle插件和Android SDK版本,這可能導致一些兼容性問題。如果開發者使用較老的Android版本,可能需要進行一些兼容性處理。

  2. 構建時間增加

    使用DataBinding可能會增加應用程序的構建時間,因為DataBinding庫需要生成綁定類。在編譯時需要分離和處理xml文件,增加了編譯時間。

  3. 難以調試

    由於DataBinding庫使用了代碼生成,因此在調試時可能會出現一些困難。例如,開發者可能會發現在使用DataBinding時,調試器可能會跳過某些代碼行,或者某些變量的值可能無法正確顯示。

  4. 增大包的體積

    佔用大量內存,每個可觀測數據對象都會對應一個監聽器WeakListener對象。

3.4.4 DataBinding應用場景

  1. MVVM架構:DataBinding可以幫助開發者實現MVVM架構中的視圖模型(ViewModel)和視圖(View)之間的綁定。通過DataBinding,開發者可以將視圖和數據模型分離,提高代碼的可讀性和可維護性。
  2. 佈局優化:DataBinding可以幫助開發者實現佈局優化,例如通過使用綁定表達式和條件語句來控制UI組件的可見性。通過佈局優化,可以使得應用程序的UI更加靈活和可定製。
  3. 多語言支持:DataBinding可以幫助開發者實現多語言支持,例如通過使用綁定表達式來動態設置UI組件的文本。通過多語言支持,可以使得應用程序的用户界面更加友好和易用。
  4. 動態主題:DataBinding可以幫助開發者實現動態主題,例如通過使用綁定表達式來動態設置UI組件的顏色和樣式。通過動態主題,可以使得應用程序的UI更加美觀和精緻。

3.4.5 DataBinding原理分析

類圖

源碼

DataBinding的原理主要包括以下幾個方面:

  1. 佈局文件的解析和生成:在DataBinding中,佈局文件會被解析並生成對應的ViewDataBinding類。這個類包含了佈局文件中定義的所有UI組件的引用,以及綁定到這些UI組件的數據對象。
  2. 數據對象的綁定:DataBinding會在編譯時生成代碼來完成UI組件和數據對象的綁定。生成的代碼會被包含在BR類中。這個類包含了應用程序中所有用於綁定的變量的引用。當數據對象發生變化時,DataBinding會自動更新UI組件的值。
  3. 觀察者模式的應用:DataBinding使用觀察者模式來保持UI和數據之間的同步。當數據對象發生變化時,DataBinding會自動更新UI組件的值。在DataBinding中,數據對象是被觀察的對象,而UI組件是觀察者。
  4. 雙向綁定的實現:DataBinding支持雙向綁定,即當UI組件的值發生變化時,DataBinding會自動更新數據對象的值。這是通過使用雙向綁定適配器來實現的。適配器會在UI組件的值發生變化時自動更新數據對象的值。
  5. 屬性轉換器的使用:DataBinding還支持屬性轉換器,可以用來將數據對象中的值轉換為UI組件可以顯示的值。例如,可以使用屬性轉換器將日期格式化為特定的格式。
  6. 數據綁定表達式的應用:DataBinding還支持數據綁定表達式,可以用來在佈局文件中動態計算UI組件的值。例如,可以使用數據綁定表達式將兩個文本框中的值相加並將結果顯示在第三個文本框中。

總的來説,Android Jetpack DataBinding的原理是基於數據綁定和觀察者模式的。它通過生成代碼來完成UI組件和數據對象之間的綁定,並使用觀察者模式來保持UI和數據之間的同步。同時,DataBinding還支持雙向綁定、屬性轉換器和數據綁定表達式等特性,使得它可以滿足更加複雜的UI和數據交互需求。

3.4.6 DataBinding注意事項

  1. 錯誤:Cannot find the setter for attribute ‘XXX’ with parameter type XXX。這個錯誤通常是由於綁定表達式中的變量類型不正確或佈局文件中的屬性名稱不正確導致的。可以檢查變量類型和屬性名稱是否正確。
  2. 錯誤:Could not find accessor XXX。這個錯誤通常是由於佈局文件中使用了不存在的變量或方法導致的。可以檢查佈局文件中使用的變量和方法是否存在。
  3. 錯誤:Variable XXX has incompatible type XXX。這個錯誤通常是由於DataBinding的變量類型和佈局文件中的變量類型不一致導致的。可以檢查DataBinding代碼中變量的類型和佈局文件中變量的類型是否一致。
  4. 錯誤:DataBinding不支持lambda表達式。如果使用lambda表達式,編譯時會出現錯誤。可以使用方法引用或匿名內部類來替代lambda表達式。
  5. 性能問題:在使用數據綁定表達式時,要注意表達式的複雜度。複雜的表達式可能會影響應用程序的性能。可以嘗試將複雜的表達式分解成更簡單的表達式,以提高應用程序的性能。
  6. 混淆問題:如果應用程序使用了代碼混淆,那麼需要將DataBinding的類排除在混淆範圍之外,否則會導致編譯錯誤或運行時異常。
  7. ViewStub問題:在使用DataBinding的佈局文件中,不能包含ViewStub,否則會導致編譯錯誤。

3.4.7 ButterKnife VS ViewBinding VS RoboBinding VS DataBinding

image.png

四、總結與展望

《Android業務架構 · 基礎篇 · Jetpack四件套》一文首先通過4W2H全方位的講解了Jepack對Android業務開發的價值,然後通過基礎定義、基礎使用、優劣勢分析、應用場景、原理分析、注意事項等多維度分析了Jetpack四件套。

在我們工作中,可維護、可擴展、可測試和可重用的業務架構對於提高應用程序的質量和效率意義非凡,而JetPack是幫助開發者快速地組織和管理應用程序的代碼的工具包。

這也是小木箱強烈建議大家學習Jetpack很重要的原因。希望通過這篇文章能夠讓你意識到Jetpack對業務開發的重要性。

提高篇將介紹MVC、MVP、MVVM和MVI四劍客,同樣是Android業務架構核心內容。 今天就到這裏啦,我是 小木箱,我們下一篇見~