zoukankan      html  css  js  c++  java
  • MVP模式入门(结合Rxjava,Retrofit)

      本文MVP的sample实现效果:

      

      github地址:https://github.com/xurui1995/MvpSample

      

      老规矩,在说对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的更多概念:

      【翻译】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,可以试试契约类)

           

            

      

  • 相关阅读:
    1015
    1016
    1014
    1002
    1010
    1006
    动态规划1001
    动态规划1002
    使用EF框架调用带有输出参数(output)的存储过程
    工程地质相关知识
  • 原文地址:https://www.cnblogs.com/xurui1995/p/6021209.html
Copyright © 2011-2022 走看看