MVP模式入門(結合Rxjava,Retrofit)
本文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