在实际的开发过程中,常用到的设计模式有MVC、MVP、MVVM3种设计模式,在MVC设计模式中,Activity处于C层,却往往要兼顾V和C的2部分责任,是的C层和V层的耦合性太高,导致Activity或者Fragment层的代码臃肿,不具备扩展性,因此选择使用MVP、MVVM设计模式成为主流模式。
从本节开始,就介绍一下MVP设计模式的使用。在MVP模式中同样是分为3个角色:
M:Model层,主要负责数据的处理,包括联网操作,数据库等;
V:View层,主要包括Activity、Fragment;
P:Presenter层,主要负责和M层和V层交互。
其实在使用MVP结构设计时,最主要的思想就是接口设计,在MVC中,C往往完成View和C的业务逻辑功能,在MVP中,往往是将View层抽象为接口,将业务逻辑抽象成Presenter层,实现了各司其职。
接下来就先实现一个简单的MVP设计模式
1.Model层
在Model层主要就是获取数据然后做数据回调
public interface IModel {
/**
* 网络请求加载数据,回调
*/
void loadData(String appid,DataCallback callback);
/**
* 回调接口
*/
interface DataCallback{
void complete(String url);
}
}
2.View层
public interface IBaseView {
/**
* 数据显示
*/
void showData(String url);
}
3.P层
P层主要是实现View层和Model层的绑定,因此需要实例化两个Module。
public class IPresenter {
private IBaseView mView;
//在此实例化Model,不需要传入构造方法
private IModel mModel = new ModelImpl();
public IPresenter(IBaseView mView) {
super();
this.mView = mView;
}
/**
* 实现View和Model的绑定
* @param appid View给P层的数据
*/
public void fetch(String appid){
if(mModel != null){
mModel.loadData(appid,new IModel.DataCallback() {
@Override
public void complete(String url) {
//Model层的数据回调过来,在View层加载
mView.showData(url);
}
});
}
}
}
在上述代码中,没有将Model放在构造方法中的原因就是,如果在Presenter的构造方法中传入Model,那么就得在View层创建Model的实例,这样就会又和Model建立联系,所以在P层做了实现类。
public class ModelImpl implements IModel {
/**
* 加载数据
* @param appid
* @param callback 回调
*/
@Override
public void loadData(String appid, DataCallback callback) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
IDataService dataService = retrofit.create(IDataService.class);
Call<DataBean> call = dataService.data("getAppConfig.php", appid);
call.enqueue(new Callback<DataBean>() {
@Override
public void onResponse(Call<DataBean> call, Response<DataBean> response) {
String url = response.body().getUrl();
//在此将数据回调,到P层
callback.complete(url);
}
@Override
public void onFailure(Call<DataBean> call, Throwable t) {
}
});
}
}
在Model的实现类ModelImp中,做网络请求操作,将数据回调到P层。
在View层,即可点击获取数据按钮,得到数据的回调。
public class MainActivity extends AppCompatActivity implements IBaseView {
private IPresenter presenter;
private EditText et_appid;
private Button btn_get;
private TextView tv_data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initPresent
initPresent();
findView();
initEvent();
}
private void initEvent() {
btn_get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//点击按钮,就去向P层请求获取数据
presenter.fetch(getEditText());
}
});
}
private void findView() {
et_appid = findViewById(R.id.et_appid);
btn_get = findViewById(R.id.btn_get);
tv_data = findViewById(R.id.tv_data);
}
private void initPresent() {
presenter = new IPresenter(this);
}
@Override
public void showData(String url) {
//url已经在P层获取了M层的回调,这边值需要显示
tv_data.setText(url);
}
public String getEditText(){
return et_appid.getText().toString();
}
}
这样就根据输入的appid得到了最终的响应数据。
目前,以上就是简单地实现了MVP的基础架构,接下来,我们对这个基础架构进行优化。
优化1:内存泄漏
private void initPresent() {
presenter = new IPresenter(this);
}
我们在View层使用Presenter的时候,都要去创建一个Presenter实例,因为P和V是一一对应的关系,所以每次创建一个新的View,都需要去创建一个P实例,而且这个P持有View的引用,会造成内存泄漏。
内存泄漏的问题:因为View层和P层是关联的,当View层需要获取数据时,会通过P层去向Model层获取数据;如果当前View退出时,其对应的P层可能还在进行耗时操作,导致View不能退出,导致了内存泄漏。
所以可以创建一个Presenter的基类,当中实现对View的绑定和移除,也就是说,在onCreate的时候,就绑定,在onDestory的时候就移除。
所以先对P和V做一下操作。
public class BasePresenter<V> {
/**
* 使用弱引用创建View
*/
protected WeakReference<V> mView;
/**
* 关联View
*/
public void attachView(V view){
mView = new WeakReference<>(view);
}
/**
* 解除关联
*/
public void detachView(V view){
if(mView != null){
mView.clear();
}
}
protected V getView(){
return mView.get();
}
}
创建一个Activity的基类BaseActivity
public abstract class BaseActivity<V,P extends BasePresenter<V>> extends AppCompatActivity {
protected P mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//创建Presenter,并关联View
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mPresenter != null){
mPresenter.detachView((V) this);
mPresenter = null;
}
}
protected abstract P createPresenter();
}
之前,我们在View层使用Presenter的时候,是使用new 关键字创建出一个Presenter实例化,然后调用fetch方法去将View和Model绑定。在创建的这个BaseActivity中,在onCreate方法中是实例化了这个Presenter,然后绑定了当前的View。
那么在真正的Activity中,去继承这个BaseActivity的时候,就不需要去创建,对于每一个Activity都是这样的。
public class MainActivity extends BaseActivity<IMainView, MainPresenter> implements IMainView {
private EditText et_appid;
private Button btn_get;
private TextView tv_data;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
initEvent();
}
@Override
protected MainPresenter createPresenter() {
//在这里已经将Presenter初始化
return new MainPresenter();
}
private void initEvent() {
btn_get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//点击按钮,就去向P层请求获取数据
mPresenter.fetch(getEditText());
}
});
}
private void findView() {
et_appid = findViewById(R.id.et_appid);
btn_get = findViewById(R.id.btn_get);
tv_data = findViewById(R.id.tv_data);
}
@Override
public void showData(String url) {
//url已经在P层获取了M层的回调,这边值需要显示
tv_data.setText(url);
}
public String getEditText(){
return et_appid.getText().toString();
}
}
对于每一个Presenter来说,都不需要再去创建构造方法,直接继承BasePresenter即可。
public class MainPresenter extends BasePresenter<IMainView> {
//在此实例化Model,不需要传入构造方法
private IModel mModel = new ModelImpl();
/**
* 实现View和Model的绑定
* @param appid View给P层的数据
*/
public void fetch(String appid){
if(mModel != null){
mModel.loadData(appid,new IModel.DataCallback() {
@Override
public void complete(String url) {
//Model层的数据回调过来,在View层加载
getView().showData(url);
}
});
}
}
}
通过这个方法,实现了V和P的绑定,而且不需要去手动new出一个实例,更重要的是解决了内存泄漏的这个问题。
在之后的开发项目中,可以使用这个架构模式进行MVP设计的开发,当然不是一定要用MVP,根据实际的情况去选择合适的设计模式,好了,MVP就先简单介绍到这里,后边还会继续更新。