Android Jetpack組件LiveData基本使用和原理分析

LiveData一般是和 ViewModel 配合使用的,但是本文就以單獨使用 LiveData 作為例子單獨使用,這樣可以只關注 LiveData 而不被其他所干擾。

本文整體流程:首先要知道什么是 LiveData,然后演示一個例子,來看看 LiveData 是怎么使用的,接著提出問題為什么是這樣的,最后讀源碼來解釋原因!

LiveData 的源碼比較簡單,底層依賴了 Lifecycle,所以懂 Lifecycle 的源碼是關鍵,我之前寫過一篇

Android Jetpack組件Lifecycle基本使用和原理分析 最好是先看這篇文章,才能更好的理解 LiveData。

1.什么是 LiveData

LiveData是一種可觀察的數據存儲器類。與常規的可觀察類不同,LiveData 具有生命周期感知能力,意指它遵循其他應用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保 LiveData 僅更新處于活躍生命周期狀態的應用組件觀察者。

2.LiveData基礎使用的例子

這個例子,是點擊按鈕通過 LiveData 來更新 TextView 的內容

如圖:

[圖片上傳失敗...(image-90c8e1-1606802615929)]

點擊按鈕后

[圖片上傳失敗...(image-cc2c98-1606802615929)]

具體代碼

class LiveDataActivity : BaseActivity() {
    private val mContent = MutableLiveData<String>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)

        btnUpdate.setOnClickListener {
            mContent.value = "最新值是:Update"
        }

        mContent.observe(this, Observer { content ->
            tvContent.text = content
        })
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="0dp"
        android:text="Hello World"
        android:layout_height="wrap_content"
        android:textColor="#f00"
        android:gravity="center"
        android:textSize="24sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnUpdate"
        android:layout_width="wrap_content"
        android:text="Update"
        android:padding="5dp"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="18sp"
        android:layout_marginTop="20dp"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvContent" />

</androidx.constraintlayout.widget.ConstraintLayout>

默認TextView展示的是: Hello World,點擊按鈕后展示的是:“最新值是:Update” 。這個就是LiveData 的簡單使用。

3.拋出問題

為什么LiveData的工作機制是這樣的

  • LiveData 是怎么回調的?
  • LiveData 為什么可以感知生命周期?
  • LiveData 可以感知生命周期,有什么用,或者說有什么優勢?
  • LiveData 為什么只會將更新通知給活躍的觀察者。非活躍觀察者不會收到更改通知?
  • LiveData此外還提供了observerForever()方法,所有生命周期事件都能通知到,怎么做到的?

解析來通過分析源碼,來尋找答案。文章最后我會解釋這些問題的,做一個統一的總結。

4.源碼分析前的準備工作

我需要了解幾個類,來對接下來的源碼分析做一個鋪墊。

先看之前例子中的代碼

class LiveDataActivity : BaseActivity() {
    private val mContent = MutableLiveData<String>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
                
        mContent.observe(this, Observer { content ->
            tvContent.text = content
        })
    }
}

只貼出了主要代碼,我們來看下主要的類以及方法,方法參數

  • 聲明了一個MutableLiveData對象
  • 調用了MutableLiveData的observe方法
  • observe方法中 傳入 this 和 Observer
  • this 指的是LiveDataActivity對象,其實一個是一個LifecycleOwner。Observer是一個接口

來分別看下具體內容。

4.1.MutableLiveData類
public class MutableLiveData<T> extends LiveData<T> {

    public MutableLiveData(T value) {
        super(value);
    }

    public MutableLiveData() {
        super();
    }

    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}
  • 繼承了LiveData是一個可變的LiveData
  • 是一個被觀察者,是一個數據持有者
  • 提供了 setValue 和 postValue方法,其中postValue可以在子線程調用
  • postValue方法,我們下面會具體分析
4.2.MutableLiveData的observe方法參數中的 this

當前 Activity 的對象,本質上是一個LifecycleOwner 我在這篇 Android Jetpack組件Lifecycle基本使用和原理分析中有分析過,它的源碼。

public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
}
4.3MutableLiveData的observe方法參數中的 Observer
public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}
  • Observer是一個觀察者
  • Observer中有一個回調方法,在 LiveData 數據改變時會回調此方法

通過以上簡單分析,我們大概了解了這個幾個類的作用,接下來我們一步一步看源碼,來從源碼中解決我們在第 3 節提出的問題。

5.源碼分析

首先我們上面示例中的 LiveData.observe()方法開始。

//LiveDataActivity.kt
private val mContent = MutableLiveData<String>()

mContent.observe(this, Observer { content ->
    tvContent.text = content
})

我們點進observe方法中去它的源碼。

6.LiveData類

在LiveData的observe方法中

//LiveData.java
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    //1
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //2
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //3
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    //4
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    //5
    owner.getLifecycle().addObserver(wrapper);
}

注釋 1:首先會通過LifecycleOwner獲取Lifecycle對象然后獲取Lifecycle 的State,如果是DESTROYED直接 return 了。忽略這次訂閱

注釋 2 :把LifecycleOwner和Observer包裝成LifecycleBoundObserver對象,至于為什么包裝成這個對象,我們下面具體講,而且這個是重點

注釋 3:把觀察者存到 Map 中

注釋 4:之前添加過LifecycleBoundObserver,并且LifecycleOwner不是同一個,就拋異常

注釋 5:通過Lifecycle和添加 LifecycleBoundObserver觀察者,形成訂閱關系

總結:

到現在,我們知道了LiveData的observe方法中會判斷 Lifecycle 的生命周期,會把LifecycleOwner和Observer包裝成LifecycleBoundObserver對象,然后 Lifecycle().addObserver(wrapper)Lifecycle 這個被觀察者會在合適的實際通知觀察者的回調方法。

等等,什么時候通知,咋通知的呢?這個具體流程是啥呢?

回個神,我再貼下開始的示例代碼。

class LiveDataActivity : BaseActivity() {
    private val mContent = MutableLiveData<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
                //1
        btnUpdate.setOnClickListener {
            mContent.value = "最新值是:Update"
        }

        mContent.observe(this, Observer { content ->
            tvContent.text = content
        })
    }
}

在點擊按鈕的時候 LiveData會調用setValue方法,來更新最新的值,這時候我們的觀察者Observer就會收到回調,來更新 TextView。

所以接下來我們先看下 LiveData的setValue方法做了什么,LiveData還有一個postValue方法,我們也一并分析一下。

7.LiveData的setValue方法和postValue方法

7.1.先看setValue方法
//LiveData.java
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);//1
}

調用了dispatchingValue方法,繼續跟代碼

//LiveData.java
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            //1
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                //2
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

不管如何判斷,都是調用了considerNotify()方法

//LiveData.java
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);//1
}

最終調用了observer.mObserver.onChanged((T) mData)方法,這個observer.mObserver就是我們的 Observer接口,然后調用它的onChanged方法。

到現在整個被觀察者數據更新通知觀察者這個流程就通了。

7.2.然后再看下postValue方法

子線程發送消息通知更新 UI,嗯?Handler 的味道,我們具體看下代碼

//LiveData.java
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);//1
}

可以看到一行關鍵代碼ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);

點 **postToMainThread **方法進去看下

//ArchTaskExecutor.java
private TaskExecutor mDelegate;

@Override
public void postToMainThread(Runnable runnable) {
    mDelegate.postToMainThread(runnable);//1
}

看到 mDelegate 是 TaskExecutor對象,現在目標是看下 mDelegate 的具體實例對象是誰

//ArchTaskExecutor.java
private ArchTaskExecutor() {
    mDefaultTaskExecutor = new DefaultTaskExecutor();
    mDelegate = mDefaultTaskExecutor;//1
}

好的,目前的重點是看下DefaultTaskExecutor是個啥,然后看它的postToMainThread方法

//DefaultTaskExecutor.java
private volatile Handler mMainHandler;

@Override
public void postToMainThread(Runnable runnable) {
    if (mMainHandler == null) {
        synchronized (mLock) {
            if (mMainHandler == null) {
                mMainHandler = createAsync(Looper.getMainLooper());//1
            }
        }
    }

    mMainHandler.post(runnable);//2
}

注釋 1:實例了一個 Handler 對象,注意構造參數 Looper.getMainLooper()是主線的 Looper。那么就可做到線程切換了。

注釋 2:調用post 方法。

下面看下這個 Runnable

ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);

這里面的方法參數是mPostValueRunnable是個 Runnable,我們看下代碼

//LiveData.java
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);//1
    }
};

注意:postValue方法其實最終調用也是setValue方法,然后和setValue方法走的流程就是一樣的了,這個上面已經分析過了。詳情請看 7.1 小節

但是我們還不知道ObserverWrapper是啥,好那么接下來,我們的重點來了

我們要詳細看一下LifecycleBoundObserver類了,它包裝了LifecycleOwner和Observer,這就是接下來的重點內容了。

8.LifecycleBoundObserver類

再貼下一下代碼,當LiveData調用observe方法時

//LiveData.java
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //1
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

注釋 1 :用LifecycleBoundObserver對LifecycleOwner 和 Observer進行了包裝

8.1.來看下LifecycleBoundObserver類,它是LiveData的內部類
//LiveData.java
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }
}

兩個參數,一個 owner被成員變量mOwner存儲,observer參數被ObserverWrapper的 mObserver存儲。

  • LifecycleEventObserver是LifecycleObserver的子接口里面有一個onStateChanged方法,這個方法會在 Activity、Fragment 生命周期回調時調用,如果這么說不明看下這篇文章Android Jetpack組件Lifecycle基本使用和原理分析
  • ObserverWrapper 是Observer包裝類

我們接下來看下ObserverWrapper類

8.2.ObserverWrapper類
private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;
        //1
    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }
        //2
    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            //3
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            //4
            onInactive();
        }
        if (mActive) {
            //5
            dispatchingValue(this);
        }
    }
}

注:活躍狀態指的是 Activity、Fragment 等生命周期處于活躍狀態

注釋 1:獲取了我們的 Observer 對象,存儲在 成員變量mObserver身上

注釋 2:抽象方法,當前是否是活躍的狀態

注釋 3:可以繼承 LiveData 來達到擴展 LiveData 的目標,并且是在活躍的狀態調用

注釋 4:可以繼承 LiveData 來達到擴展 LiveData 的目標,并且是在非活躍的狀態調用

注釋 5:活躍狀態,發送最新的值,來達到通知的作用, dispatchingValue(this)方法咋這么眼熟,對之前在 LiveData 調用 setValue 方法時,最終也會調用到此方法。那ObserverWrapper類中的dispatchingValue這個方法是在activeStateChanged方法中調用,那activeStateChanged啥時候調用呢?

我來看下ObserverWrapper的子類也就是最重要的那個類LifecycleBoundObserver,現在看它的完整代碼

8.3.LifecycleBoundObserver完整代碼

這里是關鍵代碼了

//LiveData.java
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }
  
    @Override
    boolean shouldBeActive() {
                //1
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        //2
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        //3
        activeStateChanged(shouldBeActive());
    }
        
    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }
        
    @Override
    void detachObserver() {
         //4
        mOwner.getLifecycle().removeObserver(this);
    }
}

注釋 1:判斷當前的 Lifecycle 的生命周期是否是活躍狀態,會在回調觀察則 Observer 的時候進行判斷,只有在活躍狀態,才會回調觀察者Observer的onChanged方法。

直接就回答了我們上面的這個問題LiveData 為什么只會將更新通知給活躍的觀察者。非活躍觀察者不會收到更改通知? 首先會通過LifecycleOwner獲取Lifecycle對象然后獲取Lifecycle 的State,并且狀態大于STARTED。這里的State是和 Activity、Fragment 的生命周期是對應的,具體看這篇文章 Android Jetpack組件Lifecycle基本使用和原理分析 的第4.4小節,有詳細的解釋。

注釋 2:onStateChanged每次 Activity、Fragment的生命周期回調的時候,都會走這個方法。

獲取Lifecycle對象然后獲取Lifecycle 的State如果為DESTROYED則移除觀察者,在 Activity、Fragment的生命周期走到 onDestroy 的時候,就會取消訂閱,避免內存泄漏。

注釋 3:調用父類ObserverWrapper 的activeStateChanged方法,層層調用到觀察者Observer的onChanged方法。(自己看下源碼一目了然)

重點來了:在LiveData 調用setValue方法時,會回調觀察者Observer的onChanged方法,Activity、Fragment的生命周期變化的時候且為活躍也會回調觀察者Observer的onChanged方法。這就是為什么你在ActivityB頁面,調用setValue方法,更新了value,在ActivityA 重新獲取焦點時也同樣會收到這個最新的值。

注釋 4:移除觀察者Observer,解除訂閱關系。

到這個時候,LiveData 的 observer方法setValue方法,整個流程就分析完了。

如果我們想不管生命周期,而是想在setValue的值發生改變的時候就能接受到通知,LiveData 還提供了一個observeForever方法

class LiveDataActivity : BaseActivity() {
    private val mContent = MutableLiveData<String>()

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

        btnUpdate.setOnClickListener {
            mContent.value = "最新值是:Update"
        }

        //只要在值發生改變時,就能接收到
        mContent.observeForever { content ->
            tvContent.text = content
        }
    }
}
8.4LiveData 的observeForever方法

這個方法比observe方法少一個LifecycleOwner參數,為啥呢?因為這個方法不需要感知生命周期,需要在setValue 值更新時立馬收到回調。

來看下具體代碼

//LiveData.java
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    //1
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    wrapper.activeStateChanged(true);
}

注釋 1 :這里用到的是AlwaysActiveObserverobserve方法用到是LifecycleBoundObserver

看一下這個AlwaysActiveObserver

//LiveData.java
private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        return true;
    }
}

代碼非常的簡潔,在shouldBeActive方法中,直接 return true,這也太秀了吧

為啥直接返回 true 呢?因為這里不用管生命周期,永遠都是活躍狀態,所以這個方法叫observeForever

LiveData 的源碼非常值得讀,而且量不是很大,里面有許多值得學習的地方。

9.使用 LiveData 的優勢

這個是Google官方總結的

使用 LiveData 具有以下優勢:

  • 確保界面符合數據狀態

    LiveData 遵循觀察者模式。當生命周期狀態發生變化時,LiveData 會通知 Observer 對象。您可以整合代碼以在這些 Observer 對象中更新界面。觀察者可以在每次發生更改時更新界面,而不是在每次應用數據發生更改時更新界面。

  • 不會發生內存泄漏

    觀察者會綁定到 Lifecycle 對象,并在其關聯的生命周期遭到銷毀后進行自我清理。

  • 不會因 Activity 停止而導致崩潰

    如果觀察者的生命周期處于非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。

  • 不再需要手動處理生命周期

    界面組件只是觀察相關數據,不會停止或恢復觀察。LiveData 將自動管理所有這些操作,因為它在觀察時可以感知相關的生命周期狀態變化。

  • 數據始終保持最新狀態

    如果生命周期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的數據。例如,曾經在后臺的 Activity 會在返回前臺后立即接收最新的數據。

  • 適當的配置更改

    如果由于配置更改(如設備旋轉)而重新創建了 Activity 或 Fragment,它會立即接收最新的可用數據。

  • 共享資源

    您可以使用單一實例模式擴展 LiveData 對象以封裝系統服務,以便在應用中共享它們。LiveData 對象連接到系統服務一次,然后需要相應資源的任何觀察者只需觀察 LiveData 對象。如需了解詳情,請參閱擴展 LiveData

10.總結

開始回答第 3 節的拋出的問題

10.1.LiveData 是怎么回調的?
  • LiveData通過observe或者observeForever方法訂閱了一個觀察者
  • LiveData 通過調用setValuepostValue方法時,會取出觀察者,調用它的onChanged方法
  • 當然,當 Activity、Fragment 生命周期由非活躍變化為活躍狀態,也會收到最新的值回調onChanged方法,注意這個對應的是LiveData的observe方法。
10.2.LiveData 為什么可以感知生命周期?
  • 是因為LifecycleBoundObserver類
  • 以及在observe方法中調用了 owner.getLifecycle().addObserver(wrapper);這行代碼,具體的看下上面的源碼分析吧
10.3.LiveData 可以感知生命周期,有什么用,或者說有什么優勢?
  • 可以自動取消訂閱
  • 防止內存泄漏
10.4.LiveData 為什么只會將更新通知給活躍的觀察者。非活躍觀察者不會收到更改通知?
  • 在每次調用setValue方法時,最走到LifecycleBoundObserver的shouldBeActive這個方法的判斷上
  • 這個方法返回的是狀態為STARTED之后的狀態才會走通知觀察者回調的邏輯,否則就不執行,具體的看下上面的源碼
10.5.LiveData此外還提供了observerForever()方法,所有生命周期事件都能通知到,怎么做到的?
  • 看 8.4 小節,有詳細的分析。
  • 主要AlwaysActiveObserver的shouldBeActive這個方法直接返回的 true

11.源碼地址

LiveDataActivity.kt

12.原文地址

Android Jetpack組件LiveData基本使用和原理分析

13.參考文章

LiveData

Android官方架構組件LiveData: 觀察者模式領域二三事

推薦一下我開源的項目 WanAndroid 客戶端

WanAndroidJetpack 架構圖

image
  • 一個純 Android 學習項目,WanAndroid 客戶端。
  • 項目采用 MVVM 架構,用 Kotlin 語音編寫。
  • Android Jetpack 的大量使用包括但不限于LifecycleLiveDataViewModelDatabindingRoomConstraintLayout等,未來可能會更多。
  • 采用 RetrofitKotlin-Coroutine 協程進行網絡交互。
  • 加載圖片 Glide 主流加載圖片框架。
  • 數據存儲主要用到了 Room 和騰訊的 MMKV

Kotlin + MVVM + Jetpack + Retrofit + Glide 的綜合運用,是學習 MMVM 架構的不錯的項目。

此項目本身也是一個專門學習 Android 相關知識的 APP,歡迎下載體驗!

源碼地址(附帶下載鏈接)

WanAndroidJetpack

APP 整體概覽

wan-gif.gif

喜歡的點個 Stars,有問題的請提 Issues。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容