項(xiàng)目背景
金融產(chǎn)品剥哑,主要請(qǐng)求后端數(shù)據(jù)進(jìn)行展示淌哟。
為何改造
之前為了快速上線直接使用MVC,后續(xù)隨著版本迭代,activity的代碼量和業(yè)務(wù)日趨增大诗力,MVC已經(jīng)不滿足目前開(kāi)發(fā)效率的要求
為何選擇MVVM
相比較而言,MVVM的數(shù)據(jù)綁定模式更加契合此種展示類項(xiàng)目出革,且將大量業(yè)務(wù)邏輯代碼抽離到viewmodel中后贝或,代碼層次更加清晰;相比較MVP而言又減少了各種接口的預(yù)定義泼诱,更加靈活坛掠。
改造步驟
1.導(dǎo)包
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對(duì)象
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.創(chuàng)建基類
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治筒,由子類實(shí)現(xiàn)
* @return
*/
public abstract Class<VM> getViewModelClass();
/**
* 返回activity的布局xml屉栓,由子類實(shí)現(xiàn)
* @return
*/
public abstract int getLayoutResID();
/**
* 初始化布局,由子類實(shí)現(xiàn)
*/
public abstract void initView();
/**
* 初始化數(shù)據(jù)耸袜,由子類實(shí)現(xiàn)友多,在initView之后調(diào)用
*/
public abstract void initData();
/**
* 統(tǒng)一處理返回事件,觸發(fā)場(chǎng)景為物理返回鍵/actionbar的返回鍵
* @return 返回true表示攔截返回事件堤框,默認(rèn)false不攔截
*/
public abstract boolean onBackClick();
public DB getDataBinding() {
return childDataBinding;
}
public VM getViewModel() {
return viewModel;
}
private void initEvent() {
btnReConnectNet.setOnClickListener(v -> {
// 點(diǎn)擊重連網(wǎng)絡(luò)
onReConnectNetClick();
});
mActionBar.setOnBackClickListener(this::onBackClick);
}
public ActionBarView getmActionBar() {
return mActionBar;
}
/**
* 顯示無(wú)網(wǎng)絡(luò)頁(yè)面
*/
public void showNoNetView() {
mNoNetView.setVisibility(View.VISIBLE);
}
/**
* 隱藏?zé)o網(wǎng)頁(yè)
*/
public void hideNoNetView() {
mNoNetView.setVisibility(View.GONE);
}
/**
* 點(diǎn)擊重連網(wǎng)絡(luò)域滥,子類實(shí)現(xiàn)
*/
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;
/**
* 當(dāng)前view狀態(tài)變化
*/
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布局文件,由子類實(shí)現(xiàn)
* @return
*/
protected abstract int getLayoutResource();
/**
* 返回Fragment的viewModel蜈抓,由子類實(shí)現(xiàn)
* @return
*/
public abstract Class<VM> getViewModelClass();
public DB getDataBinding() {
return dataBinding;
}
public VM getViewModel() {
return viewModel;
}
/**
* 停止加載數(shù)據(jù)
*/
protected void stopLoadData() {
}
/**
* 開(kāi)始加載數(shù)據(jù)
*/
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){
//避免走兩次启绰,因?yàn)閛nResume會(huì)再次調(diào)用
if(beforeVisibleState == state){
return;
}
beforeVisibleState = state;
if(state){
startLoadData();
}else{
stopLoadData();
}
}
}
3 修改
3.1 改造布局
alt+enter添加<layout>標(biāo)簽
添加之后如下
<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 創(chuàng)建ViewModel
public class TestViewModel extends BaseViewModel {
/**
* LiveData數(shù)據(jù),V層就是通過(guò)訂閱此數(shù)據(jù)進(jìn)行界面刷新
*/
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){
// 網(wǎng)絡(luò)請(qǐng)求
// ······
// ······
// 請(qǐng)求成功资昧,刷新數(shù)據(jù)
userLiveData.setValue(entity.getData());
}
}
3.3 更換原來(lái)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() {
// 訂閱數(shù)據(jù)
getViewModel().getUser().observe(this, new Observer<VersionUpdateEntity.VersionUpdateData>() {
@Override
public void onChanged(VersionUpdateEntity.VersionUpdateData versionUpdateData) {
MyLog.iModule("獲取到數(shù)據(jù):"+versionUpdateData.getForceUpdateFlag().toString());
}
});
// 查詢數(shù)據(jù)
getViewModel().queryVersion(this);
}
@Override
protected void onReConnectNetClick() {
}
@Override
public boolean onBackClick() {
return false;
}
}
4 總結(jié)
主要修改點(diǎn):繼承baseActivity酬土,并定義泛型ViewDataBinding、ViewModel
ViewModel:承接業(yè)務(wù)邏輯格带、網(wǎng)絡(luò)請(qǐng)求撤缴,liveData定義
Activity:承接事件監(jiān)聽(tīng)(點(diǎn)擊,滑動(dòng)等View相關(guān))叽唱,LiveData數(shù)據(jù)訂閱屈呕,界面刷新
Model:網(wǎng)絡(luò)框架&返回?cái)?shù)據(jù)實(shí)體對(duì)象