MVP架構(gòu)


目錄

1)MVP簡(jiǎn)介
2)MVP實(shí)例


1)MVP簡(jiǎn)介

MVP模式將Activity中的業(yè)務(wù)邏輯全部剝離出來(lái)歇父,Activity只做UI部分的處理盼忌,所有業(yè)務(wù)邏輯層由Presenter層去處理。

分層 說(shuō)明
View Activity,Fragment绊茧,負(fù)責(zé)處理UI
Presenter 業(yè)務(wù)邏輯層铝宵,既能調(diào)用UI,又能請(qǐng)求數(shù)據(jù)源
Model 純業(yè)務(wù)數(shù)據(jù)源的接口與實(shí)現(xiàn)

M層與V層無(wú)法直接通信华畏,需要通過(guò)P層處理鹏秋。

MVP

2)MVP實(shí)例

實(shí)現(xiàn)一個(gè)登錄請(qǐng)求

假設(shè)我們來(lái)實(shí)現(xiàn)一個(gè)登錄請(qǐng)求。


工程結(jié)構(gòu)

-定義實(shí)體Bean

實(shí)體Bean
public class LoginReq {
    private String username;
    private String password;
    ...
}

public class LoginResponse {
    private String retcode;
    private String retinfo;
    private UserData userData;
    ...
}
public class UserData {
    private String name;
    private String age;
    ...
}

-Model層

Model層
  • Callback接口, 是Model給Presenter反饋數(shù)據(jù)信息的載體亡笑,需要在Callback中定義數(shù)據(jù)的各種反饋狀態(tài)
//通過(guò)泛型T表示接受任意類(lèi)型
public interface MvpCallback<T> {
    /**
     * 數(shù)據(jù)請(qǐng)求成功
     * @param data 請(qǐng)求到的數(shù)據(jù)
     */
    void onSuccess(T data);

    /**
     *  使用網(wǎng)絡(luò)API接口請(qǐng)求方式時(shí)侣夷,雖然已經(jīng)請(qǐng)求成功但是由
     *  于{@code msg}的原因無(wú)法正常返回?cái)?shù)據(jù)。
     */
    void onFail(T data);

    /**
     * 請(qǐng)求數(shù)據(jù)失敗仑乌,指在請(qǐng)求網(wǎng)絡(luò)API接口請(qǐng)求方式時(shí)百拓,出現(xiàn)無(wú)法聯(lián)網(wǎng)、
     * 缺少權(quán)限晰甚,內(nèi)存泄露等原因?qū)е聼o(wú)法連接到請(qǐng)求數(shù)據(jù)源衙传。
     */
    void onError();

    /**
     * 當(dāng)請(qǐng)求數(shù)據(jù)結(jié)束時(shí),無(wú)論請(qǐng)求結(jié)果是成功厕九,失敗或是拋出異常都會(huì)執(zhí)行此方法給用戶(hù)做處理蓖捶,通常做網(wǎng)絡(luò)
     * 請(qǐng)求時(shí)可以在此處隱藏“正在加載”的等待控件
     */
    void onComplete();
}
  • 登錄業(yè)務(wù)接口,這里采用了ARouter-IProvider的服務(wù)依賴(lài)注入的方式去解耦服務(wù)止剖,如不需要可不用繼承IProvider
public interface ILoginBiz extends IProvider {

    //登錄操作
    void login(LoginReq req, MvpCallback mvpCallback);

    //存儲(chǔ)登錄信息
    void saveUserdata(UserData userdata);
}
  • 登錄業(yè)務(wù)實(shí)現(xiàn)腺阳,
@Route(path = "/service/login", name="登錄服務(wù)")
public class LoginBiz implements ILoginBiz {

    @Override
    public void login(LoginReq req, final MvpCallback mvpCallback) {
        Map<String,String> param = new HashMap<String, String>();
        //調(diào)用公共組件庫(kù)的OkHttpUtil進(jìn)行網(wǎng)絡(luò)API請(qǐng)求
        OkHttpUtil.sendRequestPost(param, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //模擬2秒休眠
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //回調(diào)onComplete
                mvpCallback.onComplete();
                //模擬response數(shù)據(jù)
                UserData userData = new UserData("涂高峰","18");
                LoginResponse loginResponse = new LoginResponse("000000","登錄成功",userData);
                //回調(diào)onSuccess
                mvpCallback.onSuccess(loginResponse.getUserData());
            }
        });
    }

    @Override
    public void saveUserdata(UserData userdata) {

    }

    @Override
    public void init(Context context) {
    }
}

-View層落君,View接口是Activity層和Presenter層的中間層,它用于根據(jù)具體業(yè)務(wù)需要亭引,為Presenter層提供調(diào)用Activity中具體UI操作的方法绎速。

View層
  • IMvpView,抽象出的公共View
public interface IMvpView {

    /**
     * 顯示正在加載view
     */
    void showLoading();

    /**
     * 關(guān)閉正在加載view
     */
    void hideLoading();

    /**
     * 顯示提示
     * @param msg
     */
    void showToast(String msg);
}
  • ILoginView焙蚓,登錄業(yè)務(wù)View纹冤,繼承了IMvpView。
public interface ILoginView extends IMvpView {

    /**
     * 登錄請(qǐng)求成功提示
     */
    void loginSuccess(UserData userData);

    /**
     * 登錄請(qǐng)求失敗提示
     */
    void loginFail(String text);
}

-Presenter層购公,具體的業(yè)務(wù)邏輯處理Java類(lèi)萌京,不含任何Android-API操作,負(fù)責(zé)請(qǐng)求數(shù)據(jù)源宏浩,并對(duì)數(shù)據(jù)源反饋?zhàn)鎏幚怼?/p>

Presenter層
  • MvpPresenter知残,抽象出公共的Presenter,
public class MvpPresenter<V extends IMvpView> {

    /**
     * 綁定的view
     */
    private V mvpView;

    /**
     * 綁定view比庄,一般在初始化中調(diào)用該方法
     */
    public void attachView(V view){
        this.mvpView = view;
    }

    /**
     * 斷開(kāi)view求妹,一般在onDestroy中調(diào)用
     * 對(duì)于Activity異常銷(xiāo)毀導(dǎo)致mvpView空指針
     * 采用attachView和detachView,將view與activity生命周期綁定
     */
    public void detachView(){
        this.mvpView = null;
    }

    /**
     * 是否與View建立連接
     * 每次調(diào)用業(yè)務(wù)請(qǐng)求的時(shí)候都要出先調(diào)用方法檢查是否與View建立連接
     */
    public boolean isViewAttached(){
        return this.mvpView != null;
    }

    /**
     * 獲取連接的view
     */
    public V getView(){
        return this.mvpView;
    }

    /**
     * 獲取連接的view名稱(chēng)
     */
    public String getViewName(){
        return this.mvpView.getClass().getSimpleName();
    }
}
  • LoginPresenter佳窑,登錄業(yè)務(wù)Presenter制恍,繼承了MvpPresenter。
public class LoginPresenter extends MvpPresenter<ILoginView> {
    //依賴(lài)注入發(fā)現(xiàn)服務(wù)
    @Autowired(name = "/service/login")
    LoginBiz loginBiz;

    public LoginPresenter() {
//        this.loginBiz = new LoginBiz();
        //依賴(lài)注入
        ARouter.getInstance().inject(this);
    }

    public void login(LoginReq req){

        if (!isViewAttached()){
            //如果沒(méi)有View引用就不加載數(shù)據(jù)
            return;
        }

        getView().showLoading();

        loginBiz.login(req, new MvpCallback() {
            @Override
            public void onSuccess(final Object data) {
                if (isViewAttached()){
                    MainLooper.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            getView().loginSuccess((UserData) data);
                        }
                    });
                }
            }

            @Override
            public void onFail(Object data) {

            }

            @Override
            public void onError() {

            }

            @Override
            public void onComplete() {
                if (isViewAttached()){
                    MainLooper.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            getView().hideLoading();
                        }
                    });
                }
            }
        });
    }
}

-Activity,登錄業(yè)務(wù)Activity神凑,實(shí)現(xiàn)了ILoginView

public class MainActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{

    private LoginPresenter loginPresenter;
    private TextView txt;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txt = findViewById(R.id.txt);
        progressBar = findViewById(R.id.progressBar);
        findViewById(R.id.success_btn).setOnClickListener(this);

        //初始化Presenter
        loginPresenter = new LoginPresenter();
        //綁定View引用
        loginPresenter.attachView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //斷開(kāi)View引用
        loginPresenter.detachView();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.success_btn:
                LoginReq loginReq = new LoginReq("tgf","123");
                loginPresenter.login(loginReq);
                break;
        }
    }

    @Override
    public void showLoading() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        progressBar.setVisibility(View.GONE);
    }

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

    @Override
    public void loginSuccess(UserData userData) {
        txt.setText("姓名: "+userData.getName() + "年齡"+ userData.getAge());
    }

    @Override
    public void loginFail(String text) {
    }
}

-其他工具類(lèi)

public class MainLooper extends Handler {
    private static MainLooper instance = new MainLooper(Looper.getMainLooper());

    protected MainLooper(Looper looper) {
        super(looper);
    }

    public static MainLooper getInstance() {
        return instance;
    }

    public static void runOnUiThread(Runnable runnable) {
        if(Looper.getMainLooper().equals(Looper.myLooper())) {
            runnable.run();
        } else {
            instance.post(runnable);
        }
    }
}

參考資料

谷歌官方架構(gòu)藍(lán)圖Android Architecture Blueprints
Android MVP架構(gòu)搭建

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末净神,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溉委,更是在濱河造成了極大的恐慌鹃唯,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薛躬,死亡現(xiàn)場(chǎng)離奇詭異俯渤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)型宝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)八匠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人趴酣,你說(shuō)我怎么就攤上這事梨树。” “怎么了岖寞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵抡四,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)指巡,這世上最難降的妖魔是什么淑履? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮藻雪,結(jié)果婚禮上秘噪,老公的妹妹穿的比我還像新娘。我一直安慰自己勉耀,他們只是感情好指煎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著便斥,像睡著了一般至壤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枢纠,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天像街,我揣著相機(jī)與錄音,去河邊找鬼京郑。 笑死宅广,一個(gè)胖子當(dāng)著我的面吹牛葫掉,可吹牛的內(nèi)容都是我干的些举。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼俭厚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼户魏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起挪挤,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤叼丑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扛门,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸠信,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年论寨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了星立。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葬凳,死狀恐怖绰垂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情火焰,我是刑警寧澤劲装,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響占业,放射性物質(zhì)發(fā)生泄漏绒怨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一谦疾、第九天 我趴在偏房一處隱蔽的房頂上張望窖逗。 院中可真熱鬧,春花似錦餐蔬、人聲如沸碎紊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仗考。三九已至,卻和暖如春词爬,著一層夾襖步出監(jiān)牢的瞬間秃嗜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工顿膨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锅锨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓恋沃,卻偏偏與公主長(zhǎng)得像必搞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子囊咏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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