栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

jetpack之ViewModel

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

jetpack之ViewModel

ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

摘自官方文档

Android 框架可以管理界面控制器(如 Activity 和 Fragment)的生命周期。Android 框架可能会决定销毁或重新创建界面控制器,以响应完全不受您控制的某些用户操作或设备事件。

如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。因配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState()方法中保存从 onCreate()中恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

另一个问题是,界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。 Activity 和 Fragment 太多的逻辑控制代码降低了代码阅读性和可维护性。

从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。

ViewModel 的生命周期

ViewModel对象存在的时间范围是获取 ViewModel时传递给 ViewModelProvider的Lifecycle。ViewModel将一直留在内存中,直到限定其存在时间范围的 Lifecycle永久消失:对于 activity,是在 activity finished的时候;而对于 fragment,是在 fragment detach时候

我们通常在Activity 的onCreate()方法中创建ViewModel(避免重复创建).系统可能会在Activity的整个生命周期内多次调用onCreate()方法,例如旋转手机屏幕的时候。ViewModel存在的时间范围是从首次创建ViewModel直到activity finished 并销毁

ViewModel的创建

ViewModel使用很简单,我们只需要继承ViewModel类就行了

class ViewModelActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityViewModelBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val model  = ViewModelProvider(this).get(MyViewModel::class.java)
        model.name.observe(this){
            //update UI 的操作
        }
    }
}

在Activity中,我们可以使用ViewModelProvider来得到 ViewMode的实例。

如果在ViewModel中,我们需要使用到上下文Context对象(toast 或者获取系统服务等等),我们可以继承AndroidViewModel来构建ViewModel

class AndroidModel(app:Application): AndroidViewModel(app) {
    
}

此时这个AndroidModel的创建和上面的类似,也是用ViewModelProvider来获得。

ViewModel常常结合LiveData使用,然后在我们的Activity或者Fragemnt中去监听LiveData的改变

然后去在Activity中做UI的更新逻辑,例如,我们需要根据网络请求来决定是否弹出一个DialogFragment,我们的网络请求放在ViewModel中,DialogFragment必须在Activity或者Fragment中弹出来(因为DialogFragment的弹出不能使用Application作为Context),所以此时我们必须使用LiveData。当网络请求完成之后,我们改变LiveData的值,并且在Activity或者Fragment监听LiveData的变化,然后作出弹出DailogFragment的操作

在Fragment之间共享数据

现在的App中使用Fragment是很常见的,之前我们从Activity中向Fragment中传递数据,我们使用Bundle来传递(在创建Fragment的时候),但是在Activityt中如果需要动态传递(随时传递)数据给Fragment,我们平常的做法可能是在Activity中持有Fragment的应用,然后在Activity中去调用对于Fragment的某些方法传递数据,或者利用通知系统(EventBus),但是我们如果在BFragment中促使Activity中的数据改变,要通知到CFragment的Ui修改的话,目前的场景只能使用EventBus。当然也可以使用我们这里的ViewModel了

class ShareViewModel : ViewModel() {
    val selected = MutableLiveData()

    fun select(item: Item) {
        selected.value = item
    }
}

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: ShareViewModel?=null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
        itemSelector.setonClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: ShareViewModel?=null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
        model.selected.observe(viewLifecycleOwner, Observer { item ->
            // Update the UI
        })
    }
}

在上面的两个Fragment 在获取ViewModel的时候 传递的都是requireActivity(),那么获取到的ViewModel的实例其实就是同一个SharedViewModel,所以当Activity 或者任何一个Fragment中 改变了SharedViewModel中LiveData的数据,都会及时的通知到。这种方法有以下好处:

Activity 不需要执行任何操作,也不需要对此通信有任何了解。

除了 SharedViewModel之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。

每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。 ViewModel的源码解析

ViewModel的代码很简单

这里基本只需要知道onCleared()方法就行了,自定义ViewModel并重写这个方法,讲释放资源的逻辑放在这个方法中就行

ViewModelProvider类

获取ViewModel的实例时,我们是使用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;
}

创建ViewModelProvider的时候需要传递一个ViewModelStore 和一个Factory,而我们构建的时候只传递了一个this(Activity),其实就是一个ViewModelStoreOwner,

AppCompatActivity -->FragmentActivity -->ComponentActivity --> ViewModelStoreOwner

有上面这样一个继承实现关系,我们的AppCompatActivity其实可以说是实现了ViewModelStoreOwner的,最终返回的是ComponentActivity中的mViewModelStore 关于Factory后面再讲

@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.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

@SuppressWarnings("WeakerAccess") 
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

这里就是获取ViewModelStore的方法,可以看到在ensureViewModelStore方法中,我们会首先判断mViewModelStore 是否为null,然后通过getLastNonConfigurationInstance() 得到一个NonConfigurationInstances 实例,这里其实就是当Activity旋转的时候ViewModel中的数据还会存在的奥秘,通过nc 可以获取重建之前的mViewModelStore,然后从ViewModelStore里面根据类名获取ViewModel的实例,所以获取到的ViewModel在旋转前后其实是同一个实例,在我们的App系统configuration发生改变的时候 就会回调onRetainNonConfigurationInstance()这个方法

@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

//Activity中 的方法
@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

这里就是将ViewModelStore进行保存。 这里getLastNonConfigurationInstance 方法 最后其实是返回Activity中的mLastNonConfigurationInstances 变量的activity对象,我们看看这mLastNonConfigurationInstances 在哪里赋值,我们知道,我们的Activity的启动其实最终都会走到ActivityThread类中,当我们启动一个Activity的时候会执行其中的 performLaunchActivity 方法最终会调用到Activity的attach方法,lastNonConfigurationInstances是存在 ActivityClientRecord中的一个组件信息 

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    Class activityClass = null;
    if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
    if (r != null) {
        activityClass = r.activity.getClass();
        r.activity.mConfigChangeFlags |= configChanges;
        if (finishing) {
            r.activity.mFinished = true;
        }

        performPauseActivityIfNeeded(r, "destroy");

        if (!r.stopped) {
            callActivityonStop(r, false , "destroy");
        }
        if (getNonConfigInstance) {
            try {
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to retain activity "
                            + r.intent.getComponent().toShortString()
                            + ": " + e.toString(), e);
                }
            }
        }
        try {
            r.activity.mCalled = false;
            mInstrumentation.callActivityonDestroy(r.activity);
            if (!r.activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + safeToComponentShortString(r.intent) +
                    " did not call through to super.onDestroy()");
            }
            if (r.window != null) {
                r.window.closeAllPanels();
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                        "Unable to destroy activity " + safeToComponentShortString(r.intent)
                        + ": " + e.toString(), e);
            }
        }
        r.setState(ON_DESTROY);
    }
    schedulePurgeIdler();
    // updatePendingActivityConfiguration() reads from mActivities to update
    // ActivityClientRecord which runs in a different thread. Protect modifications to
    // mActivities to avoid race.
    synchronized (mResourcesManager) {
        mActivities.remove(token);
    }
    StrictMode.decrementExpectedActivityCount(activityClass);
    return r;
}

在屏幕旋转造成的的Activity重建的时候 就会给lastNonConfigurationInstances 这个变量赋值,这样就能够在Activity重建的时候 获取到之前的ViewModel了。而我们的ViewModel中onClear方法什么时候执行呢,在我们的ComponetActivity构方法中

public ComponentActivity() {
    // ......省略部分代码
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                // Clear out the available context
                mContextAwareHelper.clearAvailableContext();
                // And clear the ViewModelStore
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });
    }

上面注册了一个Lifecycle的监听,在我们的Activity在onDestory之后 并且isChangingConfigurations()为false的时候,才会去执行getViewModelStore().clear(); 的操作间接调用到ViewModel的onCleared()方法

get()获取ViewModel

在这里通过get方法创建ViewModel 传入的参数是 所需要创建的ViewModel的Class对象

public  T get(@NonNull Class 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);
}


@SuppressWarnings("unchecked")
@NonNull
@MainThread
public  T get(@NonNull String key, @NonNull Class modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        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);
    return (T) viewModel;
}

最终通过下面的get方法获取ViewModel,当我们从ViewModelStore 根据key值去获取ViewModel为null的时候,如果为null 就是用Factory进行创建。所以后面我们也可以定义自己的Factory 来创建ViewModel

ViewModelStore类
public class ViewModelStore {

    private final HashMap 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 keys() {
        return new HashSet<>(mMap.keySet());
    }

    
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

这里其实就是一个 HashMap存放了ViewModel,主要是ViewModel的存取

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/736312.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号