這一定是最簡單的MVP+Retrofit

說明:不講原理,不講優化,就是幹

目標:學會如何搭建最最基本的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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章