MVP模式入门(结合Rxjava,Retrofit)

MaySharkey 8年前
   <p>本文MVP的sample实现效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/05a8cee15977ed5407e7d967b16d6f76.gif"></p>    <p>老规矩,在说对MVP模式的理解之前还是要再谈谈MVC模式,了解了MVC的缺点。我们才知道为什么要用MVP。</p>    <p>关于MVC的图解,我在网上找到了一些图。如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9bed55e8596321cdb678ffbc9fd9f7e2.png"> <img src="https://simg.open-open.com/show/bafd0dc2fcaa44a61e7f02c681753f8d.png"></p>    <p>MVC模式在开发web或者管理系统中应用很多, <strong>我们的View与人交互,人点击鼠标或者输入一些东西时,View会发送相应的指令给Controller,Controller接到指令,再去调用Model的方法去更新数据(大多是对数据的增删改查),Model处理完,View刷新显示。</strong></p>    <p><strong>MVC模式的缺点:</strong></p>    <p>1:在android中,如果我们要用mvc模式,那么每层代表什么呢?</p>    <p>你可能会说:View对应android的layout.xml,Model对应android中对数据库的操作对网络等操作放在这里进行,Controller对应的则是Activity!</p>    <p>你说的都对,但是你不觉得这样的对应关系并不好吗,如果layout.xml对应View,那如果我们想动态的控制添加一些视图控件或者改变背景,那么该怎么办呢?</p>    <p>答曰:在Activity中添加代码。!!!这就是缺点之一所在:<strong> Activity既当爹(View)又当妈(Controller),layout.xml代表的View层控制能力太弱。</strong></p>    <p>2:再看一遍我们的MVC的结构图, <strong>View和Model是互相联系的</strong> ,存在耦合关系,这就给测试维护带来了难度。当我们想更换项目中的某个零件时,缺发现 太难拆下来!这个零件类的方法散布多处。关于MVC的结构图,忘了在哪听过一句经典的话,写三个字母,M,V,C,随便用线或箭头连字母,最后就是MVC的结构图。</p>    <p>说完了MVC,该主角登场了,上我们MVP的结构图。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/beca882a52c334bb739051987290e0d2.png"></p>    <p>好处不言而喻,View和Model无法通信了。</p>    <p>View层只负责与View有关的,操作View层时发出的事件传递给Presenter,Presenter去操作Model,操作完Model,再去通知View相应更新。</p>    <p>接下来,看看我们在项目中如何使用MVP模式,这里顺便使用了Retrofit和RXjava,建议你先了解它们的用法。</p>    <p>首先看我们的需求: 输入Github登录名,点击搜索按钮,搜索并显示结果(登录名,昵称, followers,following) 。</p>    <p>最终的项目结构:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/abd18512179d0de73dbb7c7966f1a00d.png"> <img src="https://simg.open-open.com/show/71631d355f2be3ed72e732a3102ad919.png"></p>    <p>bean类:</p>    <pre>  <code class="language-java">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;      }  }</code></pre>    <p>retrofit类主要是封装了利用Retrofit的网络请求</p>    <pre>  <code class="language-java">public interface GithubService {      @GET("/users/{user}")      Observable<User> getUser(@Path("user") String username);  }</code></pre>    <pre>  <code class="language-java">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);        }  }</code></pre>    <p>接下来思考我们的MVP模式了,一些从我们的View层开始,我们需要先列出和View相关的方法(不涉及逻辑)。</p>    <p>1.显示xml的试图</p>    <p>2.ProgressDialog显示</p>    <p>3.ProgressDialog消失</p>    <p>4.显示错误信息</p>    <p>接下来看具体代码:</p>    <p>activity_main.xml</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>BaseView,BasePresentor , BaseModel三个接口</p>    <pre>  <code class="language-java">public interface BaseView {      void showProgressDialog();      void hideProgressDialog();      void showText(User userbean);      void showErrorMessage(String text);  }</code></pre>    <pre>  <code class="language-java">public interface BasePresenter<T extends BaseView> {      void attachView(T view);      void detachView();      void searchUser(String loginName);  }</code></pre>    <pre>  <code class="language-java">public interface BaseModel {      void getUser(Subscriber<User> subscribe,String loginName);  }</code></pre>    <p>第二个接口 interface BasePresenter<T extends BaseView> 正是关键,至于为什么,可以用实现类去解释。</p>    <p>MainActivity实现BaseView接口,作为View层。</p>    <pre>  <code class="language-java">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();      }  }</code></pre>    <p>当点击Button产生事件时,是将逻辑交给 MainPresenter 去处理的,对应关系  <strong>V  ——>  P</strong></p>    <p>下面看 MainPresenter 代码和Model代码。</p>    <pre>  <code class="language-java">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);          }        }  }</code></pre>    <pre>  <code class="language-java">public class MainModel implements BaseModel{      @Override      public void getUser(Subscriber<User> subscriber ,String loginName) {          HttpMethods.getInstance().getUser(subscriber,loginName);      }  }</code></pre>    <p>这里的Model实现类较为简单,直接使用了封装好的 HttpMethods 的方法。(Model可以理解为一个仓库管理员,我们的网络也能理解为一个大的仓库)。</p>    <p>在 MainPresenter 中我们其实是使用了Model的方法,即 <strong>P——>M</strong></p>    <p>然后用Rxjava的观察者观察结果,再去调用View的方法刷新界面,即 <strong>P——>V</strong></p>    <p>这时候回过来头来看我们的MVP图,是不是一模一样?(如何想要进阶mvp,可以试试契约类)</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/beca882a52c334bb739051987290e0d2.png"> </p>    <p style="text-align:center"> </p>    <p> </p>    <p>来自:http://www.cnblogs.com/xurui1995/p/6021209.html</p>    <p> </p>