MVP模式

當類與類直接依賴細節(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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丸相,一起剝皮案震驚了整個濱河市搔确,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灭忠,老刑警劉巖膳算,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弛作,居然都是意外死亡涕蜂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門映琳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來机隙,“玉大人,你說我怎么就攤上這事萨西∮新梗” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵原杂,是天一觀的道長印颤。 經(jīng)常有香客問我,道長穿肄,這世上最難降的妖魔是什么年局? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任际看,我火速辦了婚禮,結(jié)果婚禮上矢否,老公的妹妹穿的比我還像新娘仲闽。我一直安慰自己,他們只是感情好僵朗,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布赖欣。 她就那樣靜靜地躺著,像睡著了一般验庙。 火紅的嫁衣襯著肌膚如雪顶吮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天粪薛,我揣著相機與錄音悴了,去河邊找鬼。 笑死违寿,一個胖子當著我的面吹牛湃交,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藤巢,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼搞莺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掂咒?” 一聲冷哼從身側(cè)響起才沧,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俏扩,沒想到半個月后糜工,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弊添,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡录淡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了油坝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫉戚。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖澈圈,靈堂內(nèi)的尸體忽然破棺而出彬檀,到底是詐尸還是另有隱情,我是刑警寧澤瞬女,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布窍帝,位于F島的核電站,受9級特大地震影響诽偷,放射性物質(zhì)發(fā)生泄漏坤学。R本人自食惡果不足惜疯坤,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望深浮。 院中可真熱鬧压怠,春花似錦、人聲如沸飞苇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽布卡。三九已至雨让,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忿等,已是汗流浹背宫患。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留这弧,地道東北人娃闲。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像匾浪,于是被迫代替她去往敵國和親皇帮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容