Android-Jetpack

[Android开发]ViewModel中的LiveData和D

2020-10-03  本文已影响0人  沈枫_SerenF

标准的MVC模式中,各层职责:

上图没有展示Model层,ViewGroup也就是layout,通常是由各控件view组成的树形结构,并在Controller中被引用,UIData是界面的控制逻辑数据,可以使用ViewModel去管理这些数据:

这样Controller就变得更简洁,更加模块化,而且ViewModel有保存Activity当前状态的功能,无论你是退到后台还是切换横屏,回到原界面数据状态不变,当然只是短时间临时保存,如果程序长时间在后台的话,还是会被杀死。

但是当ViewModel中的UIData改变时,依然要靠Controller中的References驱动ViewGroup改变,而使用LiveData会通过给数据添加一个观察者,监听到数据改变时自动刷新UI,不再需要reference去驱动UI的刷新:

再使用DataBinding彻底将Controller与ViewGroup解耦:

接下来写一个小Demo,涉及到一下几个技能点:

有几个注意点:

正如上面所说,ViewModel中保存着UIData,当Activity发生以下操作时,依然能临时保证其之前的状态:

但是当系统杀死进入后台的进程时,ViewModel也会被释放,当再次打开程序,ViewModel就被重建了,Activity之前的状态就会消失:

我们可以模拟此情况:

那如何在系统杀死了我们在后台的程序后,依然保存我们的状态数据呢?

当然我们可以借用Activity的onSaveInstanceState方法来保存状态值予以解决:

public class MainActivity extends AppCompatActivity {

    MyViewModel myViewModel;
    ActivityMainBinding binding;
    final static String KEY_TeamA_NAME = "a_number";
    final static String KEY_TeamB_NAME = "b_number";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        if (savedInstanceState != null) {

        myViewModel.getaTeamScore().setValue(savedInstanceState.getInt(KEY_TeamA_NAME));
            myViewModel.getbTeamScore().setValue(savedInstanceState.getInt(KEY_TeamB_NAME));
        }
        binding.setData(myViewModel);
        binding.setLifecycleOwner(this);
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_TeamA_NAME,myViewModel.getaTeamScore().getValue());
        outState.putInt(KEY_TeamB_NAME,myViewModel.getbTeamScore().getValue());
    }
}

不过ViewModel在2019年后就增加了自带保存状态的功能:

在gradle依赖中加上:

implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-alpha03'

ViewModel就会多了个构造函数, 写法就变成如下(Demo中以前的写法已注释了):

//public class MyViewModel extends ViewModel {
//    private MutableLiveData<Integer> aTeamScore;
//    private MutableLiveData<Integer> bTeamScore;
//    private int aBack = 0;
//    private int bBack = 0;
//
//
//    public MutableLiveData<Integer> getaTeamScore() {
//        if (aTeamScore == null) {
//            aTeamScore = new MutableLiveData<>();
//            aTeamScore.setValue(0);
//        }
//        return aTeamScore;
//    }
//
//    public MutableLiveData<Integer> getbTeamScore() {
//        if (bTeamScore == null) {
//            bTeamScore = new MutableLiveData<>();
//            bTeamScore.setValue(0);
//        }
//        return  bTeamScore;
//    }
//
//    public void aTeamAdd(int p) {
//        aBack = aTeamScore.getValue();
//        bBack = bTeamScore.getValue();
//        aTeamScore.setValue(aTeamScore.getValue() + p);
//    }
//
//    public void bTeamAdd(int p) {
//        aBack = aTeamScore.getValue();
//        bBack = bTeamScore.getValue();
//        bTeamScore.setValue(bTeamScore.getValue() + p);
//    }
//
//    public void reset() {
//        aBack = aTeamScore.getValue();
//        bBack = bTeamScore.getValue();
//        aTeamScore.setValue(0);
//        bTeamScore.setValue(0);
//    }
//
//    public void undo() {
//        aTeamScore.setValue(aBack);
//        bTeamScore.setValue(bBack);
//    }
//}

public class MyViewModel extends ViewModel {
    private int aBack = 0;
    private int bBack = 0;

    private SavedStateHandle handle;
    public MyViewModel(SavedStateHandle handle) {
        this.handle = handle;
    }

    public MutableLiveData<Integer> getaTeamScore() {
        if (!handle.contains(MainActivity.KEY_TeamA_NAME)) {
            handle.set(MainActivity.KEY_TeamA_NAME,0);
        }
        return handle.getLiveData(MainActivity.KEY_TeamA_NAME);
    }

    public MutableLiveData<Integer> getbTeamScore() {
        if (!handle.contains(MainActivity.KEY_TeamB_NAME)) {
            handle.set(MainActivity.KEY_TeamB_NAME,0);
        }
        return handle.getLiveData(MainActivity.KEY_TeamB_NAME);
    }

    public void aTeamAdd(int p) {
        aBack = getaTeamScore().getValue();
        bBack = getbTeamScore().getValue();
        getaTeamScore().setValue(getaTeamScore().getValue() + p);
    }

    public void bTeamAdd(int p) {
        aBack = getaTeamScore().getValue();
        bBack = getbTeamScore().getValue();
        getbTeamScore().setValue(getbTeamScore().getValue() + p);
    }

    public void reset() {
        aBack = getaTeamScore().getValue();
        bBack = getbTeamScore().getValue();
        getaTeamScore().setValue(0);
        getbTeamScore().setValue(0);
    }

    public void undo() {
        getaTeamScore().setValue(aBack);
        getbTeamScore().setValue(bBack);
    }
}

而MainActivity中就不需要使用onSaveInstanceState方法去保存状态了:

public class MainActivity extends AppCompatActivity {

    MyViewModel myViewModel;
    ActivityMainBinding binding;
    final static String KEY_TeamA_NAME = "a_number";
    final static String KEY_TeamB_NAME = "b_number";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//        myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        SavedStateViewModelFactory factory = new SavedStateViewModelFactory(getApplication(),this);
        myViewModel = ViewModelProviders.of(this,factory).get(MyViewModel.class);
//        if (savedInstanceState != null) {
//            myViewModel.getaTeamScore().setValue(savedInstanceState.getInt(KEY_TeamA_NAME));
//            myViewModel.getbTeamScore().setValue(savedInstanceState.getInt(KEY_TeamB_NAME));
//        }
        binding.setData(myViewModel);
        binding.setLifecycleOwner(this);
    }

//    @Override
//    protected void onSaveInstanceState(@NonNull Bundle outState) {
//        super.onSaveInstanceState(outState);
//        outState.putInt(KEY_TeamA_NAME,myViewModel.getaTeamScore().getValue());
//        outState.putInt(KEY_TeamB_NAME,myViewModel.getbTeamScore().getValue());
//    }
}

这样在程序退到后台,即使系统会杀死后台程序,当我们再次回到程序时,之前的状态依然得以保存。

但是如果我们重新启动程序或点击back键退出程序后再启动,以前的状态就会消失,这是我们可能就需要采用永久保存状态数据了。

首先,安卓有四种存储方式:

接下来了解一下持久化保存方式之一的Shared preference的用法。

Shared preference

我们可以单独建个类,去实现保存和获取数据的功能:

public class MySharedData {
    public int number = 0;
    private Context context;
    final static String CONTEXT_NAME = "context_name";
    final static String NUMBER_KEY = "myNumber";
    public MySharedData(Context context) {
        this.context = context;
    }

    public void save() {
        SharedPreferences sp = context.getSharedPreferences(CONTEXT_NAME,Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putInt(NUMBER_KEY,number);
        editor.apply();
    }

    public int fetch() {
        SharedPreferences sp = context.getSharedPreferences(CONTEXT_NAME,Context.MODE_PRIVATE);
        int x = sp.getInt(NUMBER_KEY,0);
        number = x;
        return x;
    }
    
}

在Activity中的具体使用:

MySharedData sharedData = new MySharedData((getApplicationContext()));
        sharedData.number = 100;
        sharedData.save();
        int number = sharedData.fetch();
        Log.d("tag","result: "+number);

上面的写法是将Activity中的context传到了MySharedData中,context才能调用出SharedPreferences的api,实际上ViewModel中自带有Application,需要的是继承于AndroidViewModel:

public class MyAndroidViewModel extends AndroidViewModel {

    final static String MY_KEY = "myKey";
    final static String SHARED_NAME = "mySharedName";
    private SavedStateHandle handle;
    public MyAndroidViewModel(@NonNull Application application, SavedStateHandle handle) {
        super(application);
        this.handle = handle;
        if (handle.contains(MY_KEY)) {
            load();
        }
    }

    public LiveData<Integer> getNumber() {
        return handle.getLiveData(MY_KEY);
    }

    void load() {
        SharedPreferences sp = getApplication().getSharedPreferences(SHARED_NAME, Context.MODE_PRIVATE);
        int x = sp.getInt(MY_KEY,0);
        handle.set(MY_KEY,x);
    }

    public void add(int x) {
        handle.set(MY_KEY,getNumber().getValue() + x);
    }

    //持久化保存
    public void save() {
        SharedPreferences sp = getApplication().getSharedPreferences(SHARED_NAME,Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putInt(MY_KEY,getNumber().getValue());
        editor.apply();
    }
}

调用save时建议放到Activity中的onPause方法节点中做统一保存,而不是在每次add操作时save,这样利于性能优化:

@Override
    protected void onPause() {
        super.onPause();
        myAndroidViewModel.save();
    }
上一篇下一篇

猜你喜欢

热点阅读