Jetpack之ViewModel

news2024/12/27 16:09:35

The ViewModel class is a business logic or screen level state holder.

上面是官方给的定义,ViewModel 类是业务逻辑屏幕级状态持有者。

一、业务逻辑持有者

在此之前,无论是MVC模式,还是MVP模式,在视图层,都会产生对业务逻辑层的依赖,从而导致出现内存泄漏的情况。那ViewModel又是如何处理业务逻辑层和视图层的依赖,从而避免了常见的内存泄漏的问题的呢?
在这里插入图片描述

依赖Lifecycle对宿主生命周期的感知,处理对Viewmodel的依赖

1.1、应用

NormalViewModel

package com.anniljing.viewmodelcorestudy;

import android.app.Application;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;

public class NormalViewModel extends AndroidViewModel {
    public NormalViewModel(@NonNull Application application) {
        super(application);
    }

    public List<Integer> getdatas() {
        List<Integer> data = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            data.add(i);
        }
        return data;
    }
}

NormalActivity

package com.anniljing.viewmodelcorestudy;

import android.os.Bundle;

import com.anniljing.viewmodelcorestudy.databinding.ActivityNormalBinding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class NormalActivity extends AppCompatActivity {
    private ActivityNormalBinding mBinding;
    private NormalViewModel mViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = ActivityNormalBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());
        mViewModel = new ViewModelProvider(this).get(NormalViewModel.class);
    }
}

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

此处在Activity中Lifecycle的ON_DESTROY事件中,清除了ViewModel。

1.2、源码解析

在这里插入图片描述

1.2.1 创建ViewModelStore

public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        //noinspection ConstantConditions
        if (lifecycle == null) {
            throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
                    + "constructor. Please make sure you are lazily constructing your Lifecycle "
                    + "in the first call to getLifecycle() rather than relying on field "
                    + "initialization.");
        }
        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();
                    }
                }
            }
        });
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                ensureViewModelStore();
                getLifecycle().removeObserver(this);
            }
        });
    }

在ComponentActivity的构造方法中,注册了多个Lifecycle的观察者,其中有两个涉及ViewModelStore的,一个是收到宿主的生命周期事件后,就创建了ViewModelStore,随后就移除了该观察者;另一个则是监听到宿主的ON_DESTROY事件后,清空ViewModelStore的ViewModel

1.2.2、创建Factory

1.2.2.1、创建ViewModel的时候,第一步我们创建了ViewModelProvider,调用了其中一个构造方法

 public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

1.2.2.2、ViewModelProvider的构造方法中,调用了默认的Factory

public companion object {
            internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
                if (owner is HasDefaultViewModelProviderFactory)
                    owner.defaultViewModelProviderFactory else instance

            internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
        }

1.2.2.3、由于ComponentActivity实现了HasDefaultViewModelProviderFactory接口,所以Factory的实际对象是SavedStateViewModelFactory

  @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

1.2.3、创建ViewModel

public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key
        // AGP has some desugaring issues associated with compileOnly dependencies so we need to
        // fall back to the other create method to keep from crashing.
        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

应用层通过ViewModelProvider的get方法创建ViewModel,并且保存到ViewModelStore中。

二、屏幕状态的持有者

该功能主要处理当设备配置发生变化时,界面会重新绘制,导致数据丢失的问题。
在这里插入图片描述
屏幕发生旋转的时候,会调用onDestroy方法,界面会重新绘制。

2.1、ViewModel之前

2.1.1、manifest中配置

在这里插入图片描述
在activity里面的configChanges属性里面配置orientation|screenSize,则屏幕旋转的时候,界面就不会重新绘制。

2.1.2、onSaveInstanceState和onRestoreInstanceState

如果manifest里面没有配置configChanges,则屏幕旋转的时候,在onDestroy之前会调用onSaveInstanceState,在恢复界面的时候,会调用onRestoreInstanceState,这样我们就可以在以上两个方法中做保存和恢复数据的逻辑。

package com.anniljing.viewmodelcorestudy;

import android.os.Bundle;
import android.util.Log;

import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

public class SaveStateActivity extends AppCompatActivity {
    private static final String TAG = "SaveStateActivity";
    private ActivitySaveStateBinding mBinding;
    private NormalState mNormalState;
    private static final String KEY="NormalState";
    private String saveString="";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG, "onCreate");
        if (savedInstanceState !=null){
            saveString=savedInstanceState.getString(KEY,"");
        }
        mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());
        mNormalState = new NormalState();
        mNormalState.setString(saveString);
        Log.e(TAG, mNormalState.getString());
        mBinding.tvState.setOnClickListener((view) -> {
            mNormalState.setString("NormalState");
            Log.e(TAG,"Click after:"+ mNormalState.getString());
        });
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e(TAG, "onSaveInstanceState");
        outState.putString(KEY,"NormalState");
    }

    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.e(TAG, "onRestoreInstanceState");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }
}

保存数据:
在这里插入图片描述
重新绘制的时候,取出数据:
在这里插入图片描述
在这里插入图片描述
在onSaveInstanceState里面增加保存逻辑,在onCreate方法里面就可以恢复以前的数据了。

2.2、 使用ViewModel

ViewModelSaveState

package com.anniljing.viewmodelcorestudy;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;

public class ViewModelSaveState extends AndroidViewModel {
    private String mString;

    public ViewModelSaveState(@NonNull Application application) {
        super(application);
    }

    public String getString() {
        return mString;
    }

    public void setString(String string) {
        mString = string;
    }
}

package com.anniljing.viewmodelcorestudy;

import android.os.Bundle;
import android.util.Log;

import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class SaveStateActivity extends AppCompatActivity {
    private static final String TAG = "SaveStateActivity";
    private ActivitySaveStateBinding mBinding;
    private ViewModelSaveState mViewModelSaveState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG, "onCreate");
        mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());
        mViewModelSaveState = new ViewModelProvider(this).get(ViewModelSaveState.class);
        Log.e(TAG,"Before click:"+mViewModelSaveState.getString());
        mBinding.tvState.setOnClickListener((view) -> {
            mViewModelSaveState.setString("ViewModelState");
            Log.e(TAG, "Click after:" + mViewModelSaveState.getString());
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG,"onDestroy");
    }
}

在这里插入图片描述
可以看到,引入ViewModel以后,不需要在onSaveInstanceState方法中增加额外的保存逻辑,重绘后就可以恢复以前的数据。

无论是ViewModel,还是重写onSaveInstanceState方法,当内存有限导致界面退出重绘时,就无法恢复以前的数据了。
我们可以通过以下操作来模拟内存不足时,导致应用杀死的效果:
1、启用开发者模式
2、打开“不保留活动”
3、打开应用后,按home键,退出应用
4、重新打开应用。
在这里插入图片描述

2.3、使用SavedStateHandle

ViewModelWithHandleSaveState

package com.anniljing.viewmodelcorestudy;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;

public class ViewModelWithHandleSaveState extends ViewModel {
    private static final String KEY = "ViewModelWithHandleSaveState";
    private SavedStateHandle mStateHandle;

    public ViewModelWithHandleSaveState(SavedStateHandle stateHandle) {
        mStateHandle = stateHandle;
    }

    public MutableLiveData<String> getLiveData() {
        return mStateHandle.getLiveData(KEY);
    }
}

1、构造方法里面添加SavedStateHandle参数。
2、获取的时候通过mStateHandle。
SaveStateActivity

package com.anniljing.viewmodelcorestudy;

import android.os.Bundle;
import android.util.Log;

import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class SaveStateActivity extends AppCompatActivity {
    private static final String TAG = "SaveStateActivity";
    private ActivitySaveStateBinding mBinding;
    private ViewModelWithHandleSaveState mWithHandleSaveState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG, "onCreate");
        mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());
        mWithHandleSaveState = new ViewModelProvider(this).get(ViewModelWithHandleSaveState.class);
        Log.e(TAG, "Before click:" + mWithHandleSaveState.getLiveData().getValue());
        mWithHandleSaveState.getLiveData().observe(this, s -> {
            Log.e(TAG, "After click:" + mWithHandleSaveState.getLiveData().getValue());
        });
        mBinding.tvState.setOnClickListener((view) -> mWithHandleSaveState.getLiveData().postValue("ViewModelWithHandleSaveState"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简化客户服务操作的最佳方式:客户服务响应模板

关键词&#xff1a;客户服务响应模板&#xff1b;SaleSmartly&#xff08;ss客服&#xff09; 客户服务响应模板可以通过提供标准响应来帮助简化客户服务操作。这些客户服务模板可用于各种目的和方案&#xff0c;包括欢迎客户、回复查询、结束对话、请求评论等。请继续阅读&am…

2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(5)

目录 模块A 基础设施设置与安全加固 一、项目和任务描述&#xff1a; 二、服务器环境说明 三、具体任务&#xff08;每个任务得分以电子答题卡为准&#xff09; A-1任务一 登录安全加固&#xff08;Windows&#xff09; 1.密码策略 a.密码策略必须同时满足大小写字母、数…

【ICLR 2022】重新思考点云中的网络设计和局部几何:一个简单的残差MLP框架

文章目录RETHINKING NETWORK DESIGN AND LOCAL GEOMETRY IN POINT CLOUD: A SIMPLE RESIDUAL MLP FRAMEWORKPointMLP残差点模块几何仿射模块精简版模型&#xff1a;PointMLP-elite实验结果消融实验RETHINKING NETWORK DESIGN AND LOCAL GEOMETRY IN POINT CLOUD: A SIMPLE RESI…

电容的参数-详细描述

贴片电容 如同如所示&#xff0c;MLCC&#xff08;Multi-layer Ceramic Capacitors&#xff09;&#xff0c;外形很好区分。 实际内部结构 使用的还是平行板电容器原理&#xff0c;只是这个是叠层结构&#xff1b;电解电容是卷起来的圆柱状&#xff1b; 容值&#xff1a; …

ChatGPT风口下的中外“狂飙”,一文看懂微软、谷歌、百度、腾讯、华为、字节跳动们在做什么?

毫无疑问&#xff0c;ChatGPT正成为搅动市场情绪的buzzword。 历史经历过无线电&#xff0c;半导体&#xff0c;计算机&#xff0c;移动通讯&#xff0c;互联网&#xff0c;移动互联网&#xff0c;社交媒体&#xff0c;云计算等多个时代&#xff0c;产业界也一直在寻找Next Big…

ElementUI--Dialog 弹框的使用

第一步&#xff1a;从官方文档中拷贝一个对话框到你的页面中 <el-dialog title"为中华民族之崛起而学习" :visible.sync"dialogVisible" width"30%" :fullscreen"false" :close-on-press-escape"false" show-close:close…

大数据处理学习笔记1.4 掌握Scala运算符

文章目录零、本讲学习目标一、运算符等价于方法&#xff08;一&#xff09;运算符即方法&#xff08;二&#xff09;方法即运算符1、单参方法2、多参方法3、无参方法二、Scala运算符&#xff08;一&#xff09;运算符分类表&#xff08;二&#xff09;Scala与Java运算符比较三、…

买什么样的运动型蓝牙耳机好、运动型蓝牙耳机推荐

如今耳机是我们生活中很常见的数码产品了&#xff0c;在街上看到跑步、骑行&#xff0c;室内健身房&#xff0c;都能看到大家人手一副耳机&#xff0c;运动耳机已经成为很多人的运动必备品&#xff0c;因大众佩戴耳机的种类和风格有所不同&#xff0c;这也造就了市场上琳琅满目…

零基础 Ubuntu 20.04.01 下搭建51单片机开发环境[开源编译器SDCC]

原创首发于CSDN&#xff0c;转载请注明出处&#xff0c;谢谢&#xff01; 文章目录为何会在Linux下开发单片机个人系统环境与所用开发板安装开源编译器 sdccSTC MCU ISP 闪存工具 stcgal 的安装单片机代码的编译与测试&#xff5c;编写主代码 main.c&#xff5c;使用 sdcc 编译…

基于龙芯 2K1000 的嵌入式 Linux 系统移植和驱动程序设计

2.1 需求分析 本课题以龙芯 2K1000 处理器为嵌入式系统的处理器&#xff0c;需要实现一个完成的嵌 入式软件系统&#xff0c;系统能够正常启动并可以稳定运行嵌入式 Linux。设计网络设备驱 动&#xff0c;可以实现板卡与其他网络设备之间的网络连接和文件传输。设计 PCIE 设备驱…

新时代下的医疗行业新基建研讨会

1、会议纪要 2023年2月17日&#xff0c;HIT专家网进行了《新时代下的医疗行业新基建研讨会》的会议。 对会议内容进行了记录。 会议中有友谊医院、301、北肿主任进行了分享。大纲如下所示 2、本人所想 本人的所想所感&#xff1a; 1、301在多院区的医疗信息建设&#xff0c…

程序员遇到人生低谷期怎么做?

每个人的一生都是起起伏伏的&#xff0c;你不会天天高潮&#xff0c;总会经历一段又一段的不如意&#xff0c;你怎么把握这一段段时间&#xff0c;如何掌控人生节奏&#xff0c;都源于对人生低谷期的回答。 尤其是2022年&#xff0c;程序员受到的冲击并不小&#xff0c;从年初…

车辆调度混乱?看DHTMLX Gantt 如何提高企业调度管理效率

我们公司有一个车辆调度系统&#xff0c;目前能对小规模车队的进行简单管理。但是随着公司的业务越来越复杂&#xff0c;需要调度的车辆种类和人员安排越来越困难&#xff0c;经常出现因安排不当导致货物无车可送或车辆集中闲置情况发生&#xff0c;非常影响货运效率&#xff0…

谈谈接口和抽象类有什么区别?

第13讲 | 谈谈接口和抽象类有什么区别&#xff1f; Java 是非常典型的面向对象语言&#xff0c;曾经有一段时间&#xff0c;程序员整天把面向对象、设计模式挂在嘴边。虽然如今大家对这方面已经不再那么狂热&#xff0c;但是不可否认&#xff0c;掌握面向对象设计原则和技巧&am…

论文投稿指南——中文核心期刊推荐(法律)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

python小程序课程在线学习平台系统vue

可定制框架:ssm/Springboot/vue/python/PHP/小程序/安卓均可 目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.1 JAVA简介 4 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 2.5 SPRINGBOOT框架…

基于Spring Boot的校园志愿者服务网站

文章目录项目介绍主要功能截图&#xff1a;登录个人中心志愿者管理活动类型管理活动报名管理活动心得部分代码展示设计总结项目获取方式&#x1f345; 作者主页&#xff1a;Java韩立 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试…

vulnhub LordOfTheRoot_1.0.1

总结&#xff1a;端口敲门&#xff0c;CVE-2015-8660提权&#xff0c; 目录 下载地址 漏洞分析 信息收集 端口敲门 网站分析 方法一 ssh登录提权 方法二 下载地址 LordOfTheRoot_1.0.1.ova (Size: 1.6 GB)Download: http://www.mediafire.com/download/m5tbx0dua05szjm…

【项目精选】百货中心供应链管理系统(论文+源码+视频)

点击下载源码 随着国内物流行业的迅速发展&#xff0c;越来越多的企业认识到了“供应链”一词的含义以及它对整个企业物流活动的重大意义&#xff0c;于是&#xff0c;“供应链管理”也逐渐受到了大家的重视。供应链管理主要涉及到四个领域&#xff1a;供应、生产计划、物流、需…

#Paper Reading# Language Models are Few-Shot Learner

论文题目: Language Models are Few-Shot Learner 论文地址: https://proceedings.neurips.cc/paper/2020/hash/1457c0d6bfcb4967418bfb8ac142f64a-Abstract.html 论文发表于: NIPS 2020 论文所属单位: OpenAI 论文大体内容 本文主要提出了GPT-3&#xff08;Generative Pre-Tr…