Android Architecture Components(三)ViewModel分析

ViewModel简介

与上一篇文章相同,先官方文档翻译,再源码分析~

我们先看一下官方文档的介绍:

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
ViewModel是被设计成在Lifecycle中存储和管理UI相关数据的类。ViewModel使得数据在屏幕旋转之类的配置改变的情形下不被销毁。

Android frameworl层管理着UI控制器(e.g. activity, fragment)的生命周期。framework可能会因为某些用户操作或者是系统事件而直接销毁或重建UI controller,这类事件是在控制之外的。如果出现这种情况,带有transient标志的数据都会丢失。

另一个问题是在UI controller中经常有耗时的异步请求。UI controller需要管理这些请求,保证请求结束后销毁它们,来避免潜在的内存泄漏。这种管理需要一直维护,而且在UI controller因为配置改变而重建时,可能会重新发送之前发送过的请求,浪费资源。

UI controller的主要任务是展示数据、处理用户交互、处理系统会话(例如权限请求)。要求controller也负责从数据库或网络中获取数据,使得controller变得臃肿。将数据的管理从UI controller中分离出来很简单,且更加高效。


实现ViewModel

ViewModel在activity或fragment旋转时会自动保存,保存的数据可以立即提供给重新创建的UI controller。

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

因为ViewModel被设计成生命周期超过view实例和LifecycleOwner,因此有如下注意:

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

如果ViewModel中需要使用context,可以继承AndroidViewModel,构造函数中可以传入Application对象。

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}
ViewModel的生命周期

获取ViewModel对象时,它的生命周期被限定为ViewModelProvider传入的Lifecycle。ViewModel对象会被存储在内存中,直到它被限定的Lifecycle对象被永久销毁(e.g. activity-finish,fragment-detached)。

lifecycle

ViewModel对象在第一次获取时创建,并一直存在,直到UI controller结束或销毁。

fragments间数据传递

fragments之间的数据传递很常见,一般通过实现、调用接口的方式,通过activity传递数据;而现在可以使用同一个activity中ViewModel。假设有两个fragment,一个是列表展示,点击后在另一个fragment中展示详情。

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // same UI controller, same ViewModel
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

优点:

1.activity不需要了解fragment中的任何操作。

2.fragment只需要关注SharedViewModel,而不需要关注activity和其他的fragment。即使另一个fragment消失,也可以继续运行

3.每个fragment拥有自己的生命周期,替换fragment也可以正常运行。

替代Loader类

像CursorLoader这样的加载器类经常用于使应用程序UI中的数据与数据库保持同步。使用ViewModel将数据加载操作和UI controller分开,类之间少了许多强引用。

使用Loader类:可能会使用CursorLoader去观察数据库;当数据库中数据发生改变时,loader触发一次读库并更新UI。

loader

使用ViewModel类:ViewModel使用Room和LiveData来代替loader。ViewModel确保数据能够不因配置改变而销毁;Room在数据库发生数据改变时通知LiveData,LiveData自动更新UI。

viewmodel


ViewModel源码分析

从上文的代码中我们可以看到ViewModel的创建过程:

ViewModelProviders.of(activity).get(XxxViewModel.class);

以Activity为例,先看of(FragmentActivity activity)函数:

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

of函数中最后返回的是ViewModelProvider对象。我们先看factory:factory的创建过程是一个单例模式,AndroidViewModelFactory中实现了create方法

@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
        //noinspection TryWithIdenticalCatches
        try {
            // 通过反射的方式创建对应ViewModel对象
            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);
}

而ViewModelProvider的创建只是将两个参数保存了起来。这两部就是of的主要工作。

接下来看get方法中做了什么:

public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        // 检测对应ViewModel类是否存在
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    // ViewModelStore中维护了一个HashMap来保存ViewModel
    ViewModel viewModel = mViewModelStore.get(key);

    // 当ViewModel存在时,返回
    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }

    // 当ViewModel不存在时,调用of中保存的factory的create函数,利用反射创建一个ViewModel实例。并放入ViewModelStore中
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值