首先,了解下一个java的原则,这便于后面理解presenter和view、model之间的依赖。
<!--依赖倒置原则-->
* <!--1.高层模块不依赖低层模块, 两者都依赖于抽象.-->
* <!--类A直接依赖类B, 假如要将类A改为依赖类C, 则必须通过修改类A的代码来达成;-->
* <!--这种场景下: 类A一般是高层模块, 负责复杂的业务逻辑; 类B和类C是低层模块, 负责基本的原子操作;-->
* <!--假如修改类A, 会给程序带来不必要的风险;-->
* <!--解决方案: 将类A修改为依赖接口I, 类B和类C各自实现接口I;-->
* <!--类A通过接口I间接与类B或者类C发生联系, 则会大大降低修改类A的几率;-->
* <!--2.抽象不依赖于细节.-->
* <!--3.细节应该依赖于抽象.-->
* <!--这三者请遵循依赖倒置原则: presenter子类依赖view和model的父类, 不要直接去依赖这二者的子类-->
这个是mvp模式的类图依赖逻辑: 很明显, presenter和view、presenter和model都是双向依赖, 所以需要相互注入, 后面的代码注释中有说明注入方式。
正题:mvp现在比较好的方式是契约模式,所以直接上契约模式
* <!--※契约的抽象思考:
* <!--1.model中要调用什么方法, 做什么操作? 比如这里是去发指令去请求耗时数据
* <!--2.View中要显示什么? 比如这里view要显示进度条, 完成后的数据
* <!--3.presenter中要如何调度model和view?
* <!--------比如这里就是调度model去拿数据, 调度view去显示进度、显示拿到的数据
1.这是契约接口,里面定义了LoadData这个功能的mvp契约,model、view、presenter的接口。
public interface LoadDataContract {
interface Model {
/**
* 逻辑程序开始真正去拿数据
*
* @param callback
*/
void requestData(LoadDataModel.LoadDataCallback callback);
}
/**
* Activity, fragment, view都认为是view
*/
interface View {
/**
* 界面告诉用户开始加载数据
*
* @param msg
*/
void showLoadingProgress(String msg);
/**
* 界面显示数据获取成功
*
* @param text
*/
void showData(String text);
/**
* 界面显示数据获取失败
*
* @param text
*/
void showFailureMsg(String text);
}
interface Presenter {
/**
* 最有可能就是点击某个Button去拿数据
*/
void loadData();
/**
* 注意释放数据, 不要造成上下文泄露的情况, 比如Activity.onDestroy, Fragment.onDestroy
*/
void destroy();
}
2.再看presenter的实现类
public class LoadDataPresenter implements LoadDataContract.Presenter {
private static final String TAG = "LoadDataPresenter";
private LoadDataContract.View mView;
private LoadDataContract.Model mModel;
/**
* 这里实现了model实现类中定义的内部接口, 这样就可以将presenter注入model, 实现model依赖presenter
* 而presenter直接持有model顶级父类接口, 这样就可以将model注入presenter, 实现了presenter依赖model
* 从而实现了model和presenter的双向依赖
*/
private LoadDataModel.LoadDataCallback loadDataCallback = new LoadDataModel.LoadDataCallback() {
@Override
public void success(String taskId) {
mView.showData(taskId);
}
@Override
public void failure(String eMsg) {
mView.showFailureMsg(eMsg);
}
};
/**
* 这里直接将view注入presenter, 实现presenter依赖view的类图依赖, 那么要构造presenter实例对象,
* 也需要在view的实现类中去构造presenter, 这就可以实现viewpresenter的依赖,那么类图的双向依赖就达成了。
*/
public LoadDataPresenter(LoadDataContract.View view) {
mView = view;
mModel = new LoadDataModel();
}
@Override
public void loadData() {
// 界面告诉用户开始加载数据
mView.showLoadingProgress("加载数据中");
// 逻辑程序开始真正去拿数据
mModel.requestData(loadDataCallback);
}
@Override
public void destroy() {
mView = null;
loadDataCallback = null;
mModel = null;
}
}
这个类里面直接依赖了model、view的顶级接口,符合依赖倒置原则;
3.再来看model的实现
public class LoadDataModel implements LoadDataContract.Model {
@Override
public void requestData(LoadDataCallback callback) {
// 数据获取操作,如数据库查询、网络加载等
new Thread(new Runnable() {
@Override
public void run() {
try {
// 模拟耗时操作, 这10s时间内, 真是场景下, 你可以做任何事情来替换这10s休眠
// 比如: 请求网络数据, 大量数据排序, 查询数据库等
Thread.sleep(10000L);
// 请求完成, 获取到了数据
String data = "this is the data of requesting";
// 将获取的数据通过接口反馈出去
callback.success(data);
} catch (Exception e) {
e.printStackTrace();
// 获取数据失败的回调
callback.failure("the data request failed");
}
}
}).start();
}
/**
* 用于回传请求的数据的回传
*/
public interface LoadDataCallback {
/**
* 数据请求成功
*
* @param taskId
*/
void success(String taskId);
/**
* 数据请求失败
*
* @param eMsg
*/
void failure(String eMsg);
}
}
在model的实现类中,定义了一个内部接口,用来向外传递LoadData的状态,其实最主要是给presenter用的LoadDataCallback,这样就能将presenter注入model中,也就是实现了model也依赖presenter。
4.接着看view的实现,直接认为activity就是view
public class LoadDataViewActivity extends Activity implements LoadDataContract.View {
private static final String TAG = "LoadDataViewActivity";
private View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_load_data:
if (presenter != null) {
presenter.loadData();
}
break;
default:
break;
}
}
};
private LoadDataContract.Presenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 在view中初始化presenter, 并且注入view
presenter = new LoadDataPresenter(this);
findViewById(R.id.btn_load_data).setOnClickListener(listener);
}
@Override
public void showLoadingProgress(String msg) {
// 这里去运行ProgressView
Log.i(TAG, "you can show the progressView of the progress.");
}
@Override
public void showData(String text) {
// 这里去通过ListView显示你获取到的数据, 或者其他格式数据对应的View.
Log.i(TAG, "you can add data to the ListView and adapter.");
}
@Override
public void showFailureMsg(String text) {
// 这里显示获取失败的信息, 提示用户失败原因等.
Log.i(TAG, "you can show the failed text on the TextView.");
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.destroy();
presenter = null;
}
}
}
我们需要思考一个问题:
* <!--※用户是如何调用mvp模式的?
* <!--作为用户, 能接触到的只有view, 所以, 用户调用mvp模式应该是:
* <!--user操作view -> view <-> presenter <-> model