Skip to content

ViewModel 源码

Posted on:March 16, 2021 at 19:03:21 GMT+8

摘要

ViewModel 版本:

    implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"

ViewModel 类 源码注释

ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).

ViewModel 是一个负责准备和管理 Activity 或 Fragment 的数据的类。TA 也处理与应用中其它 Activity / Fragment 的交流。

A ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.

In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (e.g. rotation). The new owner instance just re-connects to the existing model.

换句话说,这意味着如果 ViewModel 的 owner 因为配置改变被 destroy 了,ViewModel 不会。新的 owner 实例会重新连接上存在的 model。

The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding. You can also use any observability construct from you favorite framework.

ViewModel 的目的是获得和保存 Activity / Fragment 的必要信息。Activity / Fragment 应该能 observe ViewModel 中的改变。ViewModel 经常通过 LiveData 或 Data Binding 暴露这些信息。你也可以使用任何你喜欢的框架中的 observability construct。

ViewModel’s only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.

ViewModel 只负责管理 UI 的数据。TA 绝不应该访问 view,或保存 Activity / Fragment 的引用。

Typical usage from an Activity standpoint would be:

 public class UserActivity extends Activity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.user_activity_layout);
         final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
         viewModel.getUser().observe(this, new Observer<User>() {
             @Override
             public void onChanged(@Nullable User data) {
                 // update ui.
             }
         });
         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                  viewModel.doAction();
             }
         });
     }
 }

ViewModel would be:

 public class UserModel extends ViewModel {
     private final MutableLiveData<User> userLiveData = new MutableLiveData<>();

     public LiveData<User> getUser() {
         return userLiveData;
     }

     public UserModel() {
         // trigger user load.
     }

     void doAction() {
         // depending on the action, do necessary business logic calls and update the
         // userLiveData.
     }
 }

ViewModels can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.

 public class MyFragment extends Fragment {
     public void onStart() {
         UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class);
     }
 }

ViewModelProvider

构造方法

ViewModelProvider 有 3 种重载:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner)

ViewModelStoreOwner 是一个接口, 代表拥有 ViewModelStore。AppCompatActivity 和 Fragment 都直接或间接实现了 ViewModelStoreOwner 接口。

而 ViewModelStore 是一个存储 ViewModel 的类。内部维护了一个 HashMap<String, ViewModel>,用来保存所有创建的 ViewModel。ViewModelStore 实例在配置改变的过程中必须保持:如果一个 ViewModelStore 的 owner 因为配置改变被 destroy 再 recreate,新的 owner 实例应该仍然持用旧的 ViewModelStore 实例。如果 owner 被 destroy 且不再重新创建,就应该调用 ViewModelStore 的 clear 方法,以通知 ViewModel 不再被使用。

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {

上面两个方法都是调用下面一个方法:

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {

通过给定的 factory 创建 ViewModel,存储在 ViewModelStore 中。

ViewModelProvider#get()

/**
 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
 * an activity), associated with this {@code ViewModelProvider}.
 * <p>
 * The created ViewModel is associated with the given scope and will be retained
 * as long as the scope is alive (e.g. if it is an activity, until it is
 * finished or process is killed).
 *
 * @param key        The key to use to identify the ViewModel.
 * @param modelClass The class of the ViewModel to create an instance of it if it is not
 *                   present.
 * @param <T>        The type parameter for the ViewModel.
 * @return A ViewModel that is an instance of the given type {@code T}.
 */
@SuppressWarnings("unchecked")
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    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.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    //noinspection TryWithIdenticalCatches
    try {
        return modelClass.newInstance();
    } catch (InstantiationException e) {
        throw new RuntimeException("Cannot create an instance of " + modelClass, e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Cannot create an instance of " + modelClass, e);

ViewModel 还未创建时,即第 1 次调用此方法时,将会用 factory 通过反射创建一个 viewModel,再调用 mViewModelStore.put(key, viewModel) 将 viewModel 存入 ViewModelStore。

当 Activity / Fragment 因为配置改变再次创建时,会根据 key 从 ViewModelStore 中取回旧的 ViewModel,即实现了 ViewModel 类源码注释中的“新的 owner 实例会重新连接上存在的 model”。

ViewModelStore

ViewModelStore#clear()

/**
 *  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();
}

调用 clear 来通知 ViewModel 不再使用 TA 们了,且 clear HashMap<String, ViewModel>

ComponentActivity 中:

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

只有在不是配置改变的 destroy 时,才会调用 ViewModelStore 的 clear。因此在配置改变时仍会保存 ViewModel 中数据。

FragmentManagerViewModel 中:

void clearNonConfigState(@NonNull Fragment f) {
    ...
    // Clear and remove the Fragment's ViewModelStore
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    if (viewModelStore != null) {
        viewModelStore.clear();
        mViewModelStores.remove(f.mWho);
    }
}
void destroy(@NonNull FragmentHostCallback<?> host,
        @NonNull FragmentManagerViewModel nonConfig) {
    if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
        Log.d(TAG, "movefrom CREATED: " + mFragment);
    }
    boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
    boolean shouldDestroy = beingRemoved || nonConfig.shouldDestroy(mFragment);
    if (shouldDestroy) {
        boolean shouldClear;
        if (host instanceof ViewModelStoreOwner) {
            shouldClear = nonConfig.isCleared();
        } else if (host.getContext() instanceof Activity) {
            Activity activity = (Activity) host.getContext();
            shouldClear = !activity.isChangingConfigurations();
        } else {
            shouldClear = true;
        }
        if (beingRemoved || shouldClear) {
            nonConfig.clearNonConfigState(mFragment);
        }
        mFragment.performDestroy();
        mDispatcher.dispatchOnFragmentDestroyed(mFragment, false);
    } else {
        mFragment.mState = Fragment.ATTACHED;
    }
}

shouldClear = !activity.isChangingConfigurations();也是判断非配置改变导致的才会清除。

Kotlin 扩展

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

Fragment 中:

import androidx.fragment.app.viewModels
...
private val viewModel: GalleryViewModel by viewModels()

Kotlin 提供的 Fragment 扩展函数封装了我们一般创建 ViewModel 的过程:

@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
@MainThread
fun <VM : ViewModel> Fragment.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
class ViewModelLazy<VM : ViewModel> (
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                // 最终也是通过这个方法创建 ViewModel
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized() = cached != null
}

将 ViewModel 委托给viewModels(),这是一个延迟属性的委托。即第一次使用 ViewModel 时会创建,后续再使用会直接返回结果。

总结

思想:将 ViewModel 存储在 ViewModelStore 中,相对独立于 Activity / Fragment 之外。

参考

源码

ViewModel Overview | Android Developers

Jetpack 中的 ViewModel・Leo’s Studio