Android MVC改造MVVM记录
2022-11-23 本文已影响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:网络框架&返回数据实体对象