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

MaySharkey 8年前發布 | 10K 次閱讀 Retrofit RxJava Android開發 移動開發

本文MVP的sample實現效果:

老規矩,在說對MVP模式的理解之前還是要再談談MVC模式,了解了MVC的缺點。我們才知道為什么要用MVP。

關于MVC的圖解,我在網上找到了一些圖。如下:

MVC模式在開發web或者管理系統中應用很多, 我們的View與人交互,人點擊鼠標或者輸入一些東西時,View會發送相應的指令給Controller,Controller接到指令,再去調用Model的方法去更新數據(大多是對數據的增刪改查),Model處理完,View刷新顯示。

MVC模式的缺點:

1:在android中,如果我們要用mvc模式,那么每層代表什么呢?

你可能會說:View對應android的layout.xml,Model對應android中對數據庫的操作對網絡等操作放在這里進行,Controller對應的則是Activity!

你說的都對,但是你不覺得這樣的對應關系并不好嗎,如果layout.xml對應View,那如果我們想動態的控制添加一些視圖控件或者改變背景,那么該怎么辦呢?

答曰:在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模式,這里順便使用了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,可以試試契約類)

 

 

 

來自:http://www.cnblogs.com/xurui1995/p/6021209.html

 

 本文由用戶 MaySharkey 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!