成都網(wǎng)站建設(shè)拿出干貨,與各位小伙伴一起交流分享關(guān)于做出簡潔明了的MVP架構(gòu)
MVP的背景
眾所周知 MVP這種架構(gòu)模式已經(jīng)出現(xiàn)很久了,大體時間應(yīng)該是2014年吧,現(xiàn)在網(wǎng)上的關(guān)于MVP的文章也很多,各式各樣的關(guān)于MVP的架構(gòu)知識都涌現(xiàn)出來,可想而知現(xiàn)在這種架構(gòu)有多么火,還有目前風(fēng)頭正勁的MVVM,當(dāng)然我并不覺得我現(xiàn)在寫MVP有些晚,因為每個人都有每個人的架構(gòu),每個人都可以根據(jù)自己的邏輯封裝出來自己的架構(gòu)模式,今天我介紹的便是我自己通過項目總結(jié)出來的MVP
什么是MVP
MVP知識點
MVP - Model-View-Presenter
MVP和MVC的區(qū)別僅僅在于P和Control,MVC中View和Model是互通的可以互相通信,在Android中View一般代表著我們的xml進行界面的描述,而對于模型Model部分則大多對應(yīng)于本地的數(shù)據(jù)文件或網(wǎng)絡(luò)獲取的數(shù)據(jù)體,很多情況下我們對這些數(shù)據(jù)的處理也會在這一層中進行,最后的控制器Controller則當(dāng)之無愧的是右Activity承擔(dān)。
而MVP中view通過presenter訪問model,大大的減小了耦合性,業(yè)務(wù)邏輯都交給P處理,通過P訪問V層更改UI。MVP模式可以分離顯示層與邏輯層,它們之間通過接口進行通信,降低耦合。理想化的MVP模式可以實現(xiàn)同一份邏輯代碼搭配不同的顯示界面,因為它們之間并不依賴與具體,而是依賴于抽象。這使得Presenter可以運用于任何實現(xiàn)了View邏輯接口的UI,使之具有更廣泛的適用性,保證了靈活度。
這里不多介紹MVC了,相信大家都很熟悉
MVP的優(yōu)缺點
優(yōu)點:
● 降低耦合度,實現(xiàn)了M層和V層的完全分離,可以修改V層不影響M層
● 模塊職責(zé)劃分明顯,層次清晰
● P層可以復(fù)用,一個P可以對應(yīng)多個V,不需要修改P的邏輯
● 單元測試更加簡單方便
● 代碼靈活度高
缺點:
● V層和P層交互頻繁
● 代碼量多,類變多了
總結(jié)
● M層負(fù)責(zé)存儲、檢索、操縱數(shù)據(jù),代表著一類組件或者類,這些組件或類可以向外部提供數(shù)據(jù),同時也能從外部獲取數(shù)據(jù)將數(shù)據(jù)存儲起來
● V層負(fù)責(zé)將數(shù)據(jù)UI呈現(xiàn)給用戶。一般的視圖UI只包含界面,并不包含界面邏輯,V層收P層控制,在Android中一般是Activity、Fragment、View、ViewGroup。。。
● P層作為V層和M層的中間樞紐,處理用戶交互的業(yè)務(wù)邏輯
MVP實現(xiàn)
1.基本實現(xiàn)
我們都知道一般MVP架構(gòu)一共需要以下四步:
● 定義一個interface接口XView,對應(yīng)的Activity,F(xiàn)ragment實現(xiàn)這個interface
● 編寫Molde,里面的業(yè)務(wù)邏輯主要包括網(wǎng)絡(luò)請求獲取數(shù)據(jù),數(shù)據(jù)庫讀取等耗時操作,通過M層回調(diào)給P層通知V層更新UI
● 編寫Presenter,P層持有V和M的引用,實現(xiàn)P層的回調(diào),并且回調(diào)給V層更新
● Activity中調(diào)用P執(zhí)行業(yè)務(wù)邏輯,更新UI 具體代碼就不貼了,相信了解過MVP的都會寫基本的代碼
但是問題也就出來了,由于P層需要和V層進行通信,更新UI時需要持有V層的view對象,那么我們每個P里面一般都用構(gòu)造去初始化這個View,類多了之后感覺很煩,而View層里的一些常用的方法我們也可以封到base里面,比如loading的顯示隱藏,空布局和錯誤布局的顯示…
2.Base封裝
1.BaseView
package com.hankkin.xlibrary.mvp; import android.view.View; /** * Created by hankkin on 2017/3/29. */ public interface BaseView { /** * 顯示loading框 */ void showProgress(); /** * 隱藏loading框 */ void hideProgress(); void toast(CharSequence s); void toast(int id); void toastLong(CharSequence s); void toastLong(int id); /** * 顯示空數(shù)據(jù)布局 */ void showNullLayout(); /** * 隱藏空數(shù)據(jù)布局 */ void hideNullLayout(); /** * 顯示異常布局 * @param listener */ void showErrorLayout(View.OnClickListener listener); void hideErrorLayout(); }
2.BasePresenter
package com.hankkin.xlibrary.mvp; /** * Created by hankkin on 2017/3/29. */ public abstract class BasePresent{ public T view; public void attach(T view){ this.view = view; } public void detach(){ this.view = null; } }
我們在BasePresenter里面去初始化View對象,同時提供釋放View對象以防止內(nèi)存溢出
3.MvpActivity
package com.hankkin.hlibrary.base; import android.os.Bundle; import android.support.annotation.Nullable; import com.lzy.okgo.OkGo; /** * Created by hankkin on 2017/3/29. */ public abstract class MvpActivity> extends BaseAcitvity{ protected P presenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = initPresenter(); } @Override protected void onResume() { super.onResume(); presenter.attach((V) this); } @Override protected void onDestroy() { presenter.detach(); OkGo.getInstance().cancelTag(this); super.onDestroy(); } public abstract P initPresenter(); }
這樣我們在Activity中初始化P,并且連接V,在onDestroy()生命周期中釋放P中引用的V。
Example
我們按照功能模塊來構(gòu)造我們的MVP,可能大家注意到了沒有M層啊,是的,這里我把M層舍棄掉了,把業(yè)務(wù)邏輯、網(wǎng)絡(luò)請求直接放在了P層,大大減少了類的數(shù)量,這樣我們每個功能模塊只需要新建一個View和一個Presenter就可以滿足了,特殊的需求再通過特殊方法來處理,下面我們舉一個簡單的例子:
網(wǎng)絡(luò)請求我用的 jeasonlzy 大神的OKGo3,剛出鍋沒幾天,嘗試一下,個人認(rèn)為封裝的非常非常好,繼承了Rx,Retrofit,相信你會喜歡的。
https://github.com/jeasonlzy/okhttp-OkGo
好了下面看我們的例子吧:
項目結(jié)構(gòu)
看一下項目結(jié)構(gòu)
HomeView
我用的Gank.io里面的一個接口獲取數(shù)據(jù),首先我們定義我們的HomeView,里面有兩個方法獲取數(shù)據(jù)成功和獲取失敗
package com.hankkin.mvpdemo.home; import com.hankkin.hlibrary.BaseView; /** * Created by hankkin on 2017/6/19. */ public interface HomeView extends BaseView{ void getDataHttp(String data); void getDataHttpFail(String msg); }
HomePresenter
然后我們定義HomePresenter,里面只有我們的網(wǎng)絡(luò)請求,因為我們的BasePresenter持有View對象,所以在回調(diào)中直接調(diào)用HomeView的兩個成功失敗的方法
package com.hankkin.mvpdemo.home; import com.hankkin.hlibrary.BasePresent; import com.lzy.okgo.OkGo; import com.lzy.okgo.callback.StringCallback; import com.lzy.okgo.model.Response; /** * Created by hankkin on 2017/6/19. */ public class HomePresenter extends BasePresent{ public void getGankData(){ OkGo. get("http://gank.io/api/data/Android/10/1") .tag(this) .execute(new StringCallback() { @Override public void onSuccess(Response response) { view.getDataHttp(response.body()); } @Override public void onError(Response response) { super.onError(response); view.getDataHttpFail(response.message()); } }); } }
Activity
最后看一下Activity,我們的Activity繼承了MVPActivity并實現(xiàn)了HomeView,同時將泛型對象設(shè)為我們的HomeView和HomePresenter,這樣我們就可以直接調(diào)用P層的網(wǎng)絡(luò)請求方法,同時也能回調(diào)更新UI
package com.hankkin.mvpdemo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.hankkin.hlibrary.MvpActivity; import com.hankkin.mvpdemo.home.HomePresenter; import com.hankkin.mvpdemo.home.HomeView; import static com.hankkin.mvpdemo.R.id.btn_get; public class MainActivity extends MvpActivityimplements HomeView{ private TextView mTextMessage; private Button btnGet; private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: mTextMessage.setText(R.string.home); return true; case R.id.navigation_dashboard: mTextMessage.setText(R.string.control); return true; case R.id.navigation_notifications: mTextMessage.setText(R.string.notification); return true; } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextMessage = (TextView) findViewById(R.id.message); btnGet = (Button) findViewById(btn_get); BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); btnGet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showProgress(); presenter.getGankData(); } }); } @Override public HomePresenter initPresenter() { return new HomePresenter(); } @Override public void getDataHttp(String data) { mTextMessage.setText(data); hideProgress(); } @Override public void getDataHttpFail(String msg) { toast(msg); } @Override public void toast(CharSequence s) { toast("獲取成功"); } }
好了是不是很簡單呢?小伙伴們?nèi)绻猩逗玫慕ㄗh或者覺得不妥的地方希望及時指正,共同交流,謝謝。