当类与类直接依赖细节,那他们之间就有直接的耦合,当具体实现需要变化时,意味着需要修改依赖者的代码,这限制了系统的可扩展性。
MVP的全称是Model、View、Presenter,将整个应用分为三层。
View层:视图层,包含界面相关的功能,例如Activity,Fragment,View,Adapter等,该层专注于用户交互,实现设计师给出的界面,动画等交互效果。View层一般会持有Presenter层的引用,或者依赖注入(如dagger)的方式获得presenter实例,并将非UI的逻辑操作委托给Presenter。
Presenter层:逻辑控制层,充当中间人的角色,用来隔离View层和model层,该层是通过从View层剥离控制逻辑部分而形成,主要负责View层和model层的控制和交互。例如接受View层的网络数据的加载请求,并分发给model处理,同时监听model层的处理结果,最终将其反馈给View层从而实现界面的刷新
model层封装各种数据来源,例如远程网络数据,本地数据库数据等,对Presenter层提供简单易用的接口
Presenter和View以及Model的交互都是通过接口进行的;通常View与Presenter是一对一的,当然,复杂的View可能需要多个Presenter来共同处理。
关于android的MVP模式其实一直没有一个统一的实现方式,不同的人由于个人理解的不同,进而产生了很多不同的实现方式
MVP的好处
使用MVP组织代码架构,并对代码实施分层管理,有以下好处:
1.如果界面发生变化,甚至是全新改版,只需修改对应的View即可,Presenter和Model层无需改动。
2.如果业务逻辑或者数据获取方式放生变化,只需修改model层
3.如果控制逻辑发生变化,只需修改Presenter层。
4.Presenter层和View层以及model层的交互都是基于接口实现的,这有助于对Presenter进行单元测试,同时由于是面向接口编程,只需要事先定义好接口,每一层的实现都可以交由不同的开发人员并行实现,最终再一起连调,都能明显的加快某一功能的开发进度。
5.团队的新成员拿到项目的代码,能够很容易的读懂现有的逻辑,快速上手。
6.如果你正在开发一个对外的sdk,根据市场需求,需要提供带UI版本和不带UI的纯接口版本,那么使用MVP模式,将UI部分代码放在View层,将接口部分代码放在model层,打包的时候可以轻松实现是否将View层打包进去,从而避免纯借口版本混入UI相关的代码。
7.UI画好可以先写UI,接口写好可以先写业务逻辑
MVP存在的问题
1.增加代码类的数量
2.由于进行了三层划分,函数的调用栈变深,如果开发人员没能非常清楚的了解哪一层具体该负责哪些功能,那么可能存在因为层次职责辨认不清等原因导致不同层之间的代码乱入,从而没能达到MVP充分解耦各层的目的
使用
presenter:SplashPresenter(接口)---具体p层的实现SplashPresenterImpl(通过构造,持有View层的引用)
View: SplashView(接口)--具体View的实现SplashActivity--extends BaseActivity implements SplashView
View层需要持有p层的引用--因此SplashActivity中要创建篇p层
SplashPresenter presenter = new SplashPresenterImpl(this);
//检查是否登录过
presenter.checkLoginState();
调用p层--p层会调用View层
Presenter层
public interface SplashPresenter {
/**
* 检查之前是否登陆过
*/
void checkLoginState();
}
public class SplashPresenterImpl implements SplashPresenter {
private SplashView splashView;
public SplashPresenterImpl(SplashView splashView) {
this.splashView = splashView;
}
@Override
public void checkLoginState() {
if(EMClient.getInstance().isLoggedInBefore()&&EMClient.getInstance().isConnected()){
splashView.onGetLoginState(true);
}else{
splashView.onGetLoginState(false);
}
}
}
View层
public interface SplashView {
/**
* 获取到登录状态之后 后续操作
* @param isLoginBefore 是否登录过 如果是true说明登录过
*/
void onGetLoginState(boolean isLoginBefore);
}
public class SplashActivity extends BaseActivity implements SplashView {
@InjectView(R.id.iv_splash)
ImageView ivSplash;
private SplashPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ButterKnife.inject(this);
SplashPresenter presenter = new SplashPresenterImpl(this);
//检查是否登录过
presenter.checkLoginState();
}
@Override
public void onGetLoginState(boolean isLoginBefore) {
if(isLoginBefore){
//登录过 直接打开MainActivity
startActivity(MainActivity.class,true);
}else{
//没登录 打开登录界面
ObjectAnimator animator = ObjectAnimator.ofFloat(ivSplash,"alpha",0,1);
animator.setDuration(2000);
animator.start();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//当动画结束的时候 开启login页面
startActivity(LoginActivity.class,true);
}
});
}
}
}
案例2
public interface ContactPresenter {
/**
* 初始化联系人数据
*/
void initContact();
/**
* 联网更新联系人
*/
void updateContactFromServer();
/**
* 删除联系人
*/
void deleteContact(String username);
}
public class ContactPresenterImpl implements ContactPresenter {
private ContactView contactView;
public ContactPresenterImpl(ContactView contactView) {
this.contactView = contactView;
}
@Override
public void initContact() {
//先从数据库获取联系人数据
List<String> contacts = DbUtils.initContacts(EMClient.getInstance().getCurrentUser());
//通知view更新数据
contactView.onInitContact(contacts);
//联网获取最新数据
updateContactFromServer();
}
@Override
public void updateContactFromServer() {
ThreadUtils.runOnSubThread(new Runnable() {
@Override
public void run() {
try {
final List<String> contactsFromServer = EMClient.getInstance().contactManager().getAllContactsFromServer();
Collections.sort(contactsFromServer, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
//排序之后的集合放到数据库中
DbUtils.updateContactsDB(EMClient.getInstance().getCurrentUser(),contactsFromServer);
//没走异常 说明联系人数据更新成功
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
contactView.onUpdateContact(true,contactsFromServer,null);
}
});
} catch (final HyphenateException e) {
e.printStackTrace();
//如果走异常 说明联系人数据更新失败
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
contactView.onUpdateContact(false,null,e.getMessage());
}
});
}
}
});
}
@Override
public void deleteContact(final String username) {
ThreadUtils.runOnSubThread(new Runnable() {
@Override
public void run() {
try {
EMClient.getInstance().contactManager().deleteContact(username);
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
contactView.ondeleteContact(true,null);
}
});
} catch (final HyphenateException e) {
e.printStackTrace();
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
contactView.ondeleteContact(false,e.getMessage());
}
});
}
}
});
}
}
public interface ContactView {
void onInitContact(List<String> contacts);
void onUpdateContact(boolean isUpdateSuccess,List<String> contacts,String errorMsg);
void ondeleteContact(boolean isDeleteSuccess,String errorMsg);
}
public class ContactFragment extends BaseFragment implements ContactView {
private ContactLayout contactLayout;
private ContactPresenter presenter;
private ContactAdapter adapter;
public ContactFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_contact, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
contactLayout = (ContactLayout) view.findViewById(R.id.contactlayout);
presenter = new ContactPresenterImpl(this);
presenter.initContact();
}
@Override
public void onInitContact(List<String> contacts) {
if(adapter == null){
adapter = new ContactAdapter(contacts);
}
adapter.setOnItemClickListener(new ContactAdapter.OnItemClickListener() {
@Override
public void onclick(View v, String username) {
//跳转到聊天界面
Intent intent = new Intent(getContext(),ChatActivity.class);
intent.putExtra("username",username);
startActivity(intent);
}
@Override
public boolean onLongClick(View v, final String username) {
//删除联系人
Snackbar.make(contactLayout,"真的要删除"+username+"吗?",Snackbar.LENGTH_SHORT).
setAction("确定", new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.deleteContact(username);
}
}).show();
return false;
}
});
contactLayout.setAdapter(adapter);
contactLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//刷新联系人
presenter.updateContactFromServer();
}
});
}
@Override
public void onUpdateContact(boolean isUpdateSuccess, List<String> contacts, String errorMsg) {
contactLayout.setRefreshing(false);
if(isUpdateSuccess){
adapter.setContacts(contacts);
adapter.notifyDataSetChanged();
}else{
ToastUtils.showToast(getContext(),"刷新失败");
}
}
@Override
public void ondeleteContact(boolean isDeleteSuccess, String errorMsg) {
if(isDeleteSuccess){
ToastUtils.showToast(getContext(),"删除成功");
}else{
ToastUtils.showToast(getContext(),"删除失败");
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onContactChanged(ContactChangeEvent event){
//收到删除联系人的事件就更新列表
presenter.updateContactFromServer();
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}