美文网首页
Android MVC改造MVVM记录

Android MVC改造MVVM记录

作者: 咕噜__ | 来源:发表于2022-11-23 15:41 被阅读0次

    项目背景

    金融产品,主要请求后端数据进行展示。

    为何改造

    之前为了快速上线直接使用MVC,后续随着版本迭代,activity的代码量和业务日趋增大,MVC已经不满足目前开发效率的要求

    为何选择MVVM

    相比较而言,MVVM的数据绑定模式更加契合此种展示类项目,且将大量业务逻辑代码抽离到viewmodel中后,代码层次更加清晰;相比较MVP而言又减少了各种接口的预定义,更加灵活。

    改造步骤

    1.导包
        dataBinding {
            enabled = true
        }
    
        //RxJava2.0+Retrofit2的依赖
        implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
        implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
        implementation 'com.amitshekhar.android:rx2-android-networking:1.0.0'//支持把json解析成Java对象
    
        implementation 'com.squareup.retrofit2:retrofit:2.3.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    
        //管理rxjava的生命周期
        implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'
        implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.1'
        implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'
    
    def lifecycle_version = "2.5.1"
    
        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
        // alternately - if using Java8, use the following instead of lifecycle-compiler
        implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
    

    使用了RXjava2与Retrofit2,还有rxlifecycle2

    2.创建基类

    2.1 RxMVVMAppCompatActivity

    import android.os.Bundle;
    
    import androidx.annotation.CallSuper;
    import androidx.annotation.CheckResult;
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.trello.rxlifecycle2.LifecycleProvider;
    import com.trello.rxlifecycle2.LifecycleTransformer;
    import com.trello.rxlifecycle2.RxLifecycle;
    import com.trello.rxlifecycle2.android.ActivityEvent;
    import com.trello.rxlifecycle2.android.RxLifecycleAndroid;
    
    import io.reactivex.Observable;
    import io.reactivex.subjects.BehaviorSubject;
    
    public class RxMVVMAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
        private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
    
        public RxMVVMAppCompatActivity() {
        }
    
        @NonNull
        @CheckResult
        public final Observable<ActivityEvent> lifecycle() {
            return this.lifecycleSubject.hide();
        }
    
        @NonNull
        @CheckResult
        public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
            return RxLifecycle.bindUntilEvent(this.lifecycleSubject, event);
        }
    
        @NonNull
        @CheckResult
        public final <T> LifecycleTransformer<T> bindToLifecycle() {
            return RxLifecycleAndroid.bindActivity(this.lifecycleSubject);
        }
    
        @CallSuper
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            this.lifecycleSubject.onNext(ActivityEvent.CREATE);
        }
    
        @CallSuper
        protected void onStart() {
            super.onStart();
            this.lifecycleSubject.onNext(ActivityEvent.START);
        }
    
        @CallSuper
        protected void onResume() {
            super.onResume();
            this.lifecycleSubject.onNext(ActivityEvent.RESUME);
        }
    
        @CallSuper
        protected void onPause() {
            this.lifecycleSubject.onNext(ActivityEvent.PAUSE);
            super.onPause();
        }
    
        @CallSuper
        protected void onStop() {
            this.lifecycleSubject.onNext(ActivityEvent.STOP);
            super.onStop();
        }
    
        @CallSuper
        protected void onDestroy() {
            this.lifecycleSubject.onNext(ActivityEvent.DESTROY);
            super.onDestroy();
        }
    }
    

    2.2 RxMVVMFragment

    import android.os.Bundle;
    import android.view.View;
    
    import androidx.annotation.CallSuper;
    import androidx.annotation.CheckResult;
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.fragment.app.Fragment;
    
    import com.trello.rxlifecycle2.LifecycleProvider;
    import com.trello.rxlifecycle2.LifecycleTransformer;
    import com.trello.rxlifecycle2.RxLifecycle;
    import com.trello.rxlifecycle2.android.ActivityEvent;
    import com.trello.rxlifecycle2.android.FragmentEvent;
    import com.trello.rxlifecycle2.android.RxLifecycleAndroid;
    
    import io.reactivex.Observable;
    import io.reactivex.subjects.BehaviorSubject;
    
    public class RxMVVMFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
        private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
    
        @Override
        @NonNull
        @CheckResult
        public final Observable<FragmentEvent> lifecycle() {
            return lifecycleSubject.hide();
        }
    
        @Override
        @NonNull
        @CheckResult
        public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
            return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
        }
    
        @Override
        @NonNull
        @CheckResult
        public final <T> LifecycleTransformer<T> bindToLifecycle() {
            return RxLifecycleAndroid.bindFragment(lifecycleSubject);
        }
    
        @Override
        public void onAttach(android.app.Activity activity) {
            super.onAttach(activity);
            lifecycleSubject.onNext(FragmentEvent.ATTACH);
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            lifecycleSubject.onNext(FragmentEvent.CREATE);
        }
    
        @Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
        }
    
        @Override
        public void onStart() {
            super.onStart();
            lifecycleSubject.onNext(FragmentEvent.START);
        }
    
        @Override
        public void onResume() {
            super.onResume();
            lifecycleSubject.onNext(FragmentEvent.RESUME);
        }
    
        @Override
        public void onPause() {
            lifecycleSubject.onNext(FragmentEvent.PAUSE);
            super.onPause();
        }
    
        @Override
        public void onStop() {
            lifecycleSubject.onNext(FragmentEvent.STOP);
            super.onStop();
        }
    
        @Override
        public void onDestroyView() {
            lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
            super.onDestroyView();
        }
    
        @Override
        public void onDestroy() {
            lifecycleSubject.onNext(FragmentEvent.DESTROY);
            super.onDestroy();
        }
    
        @Override
        public void onDetach() {
            lifecycleSubject.onNext(FragmentEvent.DETACH);
            super.onDetach();
        }
    }
    

    2.3 BaseViewModel

    public class BaseViewModel extends ViewModel {
    
        public BaseViewModel() {
            // trigger user load.
        }
    
    }
    

    2.4 BaseActivity

    public abstract class BaseActivity<DB extends ViewDataBinding,VM extends BaseViewModel> extends RxMVVMAppCompatActivity {
    
        private ActionBarView mActionBar;
        private View mNoNetView;
        private View btnReConnectNet;
        private DB childDataBinding;
        private VM viewModel;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            MyApplication.getInstants().addActivity(this);
            supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
            initDataBinding();
            initViewModel();
            initView();
            initData();
            initEvent();
        }
    
        private void initViewModel(){
            viewModel = new ViewModelProvider(this).get(getViewModelClass());
        }
    
        private void initDataBinding() {
            ActivityBaseTestBinding baseBinding = DataBindingUtil.setContentView(this, R.layout.activity_base_test);
            childDataBinding = DataBindingUtil.inflate(LayoutInflater.from(this), getLayoutResID(), baseBinding.layoutContent, true);
            mActionBar = baseBinding.abv;
            mNoNetView = baseBinding.noNet;
            btnReConnectNet = mNoNetView.findViewById(R.id.btn_reconnect_net);
        }
    
        /**
         * 返回Activity的ViewModel,由子类实现
         * @return
         */
        public abstract Class<VM> getViewModelClass();
    
        /**
         * 返回activity的布局xml,由子类实现
         * @return
         */
        public abstract int getLayoutResID();
    
        /**
         * 初始化布局,由子类实现
         */
        public abstract void initView();
    
        /**
         * 初始化数据,由子类实现,在initView之后调用
         */
        public abstract void initData();
    
        /**
         * 统一处理返回事件,触发场景为物理返回键/actionbar的返回键
         * @return 返回true表示拦截返回事件,默认false不拦截
         */
        public abstract boolean onBackClick();
    
        public DB getDataBinding() {
            return childDataBinding;
        }
    
        public VM getViewModel() {
            return viewModel;
        }
    
        private void initEvent() {
            btnReConnectNet.setOnClickListener(v -> {
                // 点击重连网络
                onReConnectNetClick();
            });
            mActionBar.setOnBackClickListener(this::onBackClick);
        }
    
        public ActionBarView getmActionBar() {
            return mActionBar;
        }
    
        /**
         * 显示无网络页面
         */
        public void showNoNetView() {
            mNoNetView.setVisibility(View.VISIBLE);
        }
    
        /**
         * 隐藏无网页
         */
        public void hideNoNetView() {
            mNoNetView.setVisibility(View.GONE);
        }
    
        /**
         * 点击重连网络,子类实现
         */
        protected abstract void onReConnectNetClick();
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                if (onBackClick()) {
                    return true;
                }
            }
            return super.onKeyDown(keyCode, event);
        }
    
    }
    

    2.5 BaseFragment

    public abstract class BaseFragment<DB extends ViewDataBinding,VM extends BaseViewModel> extends RxMVVMFragment {
        protected View root;
        /**
         * 当前view状态变化
         */
        private boolean beforeVisibleState  = false;
        private DB dataBinding;
        private VM viewModel;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            if(getLayoutResource() == 0){
                return super.onCreateView(inflater, container, savedInstanceState);
            }
            if(root == null){
                dataBinding = DataBindingUtil.inflate(inflater, getLayoutResource(), container, false);
                viewModel = new ViewModelProvider(this).get(getViewModelClass());
                root = dataBinding.getRoot();
            }
            initView(root);
            return root;
        }
    
        /**
         * 初始化布局
         * @param root 
         */
        protected abstract void initView(View root);
    
        /**
         * 返回fragment的xml布局文件,由子类实现
         * @return 
         */
        protected abstract int getLayoutResource();
    
        /**
         * 返回Fragment的viewModel,由子类实现
         * @return 
         */
        public abstract Class<VM> getViewModelClass();
    
        public DB getDataBinding() {
            return dataBinding;
        }
    
        public VM getViewModel() {
            return viewModel;
        }
    
        /**
         * 停止加载数据
         */
        protected void stopLoadData() {
        }
    
        /**
         * 开始加载数据
         */
        protected void startLoadData() {
        }
    
        @Override
        public void onResume() {
            super.onResume();
            if (!beforeVisibleState && isResumed()) {
                dispatchHintState(true);
            }
            beforeVisibleState = true;
        }
    
        @Override
        public void onPause() {
            super.onPause();
            if (beforeVisibleState && !isResumed()) {
                dispatchHintState(false);
            }
            beforeVisibleState = false;
        }
    
        @Override
        public void onDestroyView() {
            dataBinding.unbind();
            super.onDestroyView();
        }
    
        private void dispatchHintState(boolean state){
            //避免走两次,因为onResume会再次调用
            if(beforeVisibleState == state){
                return;
            }
    
            beforeVisibleState = state;
            if(state){
                startLoadData();
            }else{
                stopLoadData();
            }
        }
    
    }
    
    3 修改

    3.1 改造布局
    alt+enter添加<layout>标签


    1669273803267.jpg

    添加之后如下

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/tv_test"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
        </LinearLayout>
    </layout>
    

    3.2 创建ViewModel

    public class TestViewModel extends BaseViewModel {
        /**
         * LiveData数据,V层就是通过订阅此数据进行界面刷新
         */
        private final MutableLiveData<VersionUpdateEntity.VersionUpdateData> userLiveData = new MutableLiveData<>();
    
        public LiveData<VersionUpdateEntity.VersionUpdateData> getUser() {
            return userLiveData;
        }
    
        public TestViewModel() {
            // trigger user load.
        }
    
        public void queryVersion(BaseActivity<? extends ViewDataBinding,? extends BaseViewModel> context){
            // 网络请求
            // ······
            // ······
            // 请求成功,刷新数据
            userLiveData.setValue(entity.getData());
        }
    }
    

    3.3 更换原来activity和fragment继承的基类

    public class MyTestActivity extends BaseActivity<ActivityMyTestBinding, TestViewModel> {
        @Override
        public Class<TestViewModel> getViewModelClass() {
            return TestViewModel.class;
        }
    
        @Override
        public int getLayoutResID() {
            return R.layout.activity_my_test;
        }
    
        @Override
        public void initView() {
            getDataBinding().tvTest.setText("初始化布局");
        }
    
        @Override
        public void initData() {
            // 订阅数据
            getViewModel().getUser().observe(this, new Observer<VersionUpdateEntity.VersionUpdateData>() {
                @Override
                public void onChanged(VersionUpdateEntity.VersionUpdateData versionUpdateData) {
                    MyLog.iModule("获取到数据:"+versionUpdateData.getForceUpdateFlag().toString());
    
                }
            });
            // 查询数据
            getViewModel().queryVersion(this);
    
        }
    
        @Override
        protected void onReConnectNetClick() {
    
        }
    
        @Override
        public boolean onBackClick() {
            return false;
        }
    }
    

    4 总结

    主要修改点:继承baseActivity,并定义泛型ViewDataBinding、ViewModel
    ViewModel:承接业务逻辑、网络请求,liveData定义
    Activity:承接事件监听(点击,滑动等View相关),LiveData数据订阅,界面刷新
    Model:网络框架&返回数据实体对象

    相关文章

      网友评论

          本文标题:Android MVC改造MVVM记录

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