ViewModel
Activity的生命周期
先说一下
作用 | |
---|---|
OnCreate() | 是Activity被创建,布局文件加载与事件的绑定 |
OnStart() | Activity由不可见变为可见,还不能进行交互,数据初始化 |
OnResume() | 用户可以与Activity进行交互 |
OnPause() | Activity停止,从前端返回后台,停止数据和动画存储,只有第一个活动的OnPause()执行完后,第二个活动的OnResume()才会执行…这个方法在系统准备去启动或者恢复另一个活动的时候调用 |
OnStop() | 活动完全不可见,但可以进行一些轻量级的回收工作,但是如果你打开的新的界面是透明界面,第一个活动还是可以看见,所以第一个活动的OnStop()是不会进行 |
OnDestroy() | 活动彻底结束 |
OnRestart() | 活动重新启动 |
一般情况,A/B 均不是透明页面:
A 跳转 B 页面会经历的生命周期:A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume() -> A.onStop。
从 B 页面返回 A 页面经历的生命周期:B.onPause() -> A.onRestart() -> A.onStart() -> A.onResume() -> B.onStop()。
B是透明页面的情况:
如果 B 是透明的,A 跳转到 B:A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume()。
从 B 返回 A:B.onPause() -> A.onResume() -> B.onPause()。
事件的绑定的代码就是这块
setContentView(R.layout.activity_main);
起始 | 终止 | |
---|---|---|
完整生存期 | OnCreate() | OnDestory() |
可见生存期 | OnCreate() | OnStop() |
前台生存期 | OnCreate() | OnResume() |
(1):当你进行屏幕旋转的时候,Activity的生命周期是这样的
进行屏幕旋转的时候会让你的Activity重新启动一遍,数据不会保留,我自己仿写的知乎就出现了这个问题,一旦进行旋转,就自己退出app了
而如果你使用了ViewModel的话,它的生命周期就是这样的
左边的Activity的生命周期变了这么多,但是用了ViewModel后就是右边的效果了
所以就可以明白ViewModel的作用了
1.ViewModel的作用
(1)进行手机的旋转的时候,数据不会丢失.
(2)由第1个功能就可以推出第二个功能了,它可以帮助Activity分担一部分的工作,可以用它来存放与界面有关的数据
2.ViewModel的基本用法
《第一行代码第三版》书中,用ViewModel之前先在app/build.gradle文件中添加依赖
implementation"androidx.lifecycle:lifecycle-extensions:2.2.0"
但是我用的时候发现好像不写这个依赖也可以
要实现的是一个计数器功能,每次按一下按钮,计数器的界面的数字就会+1,我要做的就是当它向左摇动的时候仍然可以记录下原来的数据
先给你看不用ViewModel的效果
进行旋转后
原本的5没了
使用ViewModel
我是给MainActivity创建一个对应的ViewModel
先创建一个类来继承ViewModel
Class ViewModel extends ViewModel{
int count = 0;
}
在MainActivity中添加布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/text_0"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button_0"/>
</LinearLayout>
然后MainActivity中写
public class MainActivity extends AppCompatActivity {
ViewModel viewmodel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
TextView textView = findViewById(R.id.text);
textView.setText(String.valueOf(viewmodel.count));
viewmodel = new ViewModelProvider(this).get(ViewModel.class);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewmodel.score++;
textView.setText(String.valueOf(viewmodel.score));
}
});
}
}
这时候我们看一下效果
成功实现效果
这时候我们来分析一下思路,我们是先创建了一个新的类来继承ViewModel,把相应的值都先附上,
之后在MainActivity中开始给在这个类中的对象初始化了
ViewModel viewmodel;
viewmodel = new ViewModelProvider(this).get(ViewModel.class);
先调用ViewModelProvider()的get()方法来获取ViewModel的实例
之所以这么写,是因为
ViewModel有独立的生命周期,并且生命周期要长于Activity,如果我们在onCreate()方法中创建ViewModel的实例,那么每次onCreate()方法创建时,ViewModel都会创建一个新的实例,这样当手机屏幕旋转的时候,就无法保留数据了
后面的代码都超级好理解
向ViewModel传递参数:
我们打开我们刚才写的小demo
按按钮,把textview给它加到4
这时候我们把应用从后台关掉,再打开demo
发现textview变成0了,当从后台把demo关掉的时候,它的生命周期就已经结束了,重新打开的时候会进行新的生命周期
有什么办法可以让我们关掉后台的demo后,它依然有原先的数据呢
这时候就得说一下向ViewModel传递参数
-
将原本ViewModel类中的无参构造改成有参构造
public class MainViewModel extends ViewModel { int count = 0; public MainViewModel(int count) { this.count = count; } }
2.创建一个工厂类,实现ViewModelProvider.Factory接口:
public class MainViewModelFactory implements ViewModelProvider.Factory {
public MainViewModelFactory(int counter) {
this.counter = counter;
}
public int counter = 0;
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MainViewModel(counter);
}
}
这里我们重写了create方法,并且工厂类的构造方法中也有ViewModel类中有参构造的参数,主要是因为我们需要借助工厂类来构造ViewModel的具体实例,工厂类会自动在合适的时机创建ViewModel的实例。
之后在MainActivity中修改
public class MainActivity extends AppCompatActivity {
MainViewModel viewmodel;
SharedPreferences mPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
int savedCounter = mPreferences.getInt("saved_data",0);
Button button = findViewById(R.id.button);
TextView textView = findViewById(R.id.text);
viewmodel = new ViewModelProvider(this,new MainViewModelFactory(savedCounter)).get(MainViewModel.class);
textView.setText(String.valueOf(viewmodel.count));
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewmodel.count++;
textView.setText(String.valueOf(viewmodel.count));
}
});
}
@Override
protected void onPause() {
super.onPause();
SharedPreferences.Editor editor = mPreferences.edit();
editor.putInt("saved_data",viewmodel.count);
editor.apply();
}
}
按按钮把它加到7
把后台关掉之后再打开
还是原来的数据
其实这个只有2个地方需要注意
-
@Override protected void onPause() { super.onPause(); SharedPreferences.Editor editor = mPreferences.edit(); editor.putInt("saved_data",viewmodel.count); editor.apply(); }
这块的时候,当退出该demo时,用SharedPreferences来存储数据,将Viewmodel.count存放在saved_data中
重新启动时
mPreferences = PreferenceManager.getDefaultSharedPreferences(this); int savedCounter = mPreferences.getInt("saved_data",0);
把该数据从saved_data中拿出来
viewmodel = new ViewModelProvider(this,new MainViewModelFactory(savedCounter)).get(MainViewModel.class);
这块和刚才那块比要多一个
new MainViewModelFactory(savedCounter)
不能忘记