内容简介:MVP其实我接触已经三年了,本文的实践也是在17年实践的,今天放出来也是为了分享给需要这快东西的同学,面试足够了,好东西不能雪藏。MVC:全称:Model - View - Controller,它是于1970年的时候有TrygveReenskaug在Smalltalk-80系统上首次提出。起初并不是MVC,而是MVCE,也就是Model - View - Controller后面多了一个Editor。但是当时提出MVC的理念和现在的理念大致是一样的。都是为了讲数据模型和视图层分离开来。其实MVC是一种框
MVP其实我接触已经三年了,本文的实践也是在17年实践的,今天放出来也是为了分享给需要这快东西的同学,面试足够了,好东西不能雪藏。
1 起源
MVC:全称:Model - View - Controller,它是于1970年的时候有TrygveReenskaug在Smalltalk-80系统上首次提出。起初并不是MVC,而是MVCE,也就是Model - View - Controller后面多了一个Editor。但是当时提出MVC的理念和现在的理念大致是一样的。都是为了讲数据模型和视图层分离开来。
其实MVC是一种框架模式,而非设计模式,GOF把MVC看做是3中设计模式:《观察者模式》、《策略模式》,《组合模式》三者的合体。其核心是《观察者模式》。
前期的mvc主要用在桌面程序,脱离web的编程年代!
2 理解
MVC 主要分三层,模型——视图——控制器。为了使得程序的各个部分分离降低耦合性,我们对代码的结构进行了划分。 他们的通信方式也如上图所示,即View层触发操作通知到业务层完成逻辑处理,业务层完成业务逻辑之后通知Model层更新数据,数据更新完之后通知View层展现。
Controller充当观察者角色,Model和View是被观察者角色,两者任意一方发生变化则通知Controller去控制对方。
3 优点
3.1 三层解耦,将数据和view隔离,通过Controller 去做数据和视图的交互桥梁!
3.2 满足client程序设计需求,易维护和扩展!
##4 缺点
4.1 Controller 职责过于繁重 4.2 不满足网络编程下的异步请求造成的 Model和view的断层,所谓的页面抖动! 4.3 数据和视图任意一方发生变化,则在不想更改对方的情况下,需要Controller 做处理,职责收敛太严重!
5 展望
有没有一种架构模式,减轻控制器的职责。能在Model和view之间自我通讯,无需中间人桥接,并满足在web编程时期的异步带去的各种不同步问题?,那么mvp和MVVM就先后出现了, 下节继续学习MVP。
其实无需太多代码,只要一幅图就能看懂!
MVP
作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。 在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,即View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
MVP模式的核心:
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。
这就是MVP模式,现在这样的话,Activity的工作的简单了,只用来响应生命周期,其他工作都丢到Presenter中去完成。从上图可以看出,Presenter是Model和View之间的桥梁,为了让结构变得更加简单,View并不能直接对Model进行操作,这也是MVP与MVC最大的不同之处
MVP实践
基础模型,所谓单向数据流和脚手架模式已经成了前端设计工程的主流。 接下来来个模版。
1 Model
模型,不仅代表者视图模型,还有数据模型。主要做模版功能,下面展示通用的视图和数据模型
view 模型
/**
* Created by Tamic on 2018-01-08.
*/
public abstract class ModelActivity<V extends IBaseView, P extends IPresenter> extends AppCompatActivity implements IBaseView {
protected P presenter;
protected V view;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
presenter.attachView(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachView(false);
}
protected abstract P createPresenter();
}
复制代码
DATA 模型
/**
* Created by LIUYONGKUI726 on 2017-12-28.
*/
public abstract class BaseModel<T, E> {
abstract public T getApi();
abstract public E getBean(String... var);
}
复制代码
P层
Presenter主要处理业务逻辑,把事件的流程抽象出一个个接口。下面是一个base类,防止内存泄漏,和activity关联了,生命周期同步起来。
接口类:
public interface IPresenter<V extends IBaseView> {
void start();
void attachView(V view);
void detachView(boolean retainInstance);
}
复制代码
抽象的实现类
/**
* Created by Tamic on 2018-01-08.
*/
public abstract class BasePresenter<V extends IBaseView> implements IPresenter<V> {
private WeakReference<V> viewRef;
/**
* 界面创建,Presenter与界面取得联系
*/
@Override
public void attachView(V view) {
viewRef = new WeakReference<V>(view);
}
/**
* 获取界面的引用
*/
protected V getView() {
return viewRef == null ? null : viewRef.get();
}
/**
* 判断界面是否销毁
*/
protected boolean isViewAttached() {
return viewRef != null && viewRef.get() != null;
}
/**
* 界面销毁,Presenter与界面断开联系
*/
@Override
public void detachView(boolean retainInstance) {
if (viewRef != null) {
viewRef.clear();
viewRef = null;
}
}
}
复制代码
3 View
View主要包含了view操作的共同方法抽离。下班展示了一个基础的 BaseView ,用于讲Presenter和view 绑定。
public interface IBaseView<T extends IPresenter> {
void setPresenter(T presenter);
}
复制代码
实现脚手架
本次我做一个登陆模块,来演示我们怎么最优雅的使用MVP。
登陆肯定需要网络调用,我选用了Retrofit作为请求端。下面是登录API。
1 API
/**
* Created by Tamic on 2017-12-28.
*/
public class LoginApi<T> extends Api {
/**
* A dummy authentication store containing known user names and passwords.
* TODO: remove after connecting to a real authentication system.
*/
private static final String[] DUMMY_CREDENTIALS = new String[] {
"foo@example.com:hello", "bar@example.com:world"
};
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
private UserLoginTask mAuthTask = null;
@Override
public Call<UserBean> getData(String... var1) {
//模拟请求
mAuthTask = new UserLoginTask(var1[0], var1[1]);
return (T) mAuthTask.execute();*/
Map<String, String> hashMap = new HashMap<>();
hashMap.put("ip", "21.22.11.33");
return create(LoginApiService.class).login(hashMap);
}
public interface LoginApiService {
/**
* http://ip.taobao.com/service/getIpInfo.php?ip=21.22.11.33
*/
@GET("service/getIpInfo.php")
@Headers({"Accept: application/json"})
Call<UserBean> login(
@QueryMap Map<String, String> maps);
}
}
复制代码
2 LoginModel
/**
* Created by Tamic on 2017-12-28.
*/
public class LoginModel extends BaseModel<LoginApi, UserBean> {
public LoginModel() {
}
@Override
public LoginApi getApi() {
return new LoginApi<>();
}
@Override
public UserBean getBean(String... var) {
//return (LoginBean) getApi().getData(var);
return null;
}
public Call<UserBean> getFlowable(String... var) {
return getApi().getData(var);
}
}
复制代码
2 LoginPresenter
负责登录的请求和菊花的控制,并映射成模型。
/**
* Created by Tamic on 2018-01-03.
*/
public class LoginPresenter extends BasePresenter<LoginContract.View> {
private LoginModel model;
public LoginPresenter() {
model = new LoginModel();
}
public UserBean login(String... var) throws IOException {
getView().showLoading();
if (!isViewAttached()) {
return null;
}
return model.getFlowable(var).execute().body();
}
public void login(final CallBack callBack, String... var) {
if (!isViewAttached()) {
return;
}
getView().showLoading();
model.getFlowable(var).enqueue(new Callback<UserBean>() {
@Override
public void onResponse(Call<UserBean> call, Response<UserBean> response) {
getView().dismissLoading();
callBack.onSuccess(response.code(), response.message(), response.body());
callBack.onFinal();
}
@Override
public void onFailure(Call<UserBean> call, Throwable t) {
getView().dismissLoading();
callBack.onFailure(-100, t.getMessage());
callBack.onFinal();
}
});
}
@Override
public void start() {
}
}
复制代码
3 Contract
契约是双方的约定协议的意思,MVP的契约是指对View和Presenter的约定,下面是一个对登录业务的约定。
/**
* This specifies the contract between the view and the presenter.
* Created by Tamic on 2018-01-04.
*/
public interface LoginContract {
interface View extends IBaseView {
void showLoading();
void dismissLoading();
}
interface LoginPresenter extends IPresenter {
}
}
复制代码
4 Activtiy
到了大家最熟悉的Activtiy了,我们用`LoginActivity` 实现View模版,并实现View接口,并满足契约,接下来代码就是这么优雅。
/**
* A login screen that offers login via email/password.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class LoginActivity extends ModelActivity<LoginContract.View, LoginPresenter> implements LoginContract.View {
/**
* Id to identity READ_CONTACTS permission request.
*/
private static final int REQUEST_READ_CONTACTS = 0;
// UI references.
private AutoCompleteTextView mEmailView;
private EditText mPasswordView;
private View mProgressView;
private View mLoginFormView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Set up the login form.
mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin();
return true;
}
return false;
}
});
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});
mLoginFormView = findViewById(R.id.login_form);
mProgressView = findViewById(R.id.login_progress);
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
private void attemptLogin() {
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
final String email = mEmailView.getText().toString();
final String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
focusView.requestFocus();
} else {
presenter.login(new CallBack<UserBean>() {
@Override
public void onSuccess(int code, String msg, UserBean data) {
showToast(data.getData().getCountry());
}
@Override
public void onFailure(int code, String msg) {
showToast(msg);
}
}, email, password);
presenter.start();
/* By Rxjava
presenter.login(email, password)
.subscribeOn(Schedulers.newThread())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserBean>() {
@Override
public void accept(UserBean userBean) throws Exception {
if(userBean != null) {
showToast(userBean.getData().getCity());
showUIProgress(false);
}
}
});*/
}
}
private boolean isEmailValid(String email) {
//TODO: Replace this with your own logic
return email.contains("@");
}
private boolean isPasswordValid(String password) {
//TODO: Replace this with your own logic
return password.length() > 4;
}
private void showToast(final String data) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
}
private void showUIProgress(final boolean show) {
runOnUiThread(new Runnable() {
@Override
public void run() {
showProgress(show);
}
});
}
private void resfreshUI(final String content) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mPasswordView.setError(content);
mPasswordView.requestFocus();
}
});
}
/**
* Shows the progress UI and hides the login form.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
private void showProgress(final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
@Override
public void showLoading() {
showUIProgress(true);
}
@Override
public void dismissLoading() {
showUIProgress(false);
}
@Override
public void setPresenter(IPresenter presenter) {
this.presenter = (LoginPresenter) presenter;
}
}
复制代码
以上所述就是小编给大家介绍的《Android MVP 最佳实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- vue项目实践004~~~一篮子的实践技巧
- HBase实践 | 阿里云HBase数据安全实践
- Spark 实践:物化视图在 SparkSQL 中的实践
- Spark实践|物化视图在 SparkSQL 中的实践
- HBase实践 | 数据人看Feed流-架构实践
- Kafka从上手到实践-实践真知:搭建Zookeeper集群
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!