zoukankan      html  css  js  c++  java
  • 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();
        }
    }
  • 相关阅读:
    Analysis Services features supported by SQL Server editions
    Azure DevOps to Azure AppServices
    Power BI For Competition
    Win10开机“提示语音”以及”随机播放音乐”
    Azure DevOps
    Allow Only Ajax Requests For An Action In ASP.NET Core
    Mobile CI/CD 101
    Configure SSL for SharePoint 2013
    AWS Step Function Serverless Applications
    Cordova Upload Images using File Transfer Plugin and .Net core WebAPI
  • 原文地址:https://www.cnblogs.com/muouren/p/12368283.html
Copyright © 2011-2022 走看看