這一定是最簡單的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介面
從動態圖可以看到,一共有三個地方有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