1.Mvc模式
三层模型Model-View-Control,我们可以把一个xml布局看作一个View,Activity或者Fragment看作一个Control,Model由相关的数据操作类来承担,平时一般代码最常用写法。
使用总结:
- 由于xml布局限制,Activity某种意义上充当了controller和view这2层,controller和view层无法解耦
- 逻辑代码上数据业务逻辑尽量在Mode层处理,比如接口请求采用回调形式提供方法给Activity调用,其他业务功能尽量封装工具类供Activity使用,这样才能更好的扩展业务,代码灵活性更高
2.Mvp模式
Model-View-Presenter三层,Model和View之间不进行通讯,Presenter持有View与Model处理业务逻辑,实现了Controller和View解耦。Mvp是目前主流开发模式,下面通过Google Mvp Demo步步分析。
Model层
主要是模拟接口请求回调,我们快速过。
/**
* 模拟请求接口数据
*
* @author zhukui
*/
public class MvpModel {
/**
* 模拟获取数据
*/
public void getData(final IHttpCallBack callBack) {
HttpUtil.requestData(callBack);
}
}
View层
定义BaseView,Fragment继承BaseView。
/**
* BaseView
*
* @author zhukui
*/
public interface BaseView<P extends BasePresenter> {
void setPresenter(P presenter);
boolean isActive();
}
/**
* MvpFragment
*
* @author zhukui
*/
public class MvpFragment extends Fragment implements MvpContract.View {
private static final String ARG_PARAM_1 = "param_1";
private String mParam1;
private TextView mTextView;
private MvpContract.Presenter mPresenter;
public static MvpFragment newInstance(String param1) {
MvpFragment fragment = new MvpFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM_1, param1);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM_1);
}
//初始化presenter
mPresenter = new MvpPresenterImpl(this);
setPresenter(mPresenter);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_mvp, container, false);
mTextView = view.findViewById(R.id.tv_result);
final Button button = view.findViewById(R.id.btn_get_data);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.getData();
}
});
return view;
}
@Override
public void onStart() {
super.onStart();
mPresenter.start();
mPresenter.getData();
}
@Override
public void showLoading() {
Toast.makeText(getContext(), "Loading...", Toast.LENGTH_LONG).show();
}
@Override
public void refreshUI(String result) {
mTextView.setText(result);
}
@Override
public void showError() {
}
@Override
public void setPresenter(MvpContract.Presenter presenter) {
mPresenter = presenter;
}
@Override
public boolean isActive() {
return isAdded();
}
@Override
public void onDetach() {
super.onDetach();
mPresenter.detachView();
}
}
Presenter层
定义BasePresenter和其实现类MvpPresenterImpl,这里实现了Presenter持有弱引用View,并在页面detach时清空持有的弱引用View。
/**
* BasePresenter
*
* @author zhukui
*/
public interface BasePresenter {
void start();
}
/**
* PresenterImpl - 实现业务逻辑并刷新页面
*
* @author zhukui
*/
public class MvpPresenterImpl<V extends MvpContract.View> implements MvpContract.Presenter {
/**
* Presenter持有弱引用View,并在页面detach时清空持有的弱引用View
*/
protected WeakReference<V> mView;
protected MvpModel mMvpModel;
public MvpPresenterImpl(V view) {
mView = new WeakReference<V>(view);
view.setPresenter(this);
mMvpModel = new MvpModel();
}
protected boolean isViewActive() {
return mView != null && mView.get().isActive();
}
@Override
public void detachView() {
if (mView != null) {
mView.clear();
mView = null;
}
}
@Override
public void getData() {
if (!isViewActive()) {
return;
}
mMvpModel.getData(new IHttpCallBack<String>() {
@Override
public void onSuccess(String s) {
mView.get().refreshUI("success");
}
@Override
public void onError(String error) {
mView.get().refreshUI("error");
}
});
}
@Override
public void start() {
if (!isViewActive()) {
return;
}
mView.get().showLoading();
}
}
Contract契约类
Google MVP契约类,用于定义同一个界面的view和presenter的接口,通过规范的方法命名或注释,可以清晰的看到整个页面的逻辑。
/**
* Contract 契约类
* <p>
* Google MVP契约类,用于定义同一个界面的view和presenter的接口,
* 通过规范的方法命名或注释,可以清晰的看到整个页面的逻辑。
*
* @author zhukui
*/
public interface MvpContract {
interface Presenter extends BasePresenter {
/**
* 获取数据
*/
void getData();
/**
* 拆卸View
*/
void detachView();
}
interface View extends BaseView<Presenter> {
/**
* 显示加载中
*/
void showLoading();
/**
* 刷新界面
*/
void refreshUI(String result);
/**
* 显示错误界面
*/
void showError();
}
}
使用总结:
- mvp模式,实现了model与view的解耦,Presenter处理业务逻辑
- Presenter跟View相互持有,导致View页面关闭时Presenter层可能有异步线程正在工作,从而导致页面释放不了,解决方法:Presenter持有弱引用View,并在页面detach时清空持有的弱引用View。
- 业务逻辑非常复杂的页面,Presenter层、View层接口会很多很庞大,难以维护。
3.Mvvm模式
View-ViewModel-View三层,通过双向绑定的机制,实现了数据和UI一方改变另一方能够及时更新。谷歌Mvvm框架中View和ViewModel通过Binding进行关联,并通过DataBinding完成双向绑定。MVVM代码结构如下:
?dataBinding配置
在build.gradle文件android标签下增加dataBinding代码。
android {
dataBinding{
enabled true
}
}
Model层
模拟接口请求,这里直接跳过。
View层
A.xml布局设置
指定了当前页面的Model对象,用layout包裹,data中配置Model对象变量名和Model对象路径,通过@{}或@={}的方式进行引用,其中@={}的方式表示双向绑定。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!--绑定ViewModel-->
<variable
name="viewModel"
type="com.android.zk.mvvm.viewmodel.MvvmViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btn_getAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:onClick="@{viewModel.getData}"
android:text="获取数据" />
<TextView
android:id="@+id/tv_getResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.result}" />
</LinearLayout>
</layout>
B.activity设置
ActivityMvvmBinding为同步代码后自动生成类,activity中实现了View与ViewModel绑定。
/**
* Mvvm模式
* <p>
* 说明:简单使用,后续会完善使用
*
* @author zhukui
*/
public class MvvmActivity extends AppCompatActivity {
private ActivityMvvmBinding binding;
private MvvmViewModel mvvmViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//替换setContentView
binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
mvvmViewModel = new MvvmViewModel(getApplication(), binding);
//初始化viewModel
binding.setViewModel(mvvmViewModel);
}
}
ViewModel层
ViewMode层主要处理业务逻辑,binding是传递过来的,实现双向绑定需要继BaseObservable,对读方法用@Bindable处理,写方法用notifyPropertyChanged处理,其中BR为自动生成。
/**
* ViewModel
*
* @author zhukui
*/
public class MvvmViewModel extends BaseObservable {
private ActivityMvvmBinding binding;
private MvvmModel mvvmModel;
private String result;
@Bindable
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
notifyPropertyChanged(BR.result);
}
public MvvmViewModel(Context context, ActivityMvvmBinding binding) {
this.binding = binding;
mvvmModel = new MvvmModel();
setResult("success");
}
public void getData(View view) {
mvvmModel.getData(new IHttpCallBack<String>() {
@Override
public void onSuccess(String success) {
setResult(success);
}
@Override
public void onError(String error) {
setResult(error);
}
});
}
}
使用总结:
- MVVM中Activity和xml布局充当View,ViewModel处理业务逻辑以及获取数据,弱化Model。
- MVVM解决MVP中V层、P层好多接口的问题,让View变得更简洁,修改任何一方,两者都会保持数据同步。
- 缺点:数据和视图的双向绑定,导致出现问题时不太好定位问题来源,有可能是数据、视图属性更改导致。
|