手把手教你搭建簡單的mvp框架

mvp java retrofit gson okhttp


對MVP的文字描述介紹

MVC(Model - View - Controller)

  • Model : 數(shù)據(jù)(JavaBean實(shí)體類,用于保存實(shí)例數(shù)據(jù))
  • View : UI界面牺陶,和用戶交互(向用戶展示數(shù)據(jù)以及接收用戶的輸入)
  • Controller : 更新UI界面和數(shù)據(jù)實(shí)例

例如:View層接受用戶的輸入狈涮,然后通過Controller修改對應(yīng)的Model實(shí)例渠羞;同時(shí)躏吊,當(dāng)Model實(shí)例的數(shù)據(jù)發(fā)生變化的時(shí)候秸侣,需要修改UI界面赤屋,可以通過Controller更新界面园骆。(View層也可以直接更新Model實(shí)例的數(shù)據(jù)仪芒,而不用每次都通過Controller唁影,這樣對于一些簡單的數(shù)據(jù)更新工作會(huì)變得方便許多。)

MVP

MVP.png

MVP把Activity中的UI邏輯抽象成View接口掂名,把業(yè)務(wù)邏輯抽象成Presenter接口据沈,Model類還是原來的Model。
在MVP模式中Activity的功能就是響應(yīng)生命周期和顯示界面饺蔑,具體其他的工作都丟到了Presenter層中進(jìn)行完成锌介,Presenter其實(shí)是Model層和View層的橋梁。

  • M(model) :數(shù)據(jù)層,用來放數(shù)據(jù)的處理(比如網(wǎng)絡(luò)請求孔祸,緩存等)
  • V(view) : 負(fù)責(zé)UI具體實(shí)現(xiàn)展現(xiàn)隆敢。比如Presenter派發(fā)過來一個(gè)動(dòng)作是showDialog顯示進(jìn)度命令,那么我們這個(gè)View就負(fù)責(zé)實(shí)現(xiàn)具體UI
  • P(presenter) : 負(fù)責(zé)處理業(yè)務(wù)邏輯代碼崔慧,處理Model數(shù)據(jù)拂蝎,然后將處理完的數(shù)據(jù)分發(fā)到View層
MVP.png

關(guān)于android的MVP模式其實(shí)一直沒有一個(gè)統(tǒng)一的實(shí)現(xiàn)方式,不同的人由于個(gè)人理解的不同惶室,進(jìn)而產(chǎn)生了很多不同的實(shí)現(xiàn)方式温自,其實(shí)很難去說哪一種更好,哪一種不好皇钞,針對不同的場合悼泌,不同的實(shí)現(xiàn)方式都有各自的優(yōu)缺點(diǎn)。
在這里夹界,我介紹的MVP是Google提出的一種MVP實(shí)現(xiàn)方式馆里。

APP——Dreamer

這個(gè)app要實(shí)現(xiàn)的是類似于“周公解夢”的查詢功能,點(diǎn)擊卡片可以看詳情

Dreamer.png

手?jǐn)]代碼?墒痢p佟!

準(zhǔn)備工作
  • 在 build.gradle(Module: app )里添加依賴并 sync
  • 下載GsonFormat 插件复斥,便于后續(xù)根據(jù)拿到的 json 數(shù)據(jù)直接生成使用該插件生成類
  • api 接口(此api接口僅供本人自己使用哦~)
  • android manifests 中添加網(wǎng)絡(luò)權(quán)限

ps 下面以此app的search為例子講 MVP 框架

1.Contract

加入了契約類來統(tǒng)一管理view與presenter的所有的接口慢哈,這種方式使得view與presenter中有哪些功能,一目了然永票,維護(hù)起來也方便卵贱,實(shí)例如下:

package com.example.chenshuyu.dreamer.search;

import...

public interface DreamerSearchContract {
    interface DreamerSearchUIView{
        //當(dāng)網(wǎng)絡(luò)錯(cuò)誤等原因獲取搜索結(jié)果沒有成功時(shí)
        void onError();
        //成功獲得搜索結(jié)果,且結(jié)果不為空時(shí)
        void updateRV(List<Dreamer.DataBean> dataBeans);
        //成功獲得搜索結(jié)果侣集,但是并沒有搜出來東西時(shí)
        void onNull();
    }

    interface DreamerSearchPresenter{
        //根據(jù)搜索輸入的獲keyword獲得搜索結(jié)果的
        void getSearchDream(String keyword);
    }
}
2.Model
package com.example.chenshuyu.dreamer.search;

import ...

public class DreamSearchModel {

    private Retrofit retrofit = new Retrofit.Builder()
            //設(shè)置數(shù)據(jù)解析器
            .addConverterFactory(GsonConverterFactory.create())
            //設(shè)置網(wǎng)絡(luò)請求的Url地址
            .baseUrl("https://api.shenjian.io/")
            .build();

    // 創(chuàng)建網(wǎng)絡(luò)請求接口的實(shí)例
    private RetrofitService api = retrofit.create(RetrofitService.class);

    //通過model層的update方法獲得網(wǎng)絡(luò)請求拿到的數(shù)據(jù)
    public Call<Dreamer> update(String keyword){
        return api.getSearch("cfcde6656c3d1b67ecbecf400592d05e",keyword);
    }
}
3.Presenter
package com.example.chenshuyu.dreamer.search;

/**
 * presenter接口的具體實(shí)現(xiàn)類
 */
public class DreamSearchPresenterImpl implements DreamerSearchContract.DreamerSearchPresenter {
    
    /**
     * presenter應(yīng)該持有 view 層和 model 層的引用
     * 這樣才能完成兩層之間的邏輯交互
     * 同時(shí)使 view 層和 model 層完全隔離開來
     */
    private DreamerSearchContract.DreamerSearchUIView searchUIView;
    private DreamSearchModel dreamSearchModel = new DreamSearchModel();

    /**
     * presenter 層對應(yīng)的類持有的 view 層對應(yīng)的類是沒有辦法在 presenter 內(nèi)部實(shí)例化的(此時(shí)的view是有方法但是沒有具體實(shí)現(xiàn)的接口)
     * view 層的具體實(shí)現(xiàn)是 activity 繼承 view 層键俱,并重寫 view 層的所有方法,即 activity 就是 view 層
     * 故成員對象 view 的實(shí)例化對象是在activity中傳給presenter的
     * 所以 presenter 的構(gòu)造函數(shù)中應(yīng)該傳入 view
     * model 層是有具體實(shí)現(xiàn)類的世分,并且已經(jīng)在 presenter 類的內(nèi)部實(shí)例化了编振,這樣才能拿到 model 的具體數(shù)據(jù),進(jìn)行操作
     * @param searchUIView
     */
    public DreamSearchPresenterImpl(DreamerSearchContract.DreamerSearchUIView searchUIView){
        this.searchUIView = searchUIView;
    }

    /**
     * view 層和 model 層的邏輯交互臭埋,根據(jù)model的數(shù)據(jù)踪央,執(zhí)行相關(guān)的view層操作
     * @param keyword
     */
    @Override
    public void getSearchDream(String keyword) {
        Call<Dreamer> call = dreamSearchModel.update(keyword);
        call.enqueue(new Callback<Dreamer>() {
            
            //發(fā)送網(wǎng)絡(luò)請求成功
            @Override
            public void onResponse(Call<Dreamer> call, Response<Dreamer> response) {
                Dreamer dreamer = response.body();
                if (dreamer.getData() == null){
                    searchUIView.onNull();
                }else {
                    searchUIView.updateRV(dreamer.getData());
                }
            }

            //沒有成功
            @Override
            public void onFailure(Call<Dreamer> call, Throwable t) {
                searchUIView.onError();
                t.printStackTrace();
            }
        });
    }
}
4.View
package com.example.chenshuyu.dreamer.search;

import...

public class SearchActivity extends AppCompatActivity implements DreamerSearchContract.DreamerSearchUIView{
    private String keyword;
    private EditText editText;
    private ImageView imageView;
    private RecyclerView recyclerView;
    private SearchAdapter searchAdapter;
    private LinearLayout linearLayout;
    private List<Dreamer.DataBean> dataBeans = new ArrayList<>();
    private DreamerSearchContract.DreamerSearchPresenter presenter = new DreamSearchPresenterImpl(this);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);
        initId();
        initRV();

        linearLayout.setVisibility(View.GONE);
        editText.setHint("請輸入你想搜索的關(guān)鍵字");
        // 點(diǎn)擊搜索,從EditTex獲得搜索keyword瓢阴,并通過presneter獲得搜索結(jié)果
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                keyword = editText.getText().toString();
                presenter.getSearchDream(keyword);
            }
        });
    }

    // 綁定控件
    private void initId(){
        editText = findViewById(R.id.search_edit);
        imageView = findViewById(R.id.search_img);
        recyclerView = findViewById(R.id.search_rv);
        linearLayout = findViewById(R.id.search_no);
    }

    // 初始化recyclerview
    private void initRV(){
        LinearLayoutManager linearLayout = new LinearLayoutManager(this);
        linearLayout.setOrientation(LinearLayoutManager.VERTICAL);
        searchAdapter = new SearchAdapter(dataBeans,this);
        recyclerView.setLayoutManager(linearLayout);
        recyclerView.setAdapter(searchAdapter);
    }

    // 重寫view的方法
    @SuppressLint("CheckResult")
    @Override
    public void onError() {
        linearLayout.setVisibility(View.GONE);
        Toasty.error(this,"你的網(wǎng)絡(luò)崩潰了畅蹂,5555~",Toast.LENGTH_LONG).show();
    }

    @Override
    public void updateRV(List<Dreamer.DataBean> dataBeanArrayList) {
        linearLayout.setVisibility(View.GONE);
        searchAdapter.update(dataBeanArrayList);
    }

    @Override
    public void onNull() {
        dataBeans.clear();
        searchAdapter.update(dataBeans);
        linearLayout.setVisibility(View.VISIBLE);
    }
}

代碼架構(gòu)

image.png
  • 每一個(gè)activity就是一個(gè)mvp框架搭建起來的
  • entity用來存放數(shù)據(jù)請求獲得的類
  • service 統(tǒng)一管理網(wǎng)絡(luò)請求的接口

想看完整代碼的大兄弟和小仙女,可以從github上clone荣恐,在此附上我的倉庫地址~:
https://github.com/chenshuyuhhh/Dreamer.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末液斜,一起剝皮案震驚了整個(gè)濱河市累贤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌少漆,老刑警劉巖臼膏,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異示损,居然都是意外死亡渗磅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門检访,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夺溢,“玉大人,你說我怎么就攤上這事烛谊。” “怎么了嘉汰?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵丹禀,是天一觀的道長。 經(jīng)常有香客問我鞋怀,道長双泪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任密似,我火速辦了婚禮焙矛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘残腌。我一直安慰自己村斟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布抛猫。 她就那樣靜靜地躺著蟆盹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闺金。 梳的紋絲不亂的頭發(fā)上逾滥,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音败匹,去河邊找鬼寨昙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掀亩,可吹牛的內(nèi)容都是我干的舔哪。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼槽棍,長吁一口氣:“原來是場噩夢啊……” “哼尸红!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對情侶失蹤外里,失蹤者是張志新(化名)和其女友劉穎怎爵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盅蝗,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鳖链,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了墩莫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芙委。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖狂秦,靈堂內(nèi)的尸體忽然破棺而出灌侣,到底是詐尸還是另有隱情,我是刑警寧澤裂问,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布侧啼,位于F島的核電站,受9級(jí)特大地震影響堪簿,放射性物質(zhì)發(fā)生泄漏痊乾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一椭更、第九天 我趴在偏房一處隱蔽的房頂上張望哪审。 院中可真熱鬧,春花似錦虑瀑、人聲如沸湿滓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茉稠。三九已至,卻和暖如春把夸,著一層夾襖步出監(jiān)牢的瞬間而线,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國打工恋日, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膀篮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓岂膳,卻偏偏與公主長得像誓竿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子谈截,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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