MVP開發(fā)模式及簡單架構(gòu)封裝

MVP開發(fā)模式

MVP圖解.png
  • Model: 主要用于業(yè)務(wù)操作,如:網(wǎng)絡(luò)請求谆焊,數(shù)據(jù)存儲等
  • Presenter: 主要用于邏輯處理惠桃,溝通 MV ,盡可能不包含Android的代碼
  • View: 主要用于規(guī)定界面的行為

優(yōu)點

  • 由于Presenter層的出現(xiàn),減少了View的邏輯操作和負(fù)擔(dān)辜王。這樣使 View 與 Model 之間耦合度低
  • 合理規(guī)劃的話劈狐,模塊分明,模塊復(fù)用率高呐馆,便于測試

缺點

  • 由于抽出了一層 Presenter肥缔,所以導(dǎo)致類和代碼有一定的增加
  • 如果不能進(jìn)行合理規(guī)劃的將會導(dǎo)致后期模塊雜亂,代碼冗余度高
  • 糾結(jié)將 服務(wù)廣播 放在何處
  • Presenter 會持有 View 的引用汹来,如果不進(jìn)行解綁會造成內(nèi)存泄露

說到最后续膳,其實 MVP 只是一種思想,沒有什么固定的代碼收班,固定的格式坟岔。因為多在實踐中,慢慢理解和多多總結(jié)摔桦。不過在開發(fā)前一定要做好項目分析規(guī)劃炮车,切忌立馬動手寫代碼。

MVP架構(gòu)封裝使用(登錄功能示范)

GitHub傳送門 --->MVPzzz(包含以下示例)

MVPzzz架構(gòu)封裝(未完成)

導(dǎo)包

JitPack

Step 1.
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • 在所有的 repositories 中都加入上面語句酣溃,否則無法導(dǎo)入成功
Step 2.
dependencies {
    compile 'com.github.KittoZZZ:MVPzzz:v.0.0.2'
}

包含契約類

目錄結(jié)構(gòu).png

1.建立View和Presenter的契約類

public class LoginContract {
    public interface ILoginView extends IBaseView {
        void LoginSuccess();

        void loginFail(String msg);
    }

    public interface ILoginPresenter {
        void toLogin(User user);
    }
}
  • View層接口必須繼承IBaseView

  • 可以很明顯的看出 View 和 Presenter 的關(guān)系

2.新建Model類(結(jié)合RxJava)

public class UserModel extends BaseModel {
    public Observable<String> toLogin(User user) {
        //Retrofit2
        String result = "fail";
        if ("zzz".equals(user.getAccount()) && "123".equals(user.getPassword())) {
            result = "success";
        }
        return Observable.just(result);
    }
}
  • BaseModel中并為沒有實現(xiàn)什么功能瘦穆,只是先留出,后序可能進(jìn)行一些更改
  • 只是用于模擬所以并沒有進(jìn)行網(wǎng)絡(luò)請求赊豌,寫死數(shù)據(jù)

3.新建Presenter類實現(xiàn)契約類中的P層接口

    public class LoginPresenter extends BasePresenter<LoginContract.ILoginView> implements ILoginContract.ILoginPresenter {
    @InjectModel
    private UserModel userModel;

    @Override
    public void toLogin(User user) {
        userModel.toLogin(user)
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String result) throws Exception {
                        if ("success".equals(result)) {
                            getView().LoginSuccess();
                        } else {
                            getView().loginFail(result);
                        }
                    }
                });
    }
}
  • getView() 方法用于調(diào)用 View 層的接口方法扛或,如:LoginSuccess(),loginFail()
  • 必須要寫B(tài)asePresenter泛型,LoginContract.ILoginView碘饼,否則無法調(diào)用它的方法
  • 使用注解 @InjectModel 進(jìn)行注入 Model 對象

4.新建Activity類實現(xiàn)契約類中的V層接口

public class LoginActivity extends BaseMvpActivity implements LoginContract.ILoginView {
    private Button btnLogin;
    private EditText etAccount;
    private EditText etPassword;

    @InjectPresenter
    private LoginPresenter loginPresenter;

    @Override
    protected int setContentView() {
        return R.layout.activity_login;
    }

    @Override
    protected void initView() {
        etAccount = this.findViewById(R.id.et_account);
        etPassword = this.findViewById(R.id.et_password);
        btnLogin = this.findViewById(R.id.btn_login);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String account = etAccount.getText().toString().trim();
                String password = etPassword.getText().toString().trim();
                User user = new User(account, password);
                loginPresenter.toLogin(user);
            }
        });
    }

    @Override
    protected void initData() {

    }

    @Override
    public void LoginSuccess() {
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }

    @Override
    public void loginFail(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}
  • 繼承 BaseMvpActivity 實現(xiàn) LoginContract.ILoginView 熙兔,重寫方法
  • 使用注解 @InjectPresenter 進(jìn)行注入 Presenter 對象

去除契約類(多個Presenter例子)

"根據(jù)設(shè)計原則 第一條 單一原則 LoginContract 這個類就把v和p耦合了 應(yīng)該把view接口 和 presenter分離開 作為兩個獨立接口 然后分別由view和presenter子類實現(xiàn)"

上面是來自大牛的評論,經(jīng)過思考確實將 V 層和 P 層的耦合了艾恼,所以思考將契約類去除的做法住涉。

假設(shè),在登錄界面中的記住密碼功能钠绍,需要在登錄成功后將賬號和密碼進(jìn)行保存在本地舆声。下次登錄的時候,直接讀取顯示在對應(yīng)的輸入框中柳爽。

目錄結(jié)構(gòu)改.png
  • 從目錄結(jié)構(gòu)中可以看出多了 UserDataPresenterDataModel 兩個關(guān)鍵的類
  • 主要是 V 層在初始化的時候調(diào)用 UserDataPresenter 去讀取媳握,登錄成功的時候保存數(shù)據(jù)
  • 由于只是個例子所以 DataModel 使用 SP 進(jìn)行數(shù)據(jù)的存儲
  • AppContext 只是用于在 M 層獲取 ApplicationContext 的工具類

LoginActivity部分代碼

public class LoginActivity extends BaseMvpActivity implements ILoginView, IUserDataView {
    ...
    
    @InjectPresenter
    private LoginPresenter loginPresenter;
    @InjectPresenter
    private UserDataPresenter userDataPresenter;

    ...

    @Override
    protected void initData() {
        btnLogin.setClickable(false);
        userDataPresenter.loadLastData();
    }

    @Override
    public void showLoginLoading() {
        Log.i("login", "showLoginLoading: 正在登陸");
    }

    @Override
    public void hideLoginLoading() {
        Log.i("login", "hideLoginLoading: 登錄結(jié)束");
    }

    @Override
    public void loginSuccess() {
        Log.i("login", "loginSuccess: 登錄成功");
        ...
        userDataPresenter.saveData(user);
    }

    @Override
    public void loginFail(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void saveDataSuccess() {
        Log.i("login", "saveDataSuccess: 保存數(shù)據(jù)成功");
        ...
    }

    @Override
    public void saveDataFail(String msg) {
        Log.i("login", "saveDataFail: 保存數(shù)據(jù)失敗 " + msg);
    }

    @Override
    public void readDataSuccess(User user) {
        Log.i("login", "readDataSuccess: 讀取數(shù)據(jù)成功");
        btnLogin.setClickable(true);
        if (!TextUtils.isEmpty(user.getAccount()) && !TextUtils.isEmpty(user.getPassword())) {
            etAccount.setText(user.getAccount());
            etPassword.setText(user.getPassword());
           // loginPresenter.toLogin(user);
        }
    }

    @Override
    public void readDataFail(String msg) {
        Log.i("login", "loadDataFail: 讀取數(shù)據(jù)失敗" + msg);
    }
}
  • 在界面打開的時候開始讀取保存本地的數(shù)據(jù),此時登錄按鈕為不可點擊
  • 讀取成功磷脯,將登錄按鈕設(shè)為可點擊蛾找,并且將數(shù)據(jù)現(xiàn)在輸入框中
  • 如果自動登錄的話,如果滿足條件(賬號密碼不為空)則直接調(diào)用 P 層去登錄

UserDataPresenter代碼

public class UserDataPresenter extends BasePresenter<IUserDataView> implements IUserDataPresenter {
    @InjectModel
    private DataModel dataModel;

    @Override
    public void saveData(User user) {
        dataModel.saveUserData(user)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String result) throws Exception {
                        if ("success".equals(result)) {
                            getView().saveDataSuccess();
                        } else {
                            getView().saveDataFail("保存失敗");
                        }
                    }
                });
    }

    @Override
    public void loadLastData() {
        dataModel.readUserData()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<User>() {
                    @Override
                    public void accept(User user) throws Exception {
                        getView().readDataSuccess(user);
                    }
                });
    }
}

DataModel代碼就不展示赵誓,主要理解思想打毛,詳情可以看 --->MVPzzz(包含示例)


  • 上面展示的是多個 Presenter 情況使用注解來實例化柿赊,這種方式同樣適用于多個 Model 的情況
  • 正是這個例子,讓我覺得V層與P層是一一對應(yīng)的形式存在幻枉,可以說是耦合的
  • 保留著契約類可以清晰的看出VP的關(guān)系碰声,可能是個人水平較低,我還是會選擇保留契約類的做法
  • 目前對于 MVP 開發(fā)模式的疑惑開始增多了.......
  • Fragment 的使用與 Activity 類似
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末展辞,一起剝皮案震驚了整個濱河市奥邮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罗珍,老刑警劉巖洽腺,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異覆旱,居然都是意外死亡蘸朋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門扣唱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藕坯,“玉大人,你說我怎么就攤上這事噪沙×侗耄” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵正歼,是天一觀的道長辐马。 經(jīng)常有香客問我,道長局义,這世上最難降的妖魔是什么喜爷? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮萄唇,結(jié)果婚禮上檩帐,老公的妹妹穿的比我還像新娘。我一直安慰自己另萤,他們只是感情好湃密,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仲墨,像睡著了一般勾缭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上目养,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機(jī)與錄音毒嫡,去河邊找鬼癌蚁。 笑死幻梯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的努释。 我是一名探鬼主播碘梢,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伐蒂!你這毒婦竟也來了煞躬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤逸邦,失蹤者是張志新(化名)和其女友劉穎恩沛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缕减,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡雷客,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了桥狡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搅裙。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖裹芝,靈堂內(nèi)的尸體忽然破棺而出部逮,到底是詐尸還是另有隱情,我是刑警寧澤嫂易,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布兄朋,位于F島的核電站,受9級特大地震影響炬搭,放射性物質(zhì)發(fā)生泄漏蜈漓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一宫盔、第九天 我趴在偏房一處隱蔽的房頂上張望融虽。 院中可真熱鬧,春花似錦灼芭、人聲如沸有额。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巍佑。三九已至,卻和暖如春寄悯,著一層夾襖步出監(jiān)牢的瞬間萤衰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工猜旬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留脆栋,地道東北人倦卖。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像椿争,于是被迫代替她去往敵國和親怕膛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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