• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Android-Jetpack架构组件三之ViewModel

android 搞代码 3年前 (2022-03-02) 37次浏览 已收录 0个评论
文章目录[隐藏]

ViewModel简介

在晚期的Android开发中,因为利用绝对较小,页面绝对简略,咱们会将数据申请、页面UI解决和数据加载全副放在Activity或Fragment中进行,然而随着我的项目的迭代,这种开发方式显得越来越臃肿,并且也不易于我的项目的保护和扩大。

此时,借鉴后端的后端程序的开发思路,咱们对Android我的项目进行了分层,典型的有MVC,MVP和MVVM等我的项目分层,而后每层负责本人的事件即可。以当初风行的MVVM模式为例。

  • Model层:数据层,次要负责数据实体和对数据实体的操作。
  • View层:视图层,对应Android的Activity、Fragment和View等,负责数据的显示以及与用户的交互。
  • ViewModel层:关联层,用于将Model和View进行绑定,当Model产生更改时,即时告诉View进行刷新,当然,也能够反向告诉。

在JetPack架构中,ViewModel组件是一个能够感知生命周期的模式来存储和治理视图相干的数据的组件,因而它适宜以下场景。

  • 适宜须要保留大量数据的场景。例如,对于须要保留小量数据的场景,咱们能够应用Activity/ Fragment的onSaveInstanceState办法保留数据,而后在onCreate办法中利用onRestoreInstanceState进行还原。然而,onSaveInstanceState只适宜用来存储数据量少且序列化或者反序列化不简单的数据,如果被序列化的对象简单的话,序列化会耗费大量的内存,进而造成丢帧和视觉卡顿等问题。而ViewModel不仅反对数据量大的状况,还不须要序列化、反序列化操作。
  • 在Android中,Activity/Fragment次要用于显示视图数据,如果它们也负责数据库或者网络加载数据等操作,那么势必造成代码臃肿,而将逻辑代码放到ViewModel之后,能够更无效的将视图数据相干逻辑和视图控制器拆散开来。

除此之外,ViewModel的益处还有很多,然而最终的目标就是为了让代码可维护性更高,升高代码的冗余水平。

生命周期

咱们晓得,Android的Activity/Fragment是有生命周期的,咱们能够在不同的生命周期函数中执行不同的操作来达到不同的目标。因为ViewModel是保留在内存中的,所以ViewModel的生命周期并不会随Activity/Fragment的生命周期发生变化 。

下图是官网给出的ViewModel与Activity的生命周期的对应关系示意图。

从上图能够看出,ViewModel会随同着Activity/Fragment的整个生命周期,直到ViewModel绑定的Activity/Fragment执行onDestroy()办法之后才会被销毁。

根本应用

1,增加gradle以来,如下所示。

<code class="cpp">dependencies {
   implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
}

2,创立一个继承自ViewModel类的MyViewModel类,创立ViewModel类千万不能持有Context的援用,否则会引起内存透露,如果须要应用Context能够继承AndroidViewModel。

<code class="cpp">    public class MyViewModel extends ViewModel {
    private MutableLiveData<String> user;
    public LiveData<String> getUsers() {
        if (user == null) {
            user = new MutableLiveData<String>();
            loadUsers();
        }
        return user;
    }

    private void loadUsers() {
        user.setValue("Android利用开发实战");
    }
}

3, 为了防止内存透露,咱们能够在onCleared()办法中进行资源开释操作。而后,咱们在Activity中就能够应用MyViewModel,如下所示。

<code class="cpp">public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                Log.d(TAG, "LiveData监听数据返回:"+s);
            }
        });
    }
}

在 Fragment 之间共享数据

<code class="cpp">    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 onViewCreated(@NonNull View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }

    public class DetailFragment extends Fragment {

        public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
            model.getSelected().observe(getViewLifecycleOwner(), { item ->
               // Update the UI.
            });
        }
    }
    

源码剖析

ViewModel源码

ViewModel类是一个形象接口,其局部源码如下。

<code class="cpp">public abstract class ViewModel {
    
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    private volatile boolean mCleared = false;

    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

   
    @SuppressWarnings("unchecked")
    <T> T setTagIfAbsent(String key, T newValue) {
        T previous;
        synchronized (mBagOfTags) {
            previous = (T) mBagOfTags.get(key);
            if (previous == null) {
                mBagOfTags.put(key, newValue);
            }
        }
        T result = previous == null ? newValue : previous;
        if (mCleared) {
            closeWithRuntimeException(result);
        }
        return result;
    }

    
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    <T> T getTag(String key) {
        if (mBagOfTags == null) {
            return null;
        }
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }

    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

能够发现,ViewModel抽象类的次要作用就是应用HashMap存储数据。ViewModel 有一个子类AndroidViewModel,它的源码如下。

<code class="cpp">public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

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

    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

与继承ViewModel不同,AndroidViewModel须要提供一个 Application 的 Context。

ViewModelProvider

在后面的示例代码中,咱们在Activity中应用ViewModelProviders.of办法来获取ViewModel实例,如下所示。

<code class="cpp">MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);

关上ViewModelProviders类的源码,能够发现ViewModelProviders一共有四个构造方法,都是用来创立ViewModelProvider对象,只不过参数不同而已。

<code class="cpp">public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }
    
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    
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);
    }
    
 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);
    }

在构建ViewModelProvider的时候须要用到ViewModelStore和Factory,上面咱们来别离介绍一下它们。

ViewModelStore

ViewModelStore次要作用是存储ViewModel的容器,当咱们关上ViewModelStore的源码时会发现ViewModelStore是通过HashMap来存储ViewModel的数据的。并且,ViewModelStore还提供了一个clear办法,用来清空Map汇合外面的ViewModel,咱们能够在Activity/Fragment的onDestroy办法执行clear办法执行ViewModel数据的革除。

<code class="cpp"> protected void onDestroy() {
        super.onDestroy();
        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
    }

Factory

当咱们应用ViewModelProvider获取ViewModel实例时,ViewModelProvider一共提供了4个构造函数,另一个比拟重要的构造函数是

<code class="cpp">public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory{
        mFactory = factory;
        mViewModelStore = store;
    }

ViewModelProvider的第二个参数是factory,它的子类有NewInstanceFactory和AndroidViewModelFactory两个,咱们能够应用ViewModelProvider.AndroidViewModelFactory.getInstance获取单例的Factory对象,NewInstanceFactory源码如下。

<code class="cpp">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的源代码如下。

<code class="cpp">public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

       
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

       
        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,能够援用Context,其实是Appplication实例。在下面的代码中,如果是有application参数,则通过newInstance(application)实例化,否则调用父类的create办法而后通过newInstance()实例化。如果通过newInstance(application)实例化,就能够在ViewModel外面拿到Context,因为Application是APP全局的生命周期最长,所以就不存在内存泄露问题。

ViewModel是如何实现状态保留的

后面说过,ViewModel是不会随着Activity/Fragment的销毁而销毁的,因为ViewModel是将数据应用ViewModelStore 保留在HashMap 中,所以只有ViewModelStore不被销毁,则ViewModel的数据就不会被销毁。

家喻户晓,Android在横竖屏切换时会触发onSaveInstanceState,而后在还原时则会触发onRestoreInstanceState。除此之外,Android的Activity类还提供了onRetainNonConfigurationInstance和getLastNonConfigurationInstance两个办法,当设施状态产生扭转时,下面的两个办法就会被零碎调用。

其中,onRetainNonConfigurationInstance 办法用于解决配置产生扭转时数据的保留,而getLastNonConfigurationInstance则用于复原创立Activity时获取上次保留的数据。首先,咱们来看一下onRetainNonConfigurationInstance办法,如下所示。

<code class="cpp">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;
    }

能够发现,ViewModel会将数据存储在 NonConfigurationInstances 对象中,而NonConfigurationInstances是定义在Activity外面的一个类,如下所示。

<code class="cpp">static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }

再来看一下getLastCustomNonConfigurationInstance办法,
getLastNonConfigurationInstance办法返回的数据就是NonConfigurationInstances.activity属性,如下所示。

<code class="cpp">@Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

当初,咱们再看一下ComponentActivity 的 getViewModelStore办法,如下所示。

<code class="cpp">@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;
}

能够发现,在getViewModelStore办法中咱们首先会获取NonConfigurationInstances对象,不为空则从其身上拿到ViewModelStore,也就是之前保留的ViewModelStore,而后当Activity被再次创立的时候复原数据。

须要阐明的是,onRetainNonConfigurationInstance办法会在onSaveInstanceState办法之后被调用,即调用程序同样在onStop办法和 onDestroy办法之间。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android-Jetpack架构组件三之ViewModel

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址