精华内容
参与话题
问答
  • ViewModel 使用及原理解析

    万次阅读 2019-03-21 00:00:25
    ViewModel旨在以生命周期意识的方式存储和管理用户界面相关的数据,它可以用来管理Activity和Fragment中的数据.还可以拿来处理Fragment与Fragment之间的通信等等. 当Activity或者Fragment创建了关联的ViewModel,那么...

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

    本文是基于 androidx.lifecycle:lifecycle-extensions:2.0.0 的源码进行分析

    ViewModel旨在以生命周期意识的方式存储和管理用户界面相关的数据,它可以用来管理Activity和Fragment中的数据.还可以拿来处理Fragment与Fragment之间的通信等等.

    当Activity或者Fragment创建了关联的ViewModel,那么该Activity或Fragment只要处于活动状态,那么该ViewModel就不会被销毁,即使是该Activity屏幕旋转时重建了.所以也可以拿来做数据的暂存.

    ViewModel主要是拿来获取或者保留Activity/Fragment所需要的数据的,开发者可以在Activity/Fragment中观察ViewModel中的数据更改(这里需要配合LiveData食用).

    ps: ViewModel只是用来管理UI的数据的,千万不要让它持有View、Activity或者Fragment的引用(小心内存泄露)。

    本文以由浅入深的方式学习ViewModel

    一、ViewModel的使用

    1. 引入ViewModel

    //引入AndroidX吧,替换掉support包
    implementation 'androidx.appcompat:appcompat:1.0.2'
    
    def lifecycle_version = "2.0.0"
    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    

    2. 简单使用起来

    1. 定义一个User数据类
    class User implements Serializable {
    
        public int age;
        public String name;
    
        public User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    1. 然后引出我们今天的主角ViewModel
    public class UserModel extends ViewModel {
    
        public final MutableLiveData<User> mUserLiveData = new MutableLiveData<>();
    
        public UserModel() {
            //模拟从网络加载用户信息
            mUserLiveData.postValue(new User(1, "name1"));
        }
        
        //模拟 进行一些数据骚操作
        public void doSomething() {
            User user = mUserLiveData.getValue();
            if (user != null) {
                user.age = 15;
                user.name = "name15";
                mUserLiveData.setValue(user);
            }
        }
    
    }
    
    1. 这时候在Activity中就可以使用ViewModel了. 其实就是一句代码简单实例化,然后就可以使用ViewModel了.
    //这些东西我是引入的androidx下面的
    import androidx.fragment.app.FragmentActivity;
    import androidx.lifecycle.Observer;
    import androidx.lifecycle.ViewModelProviders;
    
    public class MainActivity extends FragmentActivity {
    
        private TextView mContentTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mContentTv = findViewById(R.id.tv_content);
    
            //构建ViewModel实例
            final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
    
            //让TextView观察ViewModel中数据的变化,并实时展示
            userModel.mUserLiveData.observe(this, new Observer<User>() {
                @Override
                public void onChanged(User user) {
                    mContentTv.setText(user.toString());
                }
            });
    
            findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //点击按钮  更新User数据  观察TextView变化
                    userModel.doSomething();
                }
            });
        }
    }
    

    这个时候,我们点击一下按钮(user中的age变为15),我们可以旋转手机屏幕(这个时候其实Activity是重新创建了,也就是onCreate()方法被再次调用,但是ViewModel其实是没有重新创建的,还是之前那个ViewModel),但是当我们旋转之后,发现TextView上显示的age居然还是15,这就是ViewModel的魔性所在.这个就不得不提ViewModel的生命周期了,它只有在Activity销毁之后,它才会自动销毁(所以别让ViewModel持有Activity引用啊,会内存泄露的). 下面引用一下谷歌官方的图片,将ViewModel的生命周期展示的淋漓尽致.

    3. ViewModel妙用1: Activity与Fragment"通信"

    有了ViewModel,Activity与Fragment可以共享一个ViewModel,因为Fragment是依附在Activity上的,在实例化ViewModel时将该Activity传入ViewModelProviders,它会给你一个该Activity已创建好了的ViewModel,这个Fragment可以方便的访问该ViewModel中的数据.在Activity中修改userModel数据后,该Fragment就能拿到更新后的数据.

    public class MyFragment extends Fragment {
         public void onStart() {
            //这里拿到的ViewModel实例,其实是和Activity中创建的是一个实例
             UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
         }
     }
    

    4. ViewModel妙用2: Fragment与Fragment"通信"

    下面我们来看一个例子(Google官方例子)

    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);
            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. 首先定义一个ViewModel,在里面放点数据
    2. 然后在MasterFragment和DetailFragment都可以拿到该ViewModel,拿到了该ViewModel就可以拿到里面的数据了,相当于间接通过ViewModel通信了. so easy…

    二、ViewModel源码解析

    又到了我们熟悉的源码解析环节

    我们从下面这句代码start.

    final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
    

    我们跟着ViewModelProviders.of(this)打开新世界的大门

    1. ViewModelProviders.of(this) 方法

    /**
     * 用于构建一个ViewModelProvider,当Activity是alive时它会保留所有的该Activity对应的ViewModels.
     */
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        //检查application是否为空,不为空则接收
        Application application = checkApplication(activity);
        if (factory == null) {
            //构建一个ViewModelProvider.AndroidViewModelFactory
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }
    
    

    ViewModelProviders里面的of()函数其实是为了方便我们构建一个ViewModelProvider.而ViewModelProvider,一看名字就知道干啥的了,就是提供ViewModel的.

    Factory是ViewModelProvider的一个内部接口,它的实现类是拿来构建ViewModel实例的.它里面只有一个方法,就是创建一个ViewModel.

    /**
     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
     */
    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }
    

    Factory有2个实现类:一个是NewInstanceFactory, 一个是AndroidViewModelFactory .

    • NewInstanceFactory源码
    public static class NewInstanceFactory implements Factory {
    
            @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);
                }
            }
        }
    

    NewInstanceFactory专门用来实例化那种构造方法里面没有参数的class,并且ViewModel里面是不带Context的,然后它是通过newInstance()去实例化的.

    • AndroidViewModelFactory 源码
    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    
        private static AndroidViewModelFactory sInstance;
    
        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }
    
        private Application mApplication;
    
        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }
    
        @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);
        }
    }
    

    AndroidViewModelFactory专门用来实例化那种构造方法里面有参数的class,并且ViewModel里面可能是带Context的.

    • 它是通过newInstance(application)去实例化的.如果有带application参数则是这样实例化
    • 如果没有带application参数的话,则还是会走newInstance()方法去构建实例.

    AndroidViewModelFactory通过构造方法给ViewModel带入Application,就可以在ViewModel里面拿到Context,因为Application是APP全局的,那么不存在内存泄露的问题.完美解决了有些ViewModel里面需要Context引用,但是又担心内存泄露的问题.

    下面我们继续ViewModelProviders.of(this)方法继续分析吧,注意最后一句new ViewModelProvider(activity.getViewModelStore(), factory);第一个参数会调用activity的getViewModelStore()方法(这个方法会返回ViewModelStore,这个类是拿来存储ViewModel的,下面会说到),这里的activity是androidx.fragment.app.FragmentActivity,看一下这个getViewModelStore()方法

    /**
     * 获取这个Activity相关联的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;
    }
    
    //没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore
    //注意,这是FragmentActivity中的NonConfigurationInstances(其实Activity中还定义了一个NonConfigurationInstances,内容要比这个多一些,但是由于没有关系到它,这里就不提及了)
    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
        FragmentManagerNonConfig fragments;
    }
    
    

    Android横竖屏切换时会触发onSaveInstanceState(),而还原时会调用onRestoreInstanceState(),但是Android的Activity类还有2个方法名为onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()这两个方法。

    来具体看看这2个素未谋面的方法

    /**
     保留所有fragment的状态。你不能自己覆写它!如果要保留自己的状态,请使用onRetainCustomNonConfigurationInstance()
     这个方法在FragmentActivity里面
     */
    @Override
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
    
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
    
        if (fragments == null && mViewModelStore == null && custom == null) {
            return null;
        }
    
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }
    
    
    //这个方法在Activity里面,而mLastNonConfigurationInstances.activity实际就是就是上面方法中年的nci
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
    
    

    我们来看看getLastNonConfigurationInstance()的调用时机,

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ......
        super.onCreate(savedInstanceState);
    
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
            mViewModelStore = nc.viewModelStore;
        }
        ......
    }
    

    没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore,放到了NonConfigurationInstances实例里面,横竖屏切换时保存了又恢复了回来,相当于ViewModel实例就还在啊,也就避免了横竖屏切换时的数据丢失.

    2. viewModelProvider.get(UserModel.class)

    下面我们来到那句构建ViewModel代码的后半段,它是ViewModelProvider的get()方法,看看实现,其实很简单

    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);
    }
    
    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.
            }
        }
        
        //无缓存  则重新通过mFactory构建
        viewModel = mFactory.create(modelClass);
        //缓存起来
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
    
    

    大体思路是利用一个key来缓存ViewModel,有缓存则用缓存的,没有则重新构建.构建时使用的factory是上面of()方法的那个factory.

    3. ViewModelStore

    上面多个地方用到了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();
        }
    }
    
    

    ViewModelStore有一个HashMap专门用于存储,普通吧.

    下面看看何时调用的clear()

    4. ViewModel.onCleared() 资源回收

    既然ViewModel是生命周期感知的,那么何时应该清理ViewModel呢?

    我们来到FragmentActivity的onDestroy()方法,发现它是在这里清理的.

    /**
     * Destroy all fragments.
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
    
        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
    
        mFragments.dispatchDestroy();
    }
    

    5. 再看 ViewModel

    很多朋友可能就要问了,ViewModel到底是什么?

    public abstract class ViewModel {
        /**
         * 这个方法会在ViewModel即将被销毁时调用,可以在这里清理垃圾
         */
        @SuppressWarnings("WeakerAccess")
        protected void onCleared() {
        }
    }
    

    其实很简单,就一个抽象类,里面就一个空方法??? 我擦,搞了半天,原来ViewModel不是主角…

    6. AndroidViewModel

    ViewModel有一个子类,是AndroidViewModel.它里面有一个Application的属性,仅此而已,为了方便在ViewModel里面使用Context.

    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 的源码其实不多,理解起来比较容易,主要是官方FragmentActivity提供了技术实现,onRetainNonConfigurationInstance()保存状态,getLastNonConfigurationInstance()恢复。

    原来Activity还有这么2个玩意儿,之前我还只是知道onSaveInstanceState()和onRestoreInstanceState(),涨姿势了。

    展开全文
  • 谷歌官方Android应用架构库——ViewModel

    架构库版本:1.0.0 Alpha 2 - June 2, 2017

    像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的。Framework可能会根据用户的一些操作和设备的状态对Activity或者Fragment进行销毁和重构。作为开发者,这些行为我们是无法干预的。

    所以Activity或Fragment中的一些数据也会随着销毁而丢失,随着重构而重新生成。比如你的Activity中有个用户列表,当这个Activity重构的时候,新的Activity会重新获取用户列表。对于一些简单的数据,Activity可以使用onSaveInstanceState()方法,并从onCreate的bundle中重新获取。但这一方法途径仅仅适合一些简单的UI状态,对于用户列表这种庞大的数据并不适合。

    还存在一个问题,Activity或者Fragment经常会做一些异步的耗时操作。随之就需要Activity和Fragment管理这些异步操作,并在自己被destroyed的时候清理它们,从而保证内存溢出这类问题的发生。这样的处理会随着项目扩大而变得十分复杂,一不留神,你的App就Crash了。

    Activity和Fragment本身需要处理很多用户的输入事件并和操作系统打交道,所以当它们还要花时间管理它们的数据资源时,class文件就会变得异常庞大,然后就会造就出所谓的god activities和god fragments。这些UI控制类仅仅靠一个class就能处理相关的所有事务。简直跟上帝没啥两样。但这些类如果要进行单元测试的话很困难了。

    所以就有了MVC,MVP这类设计模式,将视图与数据分离。今天讲到的ViewModel类的功能也一样,就是讲数据从UI中分离出来。并且当Activity或Fragment重构的时候,ViewModel会自动保留之前的数据并给新的Activity或Fragment使用。对于上面提到的用户列表的例子,ViewModel会为我们很好的管理这些数据。

    public class MyViewModel extends ViewModel {
        private MutableLiveData<List<User>> users;
        public LiveData<List<User>> getUsers() {
            if (users == null) {
                users = new MutableLiveData<List<Users>>();
                loadUsers();
            }
            return users;
        }
    
        private void loadUsers() {
            // do async operation to fetch users
        }
    }

    接着在我们的Activity中就能这样使用了:

    public class MyActivity extends AppCompatActivity {
        public void onCreate(Bundle savedInstanceState) {
            MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
            model.getUsers().observe(this, users -> {
                // update UI
            });
        }
    }

    如果当MyActivity被重构时,获得到的model实例是与重构前同一个,当MyActivity被销毁时,Framework会调用ViewModel的onCleared(),我们就可以在此方法中做资源的清理。

    因为ViewModel的生命周期是和Activity或Fragment分开的,所以在ViewModel中绝对不能引用任何View对象或者任何引用了Activity的Context的对象。如果ViewModel中需要Application的Context的话,可以继承AndroidViewModel。

    Fragment之间的数据共享

    在Activity中包好多个Fragment并且需要相互通信是非常常见的,这时就需要这些Fragment定义一些接口,然后让Activity来进行协调。而且这些Fragment还需要处理其他Fragment不可见或者还没有创建这些细节问题。

    上面这个问题可以被ViewModel轻易解决,想象意向有这么个Activity,它包含FragmentA和FragmentB,其中A是用户列表,B是用户的详细数据,点击列表上的某个用户,在B中显示相应的数据。

    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 onActivityCreated() {
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }
    
    public class DetailFragment extends LifecycleFragment {
        public void onActivityCreated() {
            SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(this, { item ->
               // update UI
            });
        }
    }

    这里要注意的是两个Fragment都使用了getActivity作为参数来获得ViewModel实例。这表示这两个Fragment获得的ViewModel对象是同一个。

    使用了ViewModel的好处如下:

    • Activity不需要做任何事情,不需要干涉这两个Fragment之间的通信。

    • Fragment不需要互相知道,即使一个消失不可见,另一个也能很好的工作。

    • Fragment有自己的生命周期,它们之间互不干扰,即便你用一个FragmentC替代了B,FragmentA也能正常工作,没有任何问题。

    ViewModel的生命周期

    ViewModel的生命周期跟着传递给ViewModelProvider的LifeCycle走,当生成了ViewModel的实例后,它会一直待在内存中,直到对应的LifeCycle彻底结束。下面是ViewModel与Activity对应的生命周期图:

    viewmodel-lifecycle

    展开全文
  • Android架构组件——ViewModel

    万次阅读 多人点赞 2018-03-02 20:24:36
    正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的,同时ViewModel还可以用来负责UI组件间的通信。 之前存在的问题 ViewModel用来...

    概述

    ViewModel,从字面上理解的话,它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的,同时ViewModel还可以用来负责UI组件间的通信。

    之前存在的问题

    ViewModel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。

    引入ViewModel之前,存在如下几个问题:

    • 通常Android系统来管理UI controllers(如Activity、Fragment)的生命周期,由系统响应用户交互或者重建组件,用户无法操控。当组件被销毁并重建后,原来组件相关的数据也会丢失,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
    • UI controllers经常会发送很多异步请求,有可能会出现UI组件已销毁,而请求还未返回的情况,因此UI controllers需要做额外的工作以防止内存泄露。
      当Activity因为配置变化而销毁重建时,一般数据会重新请求,其实这是一种浪费,最好就是能够保留上次的数据。
    • UI controllers其实只需要负责展示UI数据、响应用户交互和系统交互即可。但往往开发者会在Activity或Fragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。

    ViewModel基本使用

    public class MyViewModel extends ViewModel {
        private MutableLiveData<List<User>> users;
        public LiveData<List<User>> getUsers() {
            if (users == null) {
                users = new MutableLiveData<List<Users>>();
                loadUsers();
            }
            return users;
        }
    
        private void loadUsers() {
            // 异步调用获取用户列表
        }
    }

    新的Activity如下:

    public class MyActivity extends AppCompatActivity {
        public void onCreate(Bundle savedInstanceState) {
            MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
            model.getUsers().observe(this, users -> {
                // 更新 UI
            });
        }
    }

    如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法清除资源。

    因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。

    在Fragment间共享数据

    一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯:

    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 onActivityCreated() {
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }
    
    public class DetailFragment extends LifecycleFragment {
        public void onActivityCreated() {
            SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(this, { item ->
               // update UI
            });
        }
    }

    注意:上面两个Fragment都用到了如下代码来获取ViewModel,getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。

    SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

    这种方式的好处包括:

    • Activity不需要做任何事情,也不需要知道通讯的事情
    • Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
    • 每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。

    ViewModel的生命周期

    ViewModel对象的范围由获取ViewModel时传递至ViewModelProvider的Lifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
    这里写图片描述
    上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。

    一般通过如下代码初始化ViewModel:

    viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);

    this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。

    Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。

    ViewModel相关类图

    借用Android架构组件(三)——ViewModel的类图:
    这里写图片描述

    • ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是由ViewModelProvider实现的。

    • ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。

    • ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。

    • ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到不能在ViewModel中持有Activity的引用。

    • ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。

    ViewModel相关时序图

    追溯创建一个ViewModel的源码,会察觉需要的步骤有点多。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
    借用Android架构组件(三)——ViewModel的时序图:
    这里写图片描述

    时序图看起来比较复杂,但是它只描述了两个过程:

    1. 得到ViewModel对象。
    2. HolderFragment被销毁时,ViewModel收到onCleared()通知。

    ViewModel相关源码分析

    ViewModelProviders类的具体实现:

    public class ViewModelProviders {
    
        private static Application checkApplication(Activity activity) {
            Application application = activity.getApplication();
            if (application == null) {
                throw new IllegalStateException("Your activity/fragment is not yet attached to "
                        + "Application. You can't request ViewModel before onCreate call.");
            }
            return application;
        }
    
        private static Activity checkActivity(Fragment fragment) {
            Activity activity = fragment.getActivity();
            if (activity == null) {
                throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
            }
            return activity;
        }
    
        @NonNull
        @MainThread
        public static ViewModelProvider of(@NonNull Fragment fragment) {
            ViewModelProvider.AndroidViewModelFactory factory =
                    ViewModelProvider.AndroidViewModelFactory.getInstance(
                            checkApplication(checkActivity(fragment)));
            return new ViewModelProvider(ViewModelStores.of(fragment), factory);
        }
    
        @NonNull
        @MainThread
        public static ViewModelProvider of(@NonNull FragmentActivity activity) {
            ViewModelProvider.AndroidViewModelFactory factory =
                    ViewModelProvider.AndroidViewModelFactory.getInstance(
                            checkApplication(activity));
            return new ViewModelProvider(ViewModelStores.of(activity), factory);
        }
    
        @NonNull
        @MainThread
        public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
            checkApplication(checkActivity(fragment));
            return new ViewModelProvider(ViewModelStores.of(fragment), factory);
        }
    
        @NonNull
        @MainThread
        public static ViewModelProvider of(@NonNull FragmentActivity activity,
                @NonNull Factory factory) {
            checkApplication(activity);
            return new ViewModelProvider(ViewModelStores.of(activity), factory);
        }

    ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。
    1. 判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
    2. 创建ViewModel对象看似很简单,一行代码搞定。

    new ViewModelProvider(ViewModelStores.of(fragment), factory)

    先看看ViewModelStores.of()方法:

        @NonNull
        @MainThread
        public static ViewModelStore of(@NonNull Fragment fragment) {
            if (fragment instanceof ViewModelStoreOwner) {
                return ((ViewModelStoreOwner) fragment).getViewModelStore();
            }
            return holderFragmentFor(fragment).getViewModelStore();
        }

    继续深入发现其实是实现了一个接口:

    public interface ViewModelStoreOwner {
        @NonNull
        ViewModelStore getViewModelStore();
    }

    holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现

        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static HolderFragment holderFragmentFor(Fragment fragment) {
            return sHolderFragmentManager.holderFragmentFor(fragment);
        }
    
        @NonNull
        @Override
        public ViewModelStore getViewModelStore() {
            return mViewModelStore;
        }

    继续看HolderFragmentManager.holderFragmentFor()方法的具体实现

    HolderFragment holderFragmentFor(Fragment parentFragment) {
                FragmentManager fm = parentFragment.getChildFragmentManager();
                HolderFragment holder = findHolderFragment(fm);
                if (holder != null) {
                    return holder;
                }
                holder = mNotCommittedFragmentHolders.get(parentFragment);
                if (holder != null) {
                    return holder;
                }
    
                parentFragment.getFragmentManager()
                        .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
                holder = createHolderFragment(fm);
                mNotCommittedFragmentHolders.put(parentFragment, holder);
                return holder;
            }
    
    private FragmentLifecycleCallbacks mParentDestroyedCallback =
        new FragmentLifecycleCallbacks() {
            @Override
            public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
                super.onFragmentDestroyed(fm, parentFragment);
                HolderFragment fragment = mNotCommittedFragmentHolders.remove(
                        parentFragment);
                if (fragment != null) {
                    Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
                }
            }
        };
    
    private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
        HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象
        fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
        return holder;
    }
    
    public HolderFragment() {
        //这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用
        setRetainInstance(true); 
    }

    setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。

    在setRetainInstance(boolean)为true的 Fragment 中放一个专门用于存储ViewModel的Map, 自然Map中所有的ViewModel都会幸免于Activity重建,让Activity, Fragment都绑定一个这样的Fragment, 将ViewModel存放到这个 Fragment 的 Map 中, ViewModel 组件就这样实现了。

    到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法

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

    现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现

        @NonNull
        @MainThread
        public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
            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 = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。
            mViewModelStore.put(key, viewModel);
            //noinspection unchecked
            return (T) viewModel;
        }

    ViewModelProvider.get()方法比较简单,注释中都写明了。最后我们看下ViewModelStore类的具体实现

    public class ViewModelStore {
    
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
        final void put(String key, ViewModel viewModel) {
            ViewModel oldViewModel = mMap.get(key);
            if (oldViewModel != null) {
                oldViewModel.onCleared();
            }
            mMap.put(key, viewModel);
        }
    
        final ViewModel get(String key) {
            return mMap.get(key);
        }
    
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.onCleared();
            }
            mMap.clear();
        }
    }

    ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。

    ViewModel收到onCleared()通知
    HolderFragment的onDestroy()方法

        @Override
        public void onDestroy() {
            super.onDestroy();
            mViewModelStore.clear();
        }

    在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。

    总结

    • ViewModel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。
    • ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
    • ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
    • ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。

    官方:
    https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

    展开全文
  • MVVM 架构,ViewModel和LiveData(一)

    万次阅读 2018-05-19 22:33:09
    MVVM 架构,ViewModel和LiveData(一) 标签(空格分隔): 翻译计划 Android开发 原文链接 MVVM architecture, ViewModel and LiveData (Part 1) 正文 在Google I/O之间,Google推出了包含LiveData和...

    MVVM 架构,ViewModel和LiveData(一)

    标签(空格分隔): 翻译计划 Android开发


    原文链接

    正文

    在Google I/O之间,Google推出了包含LiveData和ViewModel的组件架构,这有助于开发者们使用MVVM开发Android应用程序。这篇文章旨在讲述该组件如何融汇在使用MVVM模式开发Android应用程序。

    MVVM简单介绍

    如果你很熟悉MVVM架构,那么你可以快速跳过本小节知识点。

    MVVM是增强关注点分离的体系结构模式之一,它允许将用户界面逻辑从业务(或者后端)逻辑中分离开来,他的目标(和MVC等其他目标)是为了实现”保持UI代码简单化,不涉及更多的业务逻辑,以便于开发者更好的控制和管理”。

    MVVM主要有以下几个层次:

    • 1、Model层
      • Model层表示用户程序的数据和业务逻辑,这一层的的推荐的实现策略之一就是观测数据的变化并传递出去(供谁使用),使其从ViewModel或者其他观察者/消费者中完全解耦.(这将在我们文章下面的MVVM事例中进行说明).
    • 2、ViewModel层
      • ViewModel是和Model(数据层)进行交互,并且ViewMode可以被View观察.ViewModel可以选择性地为视图提供钩子以将事件传递给模型.该层的一个重要实现策略是将Model与View分离,即ViewModel不应该意识到与谁交互的视图.
    • 3、View层
      • 此模式中的视图角色是观察(或订阅)ViewMode,观察数据b变化,以便于获取数据去更新UI元素.

    下图显示了MVVM组件和基本交互。
    MVVM.png

    LiveData

    如上所述,LiveData是新引入的组件架构之一,LiveData是一个可以被观察的数据持有者.这也就意味着应用中的组件能够观察LiveData对象的更改,而无需在它们之间创建明确的和严格的依赖关系。这将完全分离LiveData对象使用者和LiveData对象生产者。

    除此之外,LiveData还有一个很大的好处,LiveData遵守应用程序组件(活动,片段,服务)的生命周期状态,并进组件的生命周期管理,确保LiveData对象的内存泄漏.

    根据Google文档,如果您已经在使用Rx或Agera等开源库,那么你可以继续使用它们而不是替换成LiveData.但在这种情况下,您有责任处理每个Android组件生命周期的对象分配和解除分配.

    由于LiveData遵从Android的生命周期机制,这意味着除非LiveData主体(activity或fragment)处于活动状态(接收onStart()但未收到onStop()),否则它将不会调用其观察者回调.除此之外,当主体收到onDestroy()时,LiveData也会自动删除观察者防止内存泄漏.

    LiveData也会将在下面的MVVM事例中进行说明.

    ViewModel

    ViewModel也是新引入的体系架构组件之一.架构组件提供了一个名为ViewModel的新类,它负责为UI / View准备数据.

    ViewModel为您的MVVM模式中的ViewModel提供了一个很好的基类,因为ViewModel(及其子类AndroidViewModel)的扩展类会在配置更改期间自动保留其数据.这意味着,在配置更改后,此ViewModel所有数据可立即用于下一个活动(activity)或片段(fragment)实例.

    下图显示了ViewModel组件的生命周期:
    The life cycle of ViewModel component

    ViewModel也将会在下面的MVVM事例中进行说明。

    应用事例

    现在,让我们来看看最有趣的部分,让我们把上述的所有知识(组件)放在一个程序中.
    此MVVM事例应用主要包含两个界面.
    下面显示的第一个界面显示了Google GitHub上的项目列表.其中包含一些简要信息,例如标题,编程语言和watcher数量。
    image.png

    一旦用户点击了首页的item,GitHub项目的详细信息屏幕将会显示项目描述,编程语言,watcher数量,公开问题,创建和上次更新日期,最后显示克隆URL.

    image.png

    示例应用程序交互图

    下图显示了示例应用程序的包结构

    image.png

    以下交互图显示了检索Google GitHub项目的应用场景之一的示例交互图。
    image.png

    如上图所示,每个图层都从其后续图层(Fragment(View) - > ViewModel - > Repository)观察LiveData,最后一旦检索到项目列表(或者有所变化),就会使用RecyclerView(ListView)适配器绑定显示项目列表.

    Respository模块负责处理数据操作,通过这一点,Respository可以为程序的其余部分提供干净的API,并简化ViewModel的工作.
    如果需要更新数据,Respository模块应该知道从哪里获取数据以及进行哪些API调用.它们可以被视为不同数据源(REST服务,数据库,XML文件等)之间的中介.

    现在,让我们从下往上解释这些图层,从Model、ViewModel开始,最后用View来检索GitHub项目场景。

    示例应用程序模型层

    让我们从业务逻辑层开始,我们有两个模型对象

    • 1、Project.包含GitHub项目的信息,如id,名称,描述,创建日期等等
    • 2、User,包含GitHub项目所有者的用户信息.
      为了与GitHub RESTful API进行交互,我使用了我喜欢的Retrofit2来定义存储库包中的以下简单接口。
    interface GitHubService {
        String HTTPS_API_GITHUB_URL = "https://api.github.com/";
    
        @GET("users/{user}/repos")
        Call<List<Project>> getProjectList(@Path("user") String user);
    
        @GET("/repos/{user}/{reponame}")
        Call<Project> getProjectDetails(@Path("user") String user, @Path("reponame") String projectName);
    }

    为了便于ViewModel的工作,创建一个ProjectRespository来与GitHub服务交互,并最终为ViewModel提供一个LiveData对象.以下代码片段显示了getProjectList()API实现.

    public class ProjectRepository {
        private GitHubService gitHubService;
    
        //…
    
        public LiveData<List<Project>> getProjectList(String userId) {
            final MutableLiveData<List<Project>> data = new MutableLiveData<>();
    
            gitHubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
                @Override
                public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
                    data.setValue(response.body());
                }
    
                // Error handling will be explained in the next article …
            });
    
            return data;
        }
    
        // …
    }

    ProjectRepository是ViewModel的数据提供者,它有getProjectList(),它简单地将响应包装到LiveData对象中.

    为了简化本文的目的,错误处理被省略,并且将在下一篇文章中进行说明

    示例应用程序ViewModel层

    为了处理(接收)getProjectList()API,创建了ViewModel类(调用Repository API并可以为LiveData执行任何所需的数据转换).
    以下代码片段显示了ProjectListViewModel类

    public class ProjectListViewModel extends AndroidViewModel {
        private final LiveData<List<Project>> projectListObservable;
    
        public ProjectListViewModel(Application application) {
            super(application);
    
            // If any transformation is needed, this can be simply done by Transformations class ...
            projectListObservable = ProjectRepository.getInstance().getProjectList("Google");
        }
    
        /**
         * Expose the LiveData Projects query so the UI can observe it.
         */
        public LiveData<List<Project>> getProjectListObservable() {
            return projectListObservable;
        }
    }

    如上所示,我们的ProjectListViewModel类继承了AndroidViewModel,并在构造函数中调用getProjectList(“Google”)来检索Google GitHub项目.

    在现实世界的情况下,在将结果数据传递到观察视图之前可能需要进行转换,为了进行转换,可以使用Transformation类,如以下文档中所示https://developer.android.com/topic/libraries/architecture/livedata.html#transformations_of_livedata

    示例应用视图图层View

    最后,让我们快速浏览一下这个应用程序的视图层,我们主要有一个名为MainActivity的Activity(活动),它负责处理代表应用程序视图的两个片段的导航.

    • 1、ProjectListFragment:其中显示了Google GitHub项目的列表界面
    • 2、ProjectFragment:它显示所选的GitHub项目详细信息

    由于活动和片段被视为生命周期所有者,activity需要扩展LifecycleActivity,fragment需要扩展LifecycleFragment.但是,请务必记住LifecycleActivity和LifecycleFragment类都是临时实现,直到Lifecycle与支持库支持为止:https://developer.android.com/reference/android/arch/lifecycle/LifecycleActivity.html

    现在,让我们继续我们的项目检索方案,查看ProjectListFragment这个界面,下面的代码片段显示了最重要的集成部分.

    public class ProjectListFragment extends LifecycleFragment {
        private ProjectAdapter projectAdapter;
    
        //…
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            final ProjectListViewModel viewModel =
                    ViewModelProviders.of(this).get(ProjectListViewModel.class);
    
            observeViewModel(viewModel);
        }
    
        private void observeViewModel(ProjectListViewModel viewModel) {
            // Update the list when the data changes
            viewModel.getProjectListObservable().observe(this, new Observer<List<Project>>() {
                @Override
                public void onChanged(@Nullable List<Project> projects) {
                    if (projects != null) {
                        //…
                        projectAdapter.setProjectList(projects);
                    }
                }
            });
        }
    
        //…
    }

    如上代码所示,ProjectListFragment获取ProjectListViewModel数据对象,然后监听其getProjectListObservable()方法,以便获取Github项目列表。
    最后,一旦检索到项目列表(有数据变化),它将被传递给projectAdapter(RecyclerView适配器),以显示RecyclerView组件中(更新UI).

    这是对项目的一个端到端场景的解释,您可以在这里找到GitHub中提供的完整项目:

    MVVM模式的重要指导原则

    现在,重点介绍MVVM实现的一些重要指导原则:

    • 1、如示例中所示,ViewModels不会直接引用Views,因为如果这样做,ViewModels可能会超出View的生命周期,并且可能会发生内存泄漏
    • 2、建议Model和ViewModel中的桥梁LiveData公开其数据,因为LiveData遵守应用程序组件(活动、片段、服务)的生命周期状态并处理对象生命周期管理,以确保LiveData对象不泄漏

    下一篇文章的相关知识点

    故事尚未完成,因为有些事情需要处理,比如:

    • 1、Dependency Injection(依赖注入)
    • 2、Error Handling(错误处理)
    • 3、Caching(缓存)
    • 4、使用Room操作数据(数据库)
    • 5、Unit Testing(单元测试)
    • 6、Others (其他)

    这将在MVVM的下一系列文章中进行说明,敬请关注.

    展开全文
  • 感受LiveData 与 ViewModel结合之美

    万次阅读 2018-06-19 13:48:19
    LiveData与ViewModel都是Android官方架构组件(Android Architecture Components)之一。 1.前言 虽说这篇是说LiveData与ViewModel,但是或多或少都有涉及另外一个组件:Lifecycles 。它们连同Room都是在17年...
  • ViewModel 作为 Jetpack 中的明星组件,相信大家都对其有一定的了解。在 Google 的官方介绍中也详细的罗列了 ViewModel 的优点,如: 可以提供和管理UI界面数据。(将加载数据与数据恢复从 Activity or Fragment中...
  • ViewModel 是jetpack(Google 提供的应用开发框架)中的解耦数据和UI,可感知生命周期的方式存储数据,可以随着Activity的生命周期进行数据的控制,解决了以前Activity的切换横竖屏重新加载数据等问题,但是...
  • Android官方架构组件ViewModel:从前世今生到追本溯源

    千次阅读 多人点赞 2018-12-03 02:08:44
    Android官方架构组件ViewModel:从前世今生到追本溯源 Android官方架构组件Paging:分页库的设计美学 Android官方架构组件Navigation:大巧不工的Fragment管理框架 实战:使用MVVM尝试开发Github...
  • 先声明此文是转自MSDN杂志的。之前我和一些同行讨论过MVVM模式,很多人都觉得Silverlight或者WPF项目中没多大必要使用它。今天看到这篇神作,我觉得有必要给大家转过来看看。。。仔细阅读,相信你会受益匪浅。...
  • / 今日科技快讯 /近日,谷歌宣布2020年投资计划,承诺将在加州、科罗拉多州、乔治亚州、马萨诸塞州、内布拉斯加州、纽约州、俄亥俄州、俄克拉荷马州、宾夕法尼亚州、德克萨斯州和华盛...
  • ViewModel

    千次阅读 2017-12-25 12:33:22
    ViewModel 负责根据生命周期来存储和管理 UI 相关(Activity 或 Fragment)的数据。当配置发生变化时,如:屏幕方向旋转,它可以保证数据不被销毁。
  • ViewModel

    2020-09-05 10:11:23
    实现 ViewModel 架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 Activity 或 ...
  • ViewModel

    2020-05-22 18:25:30
    1. ViewModel是什么? ViewModel:即View(视图)Model(数据)。它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,以及UI组件通信。 2. ViewModel生命周期 上图是用Activity作为例子,左侧表示...
  • ViewModel

    2020-08-12 10:52:51
    随着 Android 架构的演进,从 MVC 到 MVP 再到现在的 MVVM,项目的结构越来越清晰,耦合度也越来越低,本质上讲就是对 UI 和逻辑的分离,而在这一分离的过程中,MVP 的 presenter 和 MVVM 中的ViewModel 都起了很...
  • ViewModel

    2018-07-13 13:26:43
    在Activity中创建使用ViewModel:/**转入Activity就行*/ GirlsViewModel girlsViewModel = ViewModelProviders.of(ActivityGirls.this).get(GirlsViewModel.class);2.在Fragment中创建使用ViewModel:/**转入Fragm.....
  • ViewModel

    2020-09-02 13:44:32
    ViewModel is a class that is responsible for preparing and managing the data for an [Activity] or a [Fragment]. ViewModel 是一个类,负责准备和管理活动或片段的数据。它还处理 Activity/Fragment 与应用...
  • 最近一个月主要实现了IM功能,其中UI框架使用了ViewModel和LiveData的存储和通知机制,实现后代码简洁易于维护。 感慨于Android arch components控件强大同时,需要顺带分析一波其中源码实现。今天先来分析一下...
  • ViewModel

    2020-10-14 16:09:07
    ViewModel用于Activity和Fragment之间传递数据。通常用法: MyDataModel model = new ViewModelProvider(getViewModelStore(),new MyDataModelFactory()).get(MyDataModel.class); getViewModelStore,由Activity...
  • ViewModel

    2020-01-15 16:52:24
    ViewModel是什么? ViewModel 是在2017年谷歌I/O大会上,推出ViewModel组件,用来规范开发者实现MVVM,是以感知生命周期的形式来存储和管理视图相关的数据。 ViewModel主要特点: (1) 当Activity被销毁时,我们要...
  • ViewModel

    千次阅读 2017-08-02 13:40:16
    ViewModel是用来保存和操作UI相关的数据的,这样即使configuration发生改变,数据仍然可以存在。引入ViewModel的原因有三点: Activity和Fragment等App组件的生命周期都是由系统控制的,是开发者不可控的,保存在...

空空如也

1 2 3 4 5 ... 20
收藏数 35,383
精华内容 14,153
关键字:

viewmodel