Jetpack的ViewModel與LiveData

妖久發表於2022-02-17

本文基於SDK 29

一、ViewModel與LiveData的作用:

1、viewModel:

資料共享,螢幕旋轉不丟失資料,並且在Activity與Fragment之間共享資料。

 

2、LiveData:

感知生命週期並且通知觀察者重新整理,防止記憶體洩漏。

 

二、用法

 

三、原理:

1、ViewModel:

ViewModelProviders.of(this).get(MyViewModel::class.java)

我們通過這個方法來構造ViewModel。

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    return of(activity, null);
}

/**
 * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
 * is alive. More detailed explanation is in {@link ViewModel}.
 * <p>
 * It uses the given {@link Factory} to instantiate new ViewModels.
 *
 * @param activity an activity, in whose scope ViewModels should be retained
 * @param factory  a {@code Factory} to instantiate new ViewModels
 * @return a ViewModelProvider instance
 */
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

 

從原始碼中可以看出,ViewModelProviders.of(this)獲取了一個ViewModelProvider 物件,而該物件中持有一個ViewModelProvider.AndroidViewModelFactory(因為我們傳進入的是null)

和activity.getViewModelStore()。

private final Factory mFactory;
private final ViewModelStore mViewModelStore;

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore = store;
}

我們再來看看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);
    }
    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

 

我們的ViewModel便是儲存在上面的HashMap中。

 

接下來我們再來看ViewModelProviders.of(this).get(MyViewModel::class.java)的get方法:

 @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);
 }

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

可以看出,所以會去儲存ViewModel的ViewModelStore中拿,發現已經有了便直接返回,如果沒有的話,那邊使用mFactory工廠進行構建,然後再放進ViewModelStore中。

從之前的分析可以看出,這裡的mFactory便是AndroidViewModelFactory。

@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.getConstructor(Application.class).newInstance(mApplication);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
    return super.create(modelClass);
}

其實該工廠也只是直接例項出該類而已。

此時我們便已經拿到了ViewModel。

可是它是怎麼做到資料共享的呢,想做到資料共享,按理說它應該只有一個例項物件,我們且看。

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

在獲取ViewModelProvider的時候傳進去了activity.getViewModelStore(),那我們看一下activity.getViewModelStore()是怎麼獲取ViewModelStore的。

@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) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

關鍵的程式碼在於這一句:NonConfigurationInstances nc =  (NonConfigurationInstances) getLastNonConfigurationInstance();

static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;

@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

將mLastNonConfigurationInstances.activity強轉成FragmentActivity中的一個類:NonConfigurationInstances,然後獲取ViewModelStore

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
    FragmentManagerNonConfig fragments;
}

 

NonConfigurationInstances是個靜態類,所以裡面的ViewModelStore 也是唯一的,因此ViewModelStore 能做到資料共享。

2、LivaData

我們先看這個語句:

viewModel?.livaData?.observe(this, Observer<Int> { integer -> Log.d("MainActivity", integer!!.toString()) })

從這個語句往原始碼裡面探究:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    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);
}

如果這個activity處於銷燬狀態,那麼便不會新增該觀察者,否則,構造一個LifecycleBoundObserver物件,放進mObservers裡面,mObservers即為:

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
            new SafeIterableMap<>();

然後將LifecycleBoundObserver物件放進LifecycleRegistry裡面。

LifecycleBoundObserver裡面持有的物件如下:

 

 

 當我們給LiveData設定值的時候:livaData.value = i

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

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

裡面還有個postValue方法:

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

postValue最終也會呼叫到主執行緒。postValue可以在子執行緒呼叫,而setValue必須在主執行緒呼叫,否則會丟擲異常。

我們看setValue方法:

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

 

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

這裡我們傳進來的initiator為null,所以我們主要看:

for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
    considerNotify(iterator.next().getValue());
    if (mDispatchInvalidated) {
        break;
    }
}

這裡的mObservers即為:

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
        new SafeIterableMap<>();

裡面存放著我們之前放進去的LifecycleBoundObserver物件。

iterator.next().getValue()獲取的便是LifecycleBoundObserver物件。

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}

檢測當前生命週期,至少是處於start。

@Override
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

然後執行observer.mObserver.onChanged((T) mData);回撥出去。

observer.mObserver便是我們傳進去的觀察者:

Observer<Int> { integer -> Log.d("MainActivity", integer!!.toString()) }

由以上也可以看出:我們是可以註冊多個觀察者的,所以要注意在一個Activity中只能夠註冊一次,否則會發生多個回撥。

 

那麼有個疑問,我們這樣已經實現了,那問什麼在liveData?.observe方法裡面,不但將LifecycleBoundObserver放進LiveData的SafeIterableMap裡面,還要將其放入LifecycleRegistry

裡面。owner.getLifecycle()獲取到的便是LifecycleRegistry

 

 這是為了在相關的生命週期內做相關的操作,根據上一篇文章,我們可以知道,當activity的生命週期發生改變的時候,會獲取新增進LifecycleRegistry的觀察者,然後對每個觀察者進行回撥處理。

而在這裡便會回撥LifecycleBoundObserver的onStateChanged方法。

@Override
public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event) {
    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    activeStateChanged(shouldBeActive());
}

判斷如果當前處於DESTROYED狀態,那麼便將我們新增進入的觀察者移除。

否則會呼叫activeStateChanged(shouldBeActive())方法。

 

 

 如果當前的活躍狀態與上一次一樣,那麼就直接返回。

否則如果變為活躍的狀態,那麼會呼叫dispatchingValue(this);

 

 

 這裡要注意,我們之前呼叫LiveData的setValue的時候,走的的2,但是現在走的是1,因為這次傳進來的引數不為空。

 

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

 

然後進入considerNotify這個方法,裡面有一個判斷十分重要:

if (observer.mLastVersion >= mVersion) {
    return;
}

這個判斷是做什麼用的呢?mVersion是什麼時候被賦值的,這時候就要我們回過去頭去看LiveData的setValue方法:

 

 每呼叫一次,那麼這個mVersion就會自加一。

所以這個判斷便保證了,必須是重新整理了LiveData裡面的data值,才能夠回撥觀察者事件:observer.mObserver.onChanged((T) mData);

如果生命週期變化的時候,LiveData裡面的data值沒有重新整理,就不能回撥出去。所以如果重新整理LiveData裡面的值的時候不處於活躍狀態導致沒有回撥,當生命週期來到onStart的時候就會去回撥。

 

相關文章