當類與類直接依賴細節(jié)藏古,那他們之間就有直接的耦合遗嗽,當具體實現(xiàn)需要變化時,意味著需要修改依賴者的代碼刻坊,這限制了系統(tǒng)的可擴展性蝗茁。
MVP的全稱是Model醋虏、View、Presenter哮翘,將整個應用分為三層颈嚼。
View層:視圖層,包含界面相關(guān)的功能饭寺,例如Activity阻课,F(xiàn)ragment叫挟,View,Adapter等限煞,該層專注于用戶交互抹恳,實現(xiàn)設(shè)計師給出的界面,動畫等交互效果署驻。View層一般會持有Presenter層的引用奋献,或者依賴注入(如dagger)的方式獲得presenter實例,并將非UI的邏輯操作委托給Presenter旺上。
Presenter層:邏輯控制層瓶蚂,充當中間人的角色,用來隔離View層和model層宣吱,該層是通過從View層剝離控制邏輯部分而形成窃这,主要負責View層和model層的控制和交互。例如接受View層的網(wǎng)絡(luò)數(shù)據(jù)的加載請求征候,并分發(fā)給model處理杭攻,同時監(jiān)聽model層的處理結(jié)果,最終將其反饋給View層從而實現(xiàn)界面的刷新
model層封裝各種數(shù)據(jù)來源倍奢,例如遠程網(wǎng)絡(luò)數(shù)據(jù)朴上,本地數(shù)據(jù)庫數(shù)據(jù)等垒棋,對Presenter層提供簡單易用的接口
Presenter和View以及Model的交互都是通過接口進行的卒煞;通常View與Presenter是一對一的,當然叼架,復雜的View可能需要多個Presenter來共同處理畔裕。
關(guān)于android的MVP模式其實一直沒有一個統(tǒng)一的實現(xiàn)方式,不同的人由于個人理解的不同乖订,進而產(chǎn)生了很多不同的實現(xiàn)方式
MVP的好處
使用MVP組織代碼架構(gòu)扮饶,并對代碼實施分層管理,有以下好處:
1.如果界面發(fā)生變化乍构,甚至是全新改版甜无,只需修改對應的View即可,Presenter和Model層無需改動哥遮。
2.如果業(yè)務邏輯或者數(shù)據(jù)獲取方式放生變化岂丘,只需修改model層
3.如果控制邏輯發(fā)生變化,只需修改Presenter層眠饮。
4.Presenter層和View層以及model層的交互都是基于接口實現(xiàn)的奥帘,這有助于對Presenter進行單元測試,同時由于是面向接口編程仪召,只需要事先定義好接口寨蹋,每一層的實現(xiàn)都可以交由不同的開發(fā)人員并行實現(xiàn)松蒜,最終再一起連調(diào),都能明顯的加快某一功能的開發(fā)進度已旧。
5.團隊的新成員拿到項目的代碼秸苗,能夠很容易的讀懂現(xiàn)有的邏輯,快速上手运褪。
6.如果你正在開發(fā)一個對外的sdk难述,根據(jù)市場需求,需要提供帶UI版本和不帶UI的純接口版本吐句,那么使用MVP模式胁后,將UI部分代碼放在View層,將接口部分代碼放在model層嗦枢,打包的時候可以輕松實現(xiàn)是否將View層打包進去攀芯,從而避免純借口版本混入UI相關(guān)的代碼。
7.UI畫好可以先寫UI文虏,接口寫好可以先寫業(yè)務邏輯
MVP存在的問題
1.增加代碼類的數(shù)量
2.由于進行了三層劃分侣诺,函數(shù)的調(diào)用棧變深,如果開發(fā)人員沒能非常清楚的了解哪一層具體該負責哪些功能氧秘,那么可能存在因為層次職責辨認不清等原因?qū)е虏煌瑢又g的代碼亂入年鸳,從而沒能達到MVP充分解耦各層的目的
使用
presenter:SplashPresenter(接口)---具體p層的實現(xiàn)SplashPresenterImpl(通過構(gòu)造,持有View層的引用)
View: SplashView(接口)--具體View的實現(xiàn)SplashActivity--extends BaseActivity implements SplashView
View層需要持有p層的引用--因此SplashActivity中要創(chuàng)建篇p層
SplashPresenter presenter = new SplashPresenterImpl(this);
//檢查是否登錄過
presenter.checkLoginState();
調(diào)用p層--p層會調(diào)用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 {
/**
* 獲取到登錄狀態(tài)之后 后續(xù)操作
* @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) {
//當動畫結(jié)束的時候 開啟login頁面
startActivity(LoginActivity.class,true);
}
});
}
}
}
案例2
public interface ContactPresenter {
/**
* 初始化聯(lián)系人數(shù)據(jù)
*/
void initContact();
/**
* 聯(lián)網(wǎng)更新聯(lián)系人
*/
void updateContactFromServer();
/**
* 刪除聯(lián)系人
*/
void deleteContact(String username);
}
public class ContactPresenterImpl implements ContactPresenter {
private ContactView contactView;
public ContactPresenterImpl(ContactView contactView) {
this.contactView = contactView;
}
@Override
public void initContact() {
//先從數(shù)據(jù)庫獲取聯(lián)系人數(shù)據(jù)
List<String> contacts = DbUtils.initContacts(EMClient.getInstance().getCurrentUser());
//通知view更新數(shù)據(jù)
contactView.onInitContact(contacts);
//聯(lián)網(wǎng)獲取最新數(shù)據(jù)
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);
}
});
//排序之后的集合放到數(shù)據(jù)庫中
DbUtils.updateContactsDB(EMClient.getInstance().getCurrentUser(),contactsFromServer);
//沒走異常 說明聯(lián)系人數(shù)據(jù)更新成功
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
contactView.onUpdateContact(true,contactsFromServer,null);
}
});
} catch (final HyphenateException e) {
e.printStackTrace();
//如果走異常 說明聯(lián)系人數(shù)據(jù)更新失敗
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) {
//跳轉(zhuǎn)到聊天界面
Intent intent = new Intent(getContext(),ChatActivity.class);
intent.putExtra("username",username);
startActivity(intent);
}
@Override
public boolean onLongClick(View v, final String username) {
//刪除聯(lián)系人
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() {
//刷新聯(lián)系人
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){
//收到刪除聯(lián)系人的事件就更新列表
presenter.updateContactFromServer();
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}