說明:不講原理何暮,不講優(yōu)化俄精,就是干
目標:學會如何搭建最最基本的mvp架構(gòu)
簡介
我承認畫圖不是我的強項
MVP是MVC衍生出來的架構(gòu)改橘,現(xiàn)在也比較成熟了,用的人也多了谦趣,面試也會考了疲吸,所以你必須要知道了
M:數(shù)據(jù)層(數(shù)據(jù)庫,文件前鹅,網(wǎng)絡等)
V:UI層(Activity摘悴,F(xiàn)ragment,View及其子類舰绘,Adapter及其子類)
P:中介(作用:關(guān)聯(lián)V和M)
經(jīng)過思想斗爭蹂喻,我覺得理論上的東西解釋再多不如代碼敲一遍葱椭,下面用一個用戶登陸功能講解如何搭建MVP架構(gòu),順便簡單用下Retrofit網(wǎng)絡請求庫(別怕都有注釋)總之口四,MVP記住一個核心思想:V層就只做UI的操作孵运,所有的業(yè)務處理和數(shù)據(jù)處理都交給P層(不然要你何用)就相當于把Activity里你寫的眾多網(wǎng)絡請求和數(shù)據(jù)處理代碼統(tǒng)統(tǒng)提取封裝到P里了
MVP結(jié)構(gòu)
api:Retrofit專用的,就兩行代碼蔓彩,和mvp無關(guān)掐松,待會給你看
bean:登陸的實體類,和mvp無關(guān)
login:可以看出是按功能分包粪小。他比平時見到的多出來兩個類大磺,一個是 LoginPresenter(P層),一個是 LoginContract(契約接口類探膊,用于將P和V接口封裝到一起)他們是mvp重要的組成部分
架構(gòu)圖如下:
思路
mvp結(jié)構(gòu)實現(xiàn)分三步:
一杠愧,搞一個接口契約類Contract,內(nèi)含V接口和P接口
二逞壁,搞一個實現(xiàn)V接口的view類
三流济,搞一個實現(xiàn)P接口的presenter類
第一步:
搞一個接口契約類Contract,內(nèi)含V接口和P接口腌闯。V接口里面放的是UI更新方法(V接口的實現(xiàn)類用到的方法)绳瘟;P接口里面放的是網(wǎng)絡請求,數(shù)據(jù)讀取姿骏,文件讀取等具體的業(yè)務操作方法(P接口的實現(xiàn)類用到的方法)
從動態(tài)圖可以看到糖声,一共有三個地方有UI界面變化
①點擊登陸展示等待加載遮罩
②登陸成功或者失敗后取消等待加載遮罩
③取消等待加載遮罩的同時吐司
所以V接口里會有三個UI更新方法,那么就在V接口寫三個方法唄分瘦,值得注意的是setPresenter方法只是用來把V層和P層關(guān)聯(lián)的蘸泻,本身與UI更新無關(guān),但是必須要有的
P接口在本例中只需要實現(xiàn)一個登陸按鈕觸發(fā)的網(wǎng)絡請求就可以啦嘲玫,所以只有一個方法
由此可見悦施,契約類Contract把V和P接口封裝在了一塊,而V和P接口又把具體的view和presenter用到的方法封裝在了一塊
/**
* 包含View和Presenter的契約接口
* Created by wangjiong on 2017/12/7.
*/
publicinterfaceLoginContract{
/**
* 與UI相關(guān)去团,與view相關(guān)操作
*/
interfaceView{
// 定義Presenter
voidsetPresenter();
// 展示等待加載頁面
voidshowLoading();
// 隱藏等待加載頁面
voidhideLoading();
// 顯示登陸信息
voidshowLoginInfo(String msg);
}
/**
* 與業(yè)務相關(guān)
*/
interfacePresenter{
/**
* 登陸
* @param userId? ? ? 用戶id
* @param userPassword 密碼
*/
voidlogin(String userId, String userPassword);
}
}
第二步:
搞一個實現(xiàn)V接口的view類抡诞。本例中的view類就是LoginActivity。首先實現(xiàn)接口所有的方法是必須的土陪,然后這里有兩個地方需要注意
一昼汗,我們是在實現(xiàn)V層接口setPresenter方法里通過LoginPresenter的構(gòu)造方法將LoginActivity傳遞過去的(LoginPresenter是P的實現(xiàn)類),這里要記得主動調(diào)用一下setPresenter方法觸發(fā)初始化presenter這個事
二旺坠,就是所謂的MVP中V層和P層分隔開乔遮,UI和業(yè)務分隔開。比如本例登陸按鈕的點擊事件取刃,他并不是我們平時看到的直接擼網(wǎng)絡請求代碼蹋肮,而是調(diào)用了P層里的的方法出刷,說人話就是把網(wǎng)絡請求一大堆代碼封裝了一個方法放到了P層那個類里頭了,我們在調(diào)用的時候傳需要用到的參數(shù)就行了坯辩。你說坑不坑馁龟,這點玩意說的那么高大尚
/**
* MVP層中的View層
*/
publicclassLoginActivityextendsAppCompatActivityimplementsLoginContract.View{
privateLoginPresenter mPresenter;
privateEditText mEtName, mEtPwd;
privateLinearLayout mLayoutLoading;
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setPresenter();// 初始化presenter(很重要!不能說重寫就不管了漆魔,一定要在view初始化調(diào)用此方法)
mEtName = findViewById(R.id.et_name);// 用戶名
mEtPwd = findViewById(R.id.et_pwd);// 密碼
mLayoutLoading = findViewById(R.id.layout_loading);// 遮罩層
findViewById(R.id.btn_login).setOnClickListener(newView.OnClickListener() {// 登陸按鈕
@Override
publicvoidonClick(View view){// 點擊登陸不是直接請求網(wǎng)絡坷檩,而是通過presenter請求網(wǎng)絡,然后將請求回來的數(shù)據(jù)交給view來更新
mPresenter.login(mEtName.getText().toString().trim(), mEtPwd.getText().toString().trim());
}
});
}
@Override
publicvoidsetPresenter(){
mPresenter =newLoginPresenter(this);// 一是為了實例化presenter改抡,二是通過構(gòu)造方法將view實例傳遞給presenter
}
@Override
publicvoidshowLoading(){// 展示遮罩層
mLayoutLoading.setVisibility(View.VISIBLE);
}
@Override
publicvoidhideLoading(){// 隱藏遮罩層
mLayoutLoading.setVisibility(View.GONE);
}
@Override
publicvoidshowLoginInfo(String msg){// 吐司登陸信息
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
第三步:
搞一個實現(xiàn)P接口的presenter類矢炼。本例中presenter類就是LoginPresenter,可以看到他就一個構(gòu)造方法(用來關(guān)聯(lián)V層并獲取view實例化對象)和一個請求網(wǎng)絡數(shù)據(jù)的方法阿纤。高能預警:網(wǎng)絡請求前UI頁面需要展示一個遮罩層句灌,所以調(diào)用了view的showLoading方法,請求結(jié)束后UI頁面需要取消遮罩并吐司欠拾,所以調(diào)用了view的hideLoading方法和showLoginInfo方法
/**
* MVP中的P層
* Created by wangjiong on 2017/12/7
*/
publicclassLoginPresenterimplementsLoginContract.Presenter{
privateLoginContract.View mView;
publicLoginPresenter(LoginContract.View view){// 獲取到view的實例化對象
this.mView = view;
}
@Override
publicvoidlogin(String userId, String password){
mView.showLoading();// 調(diào)用view的展示遮罩方法(view用來更新具體的UI)
// 網(wǎng)絡請求(可以自己封裝一個網(wǎng)絡庫)
Retrofit retrofit =newRetrofit.Builder()
.baseUrl("http://192.168.1.101:8080/merclienttest/")// 請求的url
.addConverterFactory(GsonConverterFactory.create())// 設(shè)置Gson為實體類解析工具
.build();
LoginApi loginApi = retrofit.create(LoginApi.class);// 傳入一個接口類并返回此類的實例對象
Call call = loginApi.login(userId, password);// 調(diào)用類中定義的login方法
call.enqueue(newCallback() {// retrofit異步請求
@Override
publicvoidonResponse(Call call, Response response){
mView.hideLoading();// 調(diào)用view的隱藏遮罩方法(view用來更新具體的UI)
mView.showLoginInfo(response.body().getMsg());// 調(diào)用view的吐司方法(view用來更新具體的UI)
}
@Override
publicvoidonFailure(Call call, Throwable t){
mView.hideLoading();
}
});
}
}
全劇終
媽個雞這是啥玩意胰锌?完了?哈哈哈藐窄,沒錯這就是MVP资昧。就是這么簡單。只需要記住一個核心思想V層僅僅是處理UI頁面的(只管化妝接客荆忍,拉皮條找老鴇子P)格带,業(yè)務邏輯放在P層去處理(網(wǎng)絡請求,數(shù)據(jù)庫东揣,文件等)践惑,P處理完之后再調(diào)用一下V層已經(jīng)寫好的對應更新UI的方法即可
擴展
沒忘記Retrofit哦,簡單介紹下使用方法
分三步:
一嘶卧,搞一個接口類Api
二,搞一個對應的實體類bean
三凉袱,調(diào)用Retrofit方法請求網(wǎng)絡
第一步:搞一個Api接口芥吟。本例中是LoginApi
第一行代碼 @GET("login") 這個Get就代表get請求,如果換成Post那就代表post請求专甩。login代表接口名(接口名需要后臺提供)
第二行代碼?Call login(@Query("userId") String userId, @Query("password") String password); 其中 login 代表自己定義的一個方法钟鸵,名字隨便起(小駝峰式命名),@Query("userId") 代表接口中有個名為 userId 的參數(shù)涤躲,?String userId 代表給這個參數(shù)傳值棺耍,值為userId(名字隨便起)
/**
* 登陸接口
* Created by wangjiong on 2017/12/7.
*/
publicinterfaceLoginApi{
@GET("login")//get請求login接口(接口名需要后臺提供)
Calllogin(@Query("userId") String userId, @Query("password") String password);// 聲明一個login的方法(隨便寫),兩個string形參种樱,并返回一個實體類LoginBean
}
沒反應過來蒙袍?
jsonObject.put("userId",userId) 這樣寫懂了吧俊卤,第一個userId是接口里的參數(shù)名,第二個userId是你傳的字符串值害幅,名字隨便搞的消恍,叫啥都行
你也可以@Query("userId") String myUserId這就等價于
jsonObject.put("userId",myUserId??)? 這里只是為了說明問題,所以不必太在意細節(jié)
第二步以现,搞一個對應的實體類bean狠怨。這個對應本例中的LoginBean。推薦用GsonFormat插件自動生成邑遏,啥佣赖?沒聽過,你又不是妹子记盒,百度吧
publicclassLoginBean{
/**
* code : 200
* msg : 登陸成功
*/
privateString code;
privateString msg;
publicStringgetCode(){
returncode;
}
publicvoidsetCode(String code){
this.code = code;
}
publicStringgetMsg(){
returnmsg;
}
publicvoidsetMsg(String msg){
this.msg = msg;
}
}
第三步憎蛤,調(diào)用Retrofit方法請求網(wǎng)絡。做完上面兩步孽鸡,就可以正常調(diào)用了蹂午,咋用?你又不是妹子彬碱,參考LoginPresenter吧
/**
* MVP中的P層
* Created by wangjiong on 2017/12/7
*/
publicclassLoginPresenterimplementsLoginContract.Presenter{
privateLoginContract.View mView;
publicLoginPresenter(LoginContract.View view){// 獲取到view的實例化對象
this.mView = view;
}
@Override
publicvoidlogin(String userId, String password){
mView.showLoading();// 調(diào)用view的展示遮罩方法(view用來更新具體的UI)
// 網(wǎng)絡請求(可以自己封裝一個網(wǎng)絡庫)
Retrofit retrofit =newRetrofit.Builder()
.baseUrl("http://192.168.1.101:8080/merclienttest/")// 請求的url
.addConverterFactory(GsonConverterFactory.create())// 設(shè)置Gson為實體類解析工具
.build();
LoginApi loginApi = retrofit.create(LoginApi.class);// 傳入一個接口類并返回此類的實例對象
Call call = loginApi.login(userId, password);// 調(diào)用類中定義的login方法
call.enqueue(newCallback() {// retrofit異步請求
@Override
publicvoidonResponse(Call call, Response response){
mView.hideLoading();// 調(diào)用view的隱藏遮罩方法(view用來更新具體的UI)
mView.showLoginInfo(response.body().getMsg());// 調(diào)用view的吐司方法(view用來更新具體的UI)
}
@Override
publicvoidonFailure(Call call, Throwable t){
mView.hideLoading();
}
});
}
}
源碼地址:https://github.com/GodJiong/mvp