本文MVP的sample實現效果:html
github地址:https://github.com/xurui1995/MvpSamplejava
老規矩,在說對MVP模式的理解以前仍是要再談談MVC模式,瞭解了MVC的缺點。咱們才知道爲何要用MVP。android
關於MVC的圖解,我在網上找到了一些圖。以下:git
MVC模式在開發web或者管理系統中應用不少,咱們的View與人交互,人點擊鼠標或者輸入一些東西時,View會發送相應的指令給Controller,Controller接到指令,再去調用Model的方法去更新數據(大可能是對數據的增刪改查),Model處理完,View刷新顯示。github
MVC模式的缺點:web
1:在android中,若是咱們要用mvc模式,那麼每層表明什麼呢?數據庫
你可能會說:View對應android的layout.xml,Model對應android中對數據庫的操做對網絡等操做放在這裏進行,Controller對應的則是Activity!設計模式
你說的都對,可是你不以爲這樣的對應關係並很差嗎,若是layout.xml對應View,那若是咱們想動態的控制添加一些視圖控件或者改變背景,那麼該怎麼辦呢?api
答曰:在Activity中添加代碼。!!!這就是缺點之一所在:Activity既當爹(View)又當媽(Controller),layout.xml表明的View層控制能力太弱。網絡
2:再看一遍咱們的MVC的結構圖,View和Model是互相聯繫的,存在耦合關係,這就給測試維護帶來了難度。當咱們想更換項目中的某個零件時,缺發現 太難拆下來!這個零件類的方法散佈多處。關於MVC的結構圖,忘了在哪聽過一句經典的話,寫三個字母,M,V,C,隨便用線或箭頭連字母,最後就是MVC的結構圖。
說完了MVC,該主角登場了,上咱們MVP的結構圖。
好處不言而喻,View和Model沒法通訊了。
View層只負責與View有關的,操做View層時發出的事件傳遞給Presenter,Presenter去操做Model,操做完Model,再去通知View相應更新。
關於MVP的更多概念:
接下來,看看咱們在項目中如何使用MVP模式,這裏順便使用了Retrofit和RXjava,建議你先了解它們的用法。
首先看咱們的需求:輸入Github登陸名,點擊搜索按鈕,搜索並顯示結果(登陸名,暱稱, followers,following)。
最終的項目結構:
bean類:
public class User { private String login; private String name; private int followers; private int following; public int getFollowers() { return followers; } public void setFollowers(int followers) { this.followers = followers; } public int getFollowing() { return following; } public void setFollowing(int following) { this.following = following; } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
retrofit類主要是封裝了利用Retrofit的網絡請求
public interface GithubService { @GET("/users/{user}") Observable<User> getUser(@Path("user") String username); }
public class HttpMethods { public static final String BASE_URL = "https://api.github.com"; private static final int DEFAULT_TIMEOUT = 5; private Retrofit retrofit; private GithubService mGithubService; //構造方法私有 private HttpMethods() { //手動建立一個OkHttpClient並設置超時時間 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); retrofit = new Retrofit.Builder() .client(httpClientBuilder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); mGithubService = retrofit.create(GithubService.class); } private static class SingletonHolder{ private static final HttpMethods INSTANCE = new HttpMethods(); } //獲取單例 public static HttpMethods getInstance(){ return SingletonHolder.INSTANCE; } public void getUser(Subscriber<User> subscriber ,String loginName){ mGithubService.getUser(loginName) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }
接下來思考咱們的MVP模式了,一些從咱們的View層開始,咱們須要先列出和View相關的方法(不涉及邏輯)。
1.顯示xml的試圖
2.ProgressDialog顯示
3.ProgressDialog消失
4.顯示錯誤信息
接下來看具體代碼:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="80dp"/> <EditText android:id="@+id/ed_text" android:layout_centerInParent="true" android:hint="請輸入搜索登陸名" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:id="@+id/search_btn" android:text="查詢" android:layout_centerHorizontal="true" android:layout_below="@+id/ed_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
BaseView,BasePresentor , BaseModel三個接口
public interface BaseView { void showProgressDialog(); void hideProgressDialog(); void showText(User userbean); void showErrorMessage(String text); }
public interface BasePresenter<T extends BaseView> { void attachView(T view); void detachView(); void searchUser(String loginName); }
public interface BaseModel { void getUser(Subscriber<User> subscribe,String loginName); }
第二個接口interface BasePresenter<T extends BaseView>正是關鍵,至於爲何,能夠用實現類去解釋。
MainActivity實現BaseView接口,做爲View層。
public class MainActivity extends AppCompatActivity implements BaseView { @InjectView(R.id.tv) TextView mTextView; @InjectView(R.id.search_btn) Button mButton; @InjectView(R.id.ed_text) EditText mEditText; private ProgressDialog dialog; private MainPresenter mMainPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); initView(); mMainPresenter=new MainPresenter(); mMainPresenter.attachView(this); } /** * 一些初始化,這裏爲ProgressDialog的初始化 */ private void initView() { dialog=new ProgressDialog(this); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.setMessage("正在搜索中"); } @OnClick(R.id.search_btn) void search(View view){ mMainPresenter.searchUser(mEditText.getText().toString()); } @Override public void showProgressDialog() { dialog.show(); } @Override public void hideProgressDialog() { dialog.dismiss(); } @Override public void showText(User userbean) { String temp=getResources().getString(R.string.user_format); String str=String.format(temp,userbean.getLogin(),userbean.getName(),userbean.getFollowers(),userbean.getFollowing()); mTextView.setText(str); } @Override public void showErrorMessage(String text) { Toast.makeText(this,text,Toast.LENGTH_SHORT).show(); } @Override protected void onDestroy() { super.onDestroy(); if(mMainPresenter!=null) mMainPresenter.detachView(); } }
當點擊Button產生事件時,是將邏輯交給MainPresenter去處理的,對應關係 V ——> P
下面看MainPresenter代碼和Model代碼。
public class MainPresenter implements BasePresenter { private BaseView mMainView; private MainModel mModel; public MainPresenter() { mModel=new MainModel(); } @Override public void attachView(BaseView view) { mMainView=view; } @Override public void detachView() { mMainView=null; } @Override public void searchUser(String loginName) { if(TextUtils.isEmpty(loginName.trim())){ mMainView.showErrorMessage("請輸入合法登陸名"); return; } if (mModel!=null){ mModel.getUser(new Subscriber<User>() { @Override public void onStart() { //先顯示對話框 mMainView.showProgressDialog(); } @Override public void onCompleted() { //請求結束,對話框消失 mMainView.hideProgressDialog(); } @Override public void onError(Throwable e) { //error時 mMainView.showErrorMessage("搜索失敗"); } @Override public void onNext(User user) { mMainView.showText(user); } },loginName); } } }
public class MainModel implements BaseModel{ @Override public void getUser(Subscriber<User> subscriber ,String loginName) { HttpMethods.getInstance().getUser(subscriber,loginName); } }
這裏的Model實現類較爲簡單,直接使用了封裝好的HttpMethods的方法。(Model能夠理解爲一個倉庫管理員,咱們的網絡也能理解爲一個大的倉庫)。
在MainPresenter中咱們實際上是使用了Model的方法,即P——>M
而後用Rxjava的觀察者觀察結果,再去調用View的方法刷新界面,即P——>V
這時候回過來頭來看咱們的MVP圖,是否是如出一轍?(如何想要進階mvp,能夠試試契約類)