美文网首页Android-Jetpack
[Android开发]ViewModel中的LiveData和D

[Android开发]ViewModel中的LiveData和D

作者: 沈枫_SerenF | 来源:发表于2020-10-03 12:54 被阅读0次

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

    • Model层:负责处理数据,包括网络数据和持久化数据的获取、加工等。
    • View层:负责处理界面绘制,展示数据,并对用户产生交互反馈等,在Android中表现为如Activity/Fragment等。
      -Controller层:负责处理业务逻辑等。

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

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

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

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

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

    • ViewModel
    • LiveData
    • DataBinding
    • Screen Orientation
    • Localization
    • Vector Drawable

    有几个注意点:

    • DataBinding需要在gradle中开启,如果使用矢量图也需要开启向下兼容功能android.defaultConfig.vectorDrawables.useSupportLibrary = true
    • Demo中通过DataBinding将数据回绑到界面xml文件中,应该有更好的方式,后面再探索。

    正如上面所说,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键退出程序后再启动,以前的状态就会消失,这是我们可能就需要采用永久保存状态数据了。

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

    • Internal file storage. 内部文件存储。
    • Extenal file storage. 外部文件存储。
    • Shared preference. 也属于内部存储,主要存储简单数据。
    • Databases.数据库。

    接下来了解一下持久化保存方式之一的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();
        }
    

    相关文章

      网友评论

        本文标题:[Android开发]ViewModel中的LiveData和D

        本文链接:https://www.haomeiwen.com/subject/zorwuktx.html