• 2.1.2.Architecture components_ViewModel


    参考

    https://developer.android.com/topic/libraries/architecture/viewmodel

    官方例子

    https://github.com/android/architecture-components-samples/tree/master/BasicSample

    ViewModel

    ViewModel类旨在以生命周期感知的方式存储和管理与UI相关的数据。

    ViewModel类允许数据幸免于配置更改(例如屏幕旋转)。

    ViewModel其实算是Presenter。

    对于少量的数据可以在activity的onSaveInstanceState保存,在下次重建activity的onCreate中恢复,但对于大量数据(比如bitmap)是不能在onSaveInstanceState中保存的,因为会造成卡顿,甚至anr,所以可以用全局的缓存位置来保存此类数据,而ViewModel就是官方提供给我们使用的一个这样的库。

    简单使用:

    1. 继承ViewModel,假设是MyViewModel,在其中写入你自己的逻辑

    2. 通过ViewModelProviders.of(this).get(MyViewModel.class),来获取此ViewModel的实例。

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

    l 如果重新创建了Activity,则它将收到与第一次Activity创建的相同的MyViewModel实例。

    l 当Activity不是由于系统原因,即用户明确finish时,系统将调用ViewModel对象的onCleared()方法,以便清理资源。这个在后边会进行一些源码分析。

    l ViewModel对象可以包含LifecycleObservers,例如LiveData对象。 但是,ViewModel对象绝不能观察对生命周期感知型的可观察对象的更改,例如LiveData对象。

    注意:ViewModel不要引用view,lifecycle,或者任何持有activity context的对象,否则会导致内存泄漏。

    AndroidViewModel

    如果ViewModel需要Application context(例如,查找系统服务),则可以扩展AndroidViewModel类。

    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的生命周期

    l ViewModel是不能跨activity的,是和当前activity关联的,不过可以在此activity内的fragment之间共享。

    l 如果想跨activity,则可以用AndroidViewModel。

    ViewModel保留在内存中,直到其生命周期范围永久消失:

    l 对于activity而言,它finish时,

    l 对于fragment而言,当它detached时。

    图1说明了activity经历屏幕旋转,重建,然后结束时的各种生命周期状态。

    该图还在关联的activity生命周期旁显示了ViewModel的生命周期。 相同的基本状态适用于Fragment的生命周期。

    wps61

    当Activity不是由于系统原因,即用户明确finish时,系统将调用ViewModel对象的onCleared()方法,以便清理资源。这个在后边会进行一些源码分析。

    在fragment之间共享数据

    ViewModel是和当前activity关联的,是不能跨activity的。

    一个activity中的两个或更多fragment需要相互通信是很常见的。 想象一下主从fragment的一种常见情况,您有一个fragment,用户在其中从列表中选择一个item,另一个fragment显示了所选item的内容。 处理这种情况绝非易事,因为两个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 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.
            });
        }
    }

    注意,两个Fragment都使通过获取activity来获得ViewModelProvider时,它们将收到相同的SharedViewModel实例,该实例的作用范围是此activity。

    这种方法提供了下边的优点:

    l 该activity不需要执行任何操作,也无需了解此通信。

    l 除SharedViewModel外,Fragment不需要彼此了解。 如果其中一个Fragment消失了,则另一个继续照常工作。

    l 每个Fragment都有自己的生命周期,并且不受另一个Fragment的生命周期影响。 如果一个Fragment替换了另一个Fragment,则UI可以继续工作而不会出现任何问题。

    全局范围的ViewModel

    在Application子类中创建一个ViewModelStore对象,然后用ViewModelProvider的

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) (Application需要implements ViewModelStoreOwner)
    或者
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory)

    创建一个ViewModelProvider,

    然后通过ViewModelProvider.get来获取对应的全局ViewModel。

    ViewModel替换loader

    诸如CursorLoader之类的Loader类经常用于使应用程序UI中的数据与数据库保持同步。 您可以使用ViewModel和其他一些类来替换Loader。 使用ViewModel可将UI控制器与数据加载操作分开,这意味着类之间的强引用更少。

    在使用Loader的一种常见方法中,应用程序可能使用CursorLoader来观察数据库的内容。 当数据库中的值更改时,Loader会自动触发数据的重新加载并更新UI:

    wps62

    ViewModel与Room和LiveData一起使用以替换Loader。 ViewModel确保数据在设备配置更改后仍然存在。 当数据库更改时,Room会通知您的LiveData,然后LiveData会使用修改后的数据更新UI。

    wps63

    源码分析

    MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);

    ViewModel

    是一个abstract类,其中只有一个方法:

    protected void onCleared() {
    }

    当不再使用这个ViewModel并将其销毁时,将调用此方法。

    当ViewModel观察到一些数据时,您需要清除这个订阅来防止这个ViewModel的泄漏,这是很有用的。

    activity和fragment中会在销毁时自动调用关联的ViewModel的onCleared方法。

    AndroidViewModel

    继承自ViewModel,

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

    ViewModelProviders

    //获取application
    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;
    }
    
    //获取fragment关联的activity
    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;
    }
    
    /**
     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
     * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
     * <p>
     * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
     *
     * @param fragment a fragment, in whose scope ViewModels should be retained
     * @return a ViewModelProvider instance
     */
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, 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 {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
     *
     * @param activity an activity, in whose scope ViewModels should be retained
     * @return a ViewModelProvider instance
     */
    @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
     * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
     * <p>
     * It uses the given {@link Factory} to instantiate new ViewModels.
     *
     * @param fragment a fragment, 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 Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
    
    /**
     * 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);
    }

    l 其中的Factory是用来创建ViewModel的工厂类,在ViewModelProvider中定义的。

    l 可以看到如果没有传递Factory,那么就会使用ViewModelProvider.AndroidViewModelFactory。

    l 可以看到ViewModelProvider在创建时除了需要传递Factory,还有一个ViewModelStore,它就是用来管理activity重建时能够获取相同实例ViewModel的,而ViewModelStore在fragment和fragmentActivity中都会创建。

    ViewModelProvider

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
    
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
    
    private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
    
    @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.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
    • l public <T extends ViewModel> T get(@NonNull Class<T> modelClass)
    • l public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass)

    modelClass:就是ViewModel的class。

    key:要获取的ViewModel实例对应的key,如果没有传递则会使用modelClass.getCanonicalName()来当key,key相同则获取的ViewModel的实例就相同。

    返回一个现有的ViewModel或在范围内创建一个新的ViewModel(通常是一个Fragment或一个activity),与这个ViewModelProvider相关联。

    创建的ViewModel与给定的范围相关联,并且只要该范围是活动的,它就会被保留(例如,如果它是一个activity,直到它finish或者进程被终止)。

    HasDefaultViewModelProviderFactory

    public interface HasDefaultViewModelProviderFactory {
        /**
         * Returns the default {@link ViewModelProvider.Factory} that should be
         * used when no custom {@code Factory} is provided to the
         * {@link ViewModelProvider} constructors.
         *
         * @return a {@code ViewModelProvider.Factory}
         */
        @NonNull
        ViewModelProvider.Factory getDefaultViewModelProviderFactory();
    }

    Fragment和ComponentActivity都实现了此接口,

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner)用到了

    ViewModelStoreOwner

    public interface ViewModelStoreOwner {
        /**
         * Returns owned {@link ViewModelStore}
         *
         * @return a {@code ViewModelStore}
         */
        @NonNull
        ViewModelStore getViewModelStore();
    }

    Fragment和ComponentActivity都实现了此接口,

    另外在

    l ViewModelProviders.of(this).get(MyViewModel.class);

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

    都用到了

    Factory

    内部有一些Factory,就是创建ViewModel的工厂类

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

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

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

    ViewModelStore

    来存储ViewModels的Class。

    • l 必须通过配置更改来保留ViewModelStore的一个实例:如果这个ViewModelStore的所有者由于配置更改而被销毁并重新创建,那么所有者的新实例应该仍然拥有ViewModelStore的相同旧实例。
    • l 如果这个ViewModelStore的所有者被销毁,并且不打算重新创建,那么它应该在这个ViewModelStore上调用clear(),以便通知ViewModels它们不再被使用。
    • l 也就是说ViewModel能够在configuration change之间也能保留的原因就是在configuration change之间保留了ViewModelStore。

    使用ViewModelStoreOwner.getViewModelStore()来检索activity和fragment的ViewModelStore。

    每个FragmentActivity和Fragment都在内存创建了自己的ViewModelStore,并且在configuration change时保留。

    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);
        }
    
        Set<String> keys() {
            return new HashSet<>(mMap.keySet());
        }
    
        /**
         *  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();
        }
    }
  • 相关阅读:
    中文词频统计
    复合数据类型,英文词频统计
    Mybatis 异常:Cause: java.io.IOException: Could not find resource com.xxx.xxx.xml
    Ajax:修改了项目的ajax相关代码,点击运行没有效果
    大数据应用期末总评
    分布式并行计算MapReduce
    分布式文件系统HDFS 练习
    安装关系型数据库MySQL和大数据处理框架Hadoop
    爬虫综合大作业
    爬取全部的校园新闻
  • 原文地址:https://www.cnblogs.com/muouren/p/12368283.html
Copyright © 2020-2023  润新知