MVP模式入門(結合Rxjava,Retrofit)

  本文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的更多概念:

  【翻譯】Android的MVP設計模式

  MVC,MVP 和 MVVM 的圖示

 

  接下來,看看咱們在項目中如何使用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,能夠試試契約類)

       

相關文章
相關標籤/搜索