这一定是最简单的MVP+Retrofit

来源:互联网 发布:怎么在mac上装windows 编辑:程序博客网 时间:2024/06/05 07:15

说明:不讲原理,不讲优化,就是干

目标:学会如何搭建最最基本的mvp架构



简介


我承认画图不是我的强项


MVP是MVC衍生出来的架构,现在也比较成熟了,用的人也多了,面试也会考了,所以你必须要知道了


M:数据层(数据库,文件,网络等)


V:UI层(Activity,Fragment,View及其子类,Adapter及其子类)


P:中介(作用:关联V和M)


经过思想斗争,我觉得理论上的东西解释再多不如代码敲一遍,下面用一个用户登陆功能讲解如何搭建MVP架构,顺便简单用下Retrofit网络请求库(别怕都有注释)总之,MVP记住一个核心思想:V层就只做UI的操作,所有的业务处理和数据处理都交给P层(不然要你何用)就相当于把Activity里你写的众多网络请求和数据处理代码统统提取封装到P里了


MVP结构


api:Retrofit专用的,就两行代码,和mvp无关,待会给你看


bean:登陆的实体类,和mvp无关


login:可以看出是按功能分包。他比平时见到的多出来两个类,一个是 LoginPresenter(P层),一个是 LoginContract(契约接口类,用于将P和V接口封装到一起)他们是mvp重要的组成部分


架构图如下:


思路


mvp结构实现分三步:


一,搞一个接口契约类Contract,内含V接口和P接口


二,搞一个实现V接口的view类


三,搞一个实现P接口的presenter类


第一步:

搞一个接口契约类Contract,内含V接口和P接口。V接口里面放的是UI更新方法(V接口的实现类用到的方法);P接口里面放的是网络请求,数据读取,文件读取等具体的业务操作方法(P接口的实现类用到的方法)


从动态图可以看到,一共有三个地方有UI界面变化


①点击登陆展示等待加载遮罩


②登陆成功或者失败后取消等待加载遮罩


③取消等待加载遮罩的同时吐司


所以V接口里会有三个UI更新方法,那么就在V接口写三个方法呗,值得注意的是setPresenter方法只是用来把V层和P层关联的,本身与UI更新无关,但是必须要有的


P接口在本例中只需要实现一个登陆按钮触发的网络请求就可以啦,所以只有一个方法


由此可见,契约类Contract把V和P接口封装在了一块,而V和P接口又把具体的view和presenter用到的方法封装在了一块


/**
* 包含View和Presenter的契约接口
* Created by wangjiong on 2017/12/7.
*/

public interface LoginContract {
   /**
    * 与UI相关,与view相关操作
    */

   interface View {
       // 定义Presenter
       void setPresenter();
       // 展示等待加载页面
       void showLoading();
       // 隐藏等待加载页面
       void hideLoading();
       // 显示登陆信息
       void showLoginInfo(String msg);
   }
   /**
    * 与业务相关
    */

   interface Presenter {
       /**
        * 登陆
        * @param userId       用户id
        * @param userPassword 密码
        */

       void login(String userId, String userPassword);
   }
}


第二步:

搞一个实现V接口的view类。本例中的view类就是LoginActivity。首先实现接口所有的方法是必须的,然后这里有两个地方需要注意


一,我们是在实现V层接口setPresenter方法里通过LoginPresenter的构造方法将LoginActivity传递过去的(LoginPresenter是P的实现类),这里要记得主动调用一下setPresenter方法触发初始化presenter这个事


二,就是所谓的MVP中V层和P层分隔开,UI和业务分隔开。比如本例登陆按钮的点击事件,他并不是我们平时看到的直接撸网络请求代码,而是调用了P层里的的方法,说人话就是把网络请求一大堆代码封装了一个方法放到了P层那个类里头了,我们在调用的时候传需要用到的参数就行了。你说坑不坑,这点玩意说的那么高大尚


/**
* MVP层中的View层
*/

public class LoginActivity extends AppCompatActivity implements LoginContract.View {
   private LoginPresenter mPresenter;
   private EditText mEtName, mEtPwd;
   private LinearLayout mLayoutLoading;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_login);
       setPresenter();// 初始化presenter(很重要!不能说重写就不管了,一定要在view初始化调用此方法)
       mEtName = findViewById(R.id.et_name);// 用户名
       mEtPwd = findViewById(R.id.et_pwd);// 密码
       mLayoutLoading = findViewById(R.id.layout_loading);// 遮罩层
       findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {// 登陆按钮
           @Override
           public void onClick(View view) {// 点击登陆不是直接请求网络,而是通过presenter请求网络,然后将请求回来的数据交给view来更新
               mPresenter.login(mEtName.getText().toString().trim(), mEtPwd.getText().toString().trim());
           }
       });
   }
   @Override
   public void setPresenter() {
       mPresenter = new LoginPresenter(this);// 一是为了实例化presenter,二是通过构造方法将view实例传递给presenter
   }
   @Override
   public void showLoading() {// 展示遮罩层
       mLayoutLoading.setVisibility(View.VISIBLE);
   }
   @Override
   public void hideLoading() {// 隐藏遮罩层
       mLayoutLoading.setVisibility(View.GONE);
   }
   @Override
   public void showLoginInfo(String msg) {// 吐司登陆信息
       Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
   }
}


第三步:

搞一个实现P接口的presenter类。本例中presenter类就是LoginPresenter,可以看到他就一个构造方法(用来关联V层并获取view实例化对象)和一个请求网络数据的方法。高能预警:网络请求前UI页面需要展示一个遮罩层,所以调用了view的showLoading方法,请求结束后UI页面需要取消遮罩并吐司,所以调用了view的hideLoading方法和showLoginInfo方法


/**
* MVP中的P层
* Created by wangjiong on 2017/12/7
*/

public class LoginPresenter implements LoginContract.Presenter {
   private LoginContract.View mView;
   public LoginPresenter(LoginContract.View view) {// 获取到view的实例化对象
       this.mView = view;
   }
   @Override
   public void login(String userId, String password) {
       mView.showLoading();// 调用view的展示遮罩方法(view用来更新具体的UI)
       // 网络请求(可以自己封装一个网络库)
       Retrofit retrofit = new Retrofit.Builder()
               .baseUrl("http://192.168.1.101:8080/merclienttest/")// 请求的url
               .addConverterFactory(GsonConverterFactory.create())// 设置Gson为实体类解析工具
               .build();
       LoginApi loginApi = retrofit.create(LoginApi.class);// 传入一个接口类并返回此类的实例对象
       Call<LoginBean> call = loginApi.login(userId, password);// 调用类中定义的login方法
       call.enqueue(new Callback<LoginBean>() {// retrofit异步请求
           @Override
           public void onResponse(Call<LoginBean> call, Response<LoginBean> response) {
               mView.hideLoading();// 调用view的隐藏遮罩方法(view用来更新具体的UI)
               mView.showLoginInfo(response.body().getMsg());// 调用view的吐司方法(view用来更新具体的UI)
           }
           @Override
           public void onFailure(Call<LoginBean> call, Throwable t) {
               mView.hideLoading();
           }
       });
   }
}





全剧终


妈个鸡这是啥玩意?完了?哈哈哈,没错这就是MVP。就是这么简单。只需要记住一个核心思想V层仅仅是处理UI页面的(只管化妆接客,拉皮条找老鸨子P),业务逻辑放在P层去处理(网络请求,数据库,文件等),P处理完之后再调用一下V层已经写好的对应更新UI的方法即可

                         

扩展


没忘记Retrofit哦,简单介绍下使用方法


分三步:


一,搞一个接口类Api


二,搞一个对应的实体类bean


三,调用Retrofit方法请求网络


第一步:搞一个Api接口。本例中是LoginApi


第一行代码 @GET("login") 这个Get就代表get请求,如果换成Post那就代表post请求。login代表接口名(接口名需要后台提供)


第二行代码 Call<LoginBean> login(@Query("userId") String userId, @Query("password") String password); 其中 login 代表自己定义的一个方法,名字随便起(小驼峰式命名),@Query("userId") 代表接口中有个名为 userId 的参数, String userId 代表给这个参数传值,值为userId(名字随便起)


/**
* 登陆接口
* Created by wangjiong on 2017/12/7.
*/

public interface LoginApi {
   @GET("login")//get请求login接口(接口名需要后台提供)
   Call<LoginBean> login(@Query("userId") String userId, @Query("password") String password);  // 声明一个login的方法(随便写),两个string形参,并返回一个实体类LoginBean
}


没反应过来?


jsonObject.put("userId",userId) 这样写懂了吧,第一个userId是接口里的参数名,第二个userId是你传的字符串值,名字随便搞的,叫啥都行


你也可以@Query("userId") String myUserId 这就等价于

jsonObject.put("userId",myUserId  )  这里只是为了说明问题,所以不必太在意细节


第二步,搞一个对应的实体类bean。这个对应本例中的LoginBean。推荐用GsonFormat插件自动生成,啥?没听过,你又不是妹子,百度吧


public class LoginBean {
   /**
    * code : 200
    * msg : 登陆成功
    */

   private String code;
   private String msg;
   public String getCode() {
       return code;
   }
   public void setCode(String code) {
       this.code = code;
   }
   public String getMsg() {
       return msg;
   }
   public void setMsg(String msg) {
       this.msg = msg;
   }
}



第三步,调用Retrofit方法请求网络。做完上面两步,就可以正常调用了,咋用?你又不是妹子,参考LoginPresenter吧


/**
* MVP中的P层
* Created by wangjiong on 2017/12/7
*/

public class LoginPresenter implements LoginContract.Presenter {
   private LoginContract.View mView;
   public LoginPresenter(LoginContract.View view) {// 获取到view的实例化对象
       this.mView = view;
   }
   @Override
   public void login(String userId, String password) {
       mView.showLoading();// 调用view的展示遮罩方法(view用来更新具体的UI)
       // 网络请求(可以自己封装一个网络库)
       Retrofit retrofit = new Retrofit.Builder()
               .baseUrl("http://192.168.1.101:8080/merclienttest/")// 请求的url
               .addConverterFactory(GsonConverterFactory.create())// 设置Gson为实体类解析工具
               .build();
       LoginApi loginApi = retrofit.create(LoginApi.class);// 传入一个接口类并返回此类的实例对象
       Call<LoginBean> call = loginApi.login(userId, password);// 调用类中定义的login方法
       call.enqueue(new Callback<LoginBean>() {// retrofit异步请求
           @Override
           public void onResponse(Call<LoginBean> call, Response<LoginBean> response) {
               mView.hideLoading();// 调用view的隐藏遮罩方法(view用来更新具体的UI)
               mView.showLoginInfo(response.body().getMsg());// 调用view的吐司方法(view用来更新具体的UI)
           }
           @Override
           public void onFailure(Call<LoginBean> call, Throwable t) {
               mView.hideLoading();
           }
       });
   }
}


源码地址:https://github.com/GodJiong/mvp