MVP+Retrofit2+RxJava2
本Demo使用MVP+Retrofit2+RxJava2來寫的,如果大家對Retrofit2+RxJava都不會使用進(jìn)行網(wǎng)絡(luò)請求的話,請先查看我之前寫的文章Retrofit2.0+RxJava2.0封裝使用引矩,因?yàn)槲疫@里面網(wǎng)絡(luò)請求是直接使用之前封裝好的粘舟,這里就不再介紹了凉驻。這里只介紹Android MVP的簡單使用和封裝使用贝淤。
Demo地址:https://github.com/pengjunshan/MVPRetrofitRxJava
其它文章
Retrofit2+RxJava2 封裝使用
OkHttp3簡單使用和封裝使用
Android開發(fā) 多語言系羞、指紋登錄郭计、手勢登錄
Android使用IconFont阿里矢量圖標(biāo)
Android Studio 使用SVN 主干和分支合并代碼
主要講解內(nèi)容
- MVP封裝前簡單使用
- MVP對Activity封裝使用
- MVP對Frgment封裝使用
效果圖
MVP介紹
MVP全名Mode View Presenter,Presenter處理邏輯業(yè)務(wù)觉啊,Model提供數(shù)據(jù)拣宏,View更新展示界面。完全隔離界面顯示與業(yè)務(wù)邏輯杠人。
優(yōu)點(diǎn):
- 分離了視圖邏輯和業(yè)務(wù)邏輯勋乾,降低了耦合宋下。
- 單一職責(zé), Model辑莫, View学歧, Presenter只處理單一邏輯。
- Model層的修改和View層的修改互不影響各吨。
- 視圖邏輯和業(yè)務(wù)邏輯分別抽象到了View和Presenter的接口中去枝笨,提高代碼的可閱讀性
缺點(diǎn):
- 接口類過多,代碼量增加揭蜒。
- Presenter和View相互持有引用横浑,解除不及時(shí)的話容易出現(xiàn)內(nèi)存泄漏。
說明:
- 這里對MVP不做過多的解釋屉更,網(wǎng)上對MVP的解釋有一大堆基本說的都差不多徙融。
- 下面我會講解怎么盡可能的減少代碼量,和避免內(nèi)存泄漏的方法瑰谜。
- MVP并沒有一個(gè)統(tǒng)一標(biāo)準(zhǔn)模式欺冀,只要遵循View和Model相互分離就行。(按照自己喜歡的模式寫就行)
MVP簡單使用方法
用到的類
Concacts:契約類 將Model萨脑、View隐轩、Presenter 進(jìn)行約束管理,方便后期類的查找渤早、維護(hù)职车。
View:Activity 和Fragment 視為View層,負(fù)責(zé)處理 UI蛛芥。
Model:包含著具體的數(shù)據(jù)請求提鸟,數(shù)據(jù)源军援、本地存儲等等仅淑。
Presenter :為業(yè)務(wù)處理層,既能調(diào)用View邏輯胸哥,又能調(diào)用Model請求數(shù)據(jù)涯竟。
EaseConcacts 類
契約類 將Model、View空厌、Presenter 進(jìn)行約束管理庐船。在各自的接口類中定義不同需求的方法。IView接口類中創(chuàng)建一個(gè)接收Data數(shù)據(jù)的回調(diào)方法嘲更;IPresenter接口類中定義一個(gè)IView觸發(fā)網(wǎng)絡(luò)請求的方法筐钟;IModel接口類中定義一個(gè)真正網(wǎng)絡(luò)請求的方法和一個(gè)回調(diào)給IPresenter類的回調(diào)方法;
public class EaseConcacts {
interface IView {
/**
* View層獲取數(shù)據(jù)回調(diào)方法
*/
void onResultData(String data);
}
interface IPresenter {
/**
* View層向Presenter發(fā)送請求方法
*/
void requestData(Context context);
}
interface IModel {
/**
* Persenter層向Model發(fā)送請求方法
*/
void getData(Context context, ModelListener modelListener);
interface ModelListener {
/**
* Model層請求接口完成后回調(diào)Persenter層方法
*/
void onReslutJson(String test);
}
}
}
EaseActivity類
IView類中實(shí)現(xiàn)Concacts中的IView接口赋朦,并且初始化Presenter,然后點(diǎn)擊按鈕觸發(fā)Presenter類中的requestData方法篓冲;requestData方法中會觸發(fā)IModel中真正網(wǎng)絡(luò)請求方法李破;然后IView類通過onResultData回調(diào)方法接收數(shù)據(jù);
public class EaseActivity extends AppCompatActivity implements IView {
private EasePresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ease);
presenter = new EasePresenter(this);
findViewById(R.id.getJSON).setOnClickListener((view) -> presenter.requestData(this));
}
@Override
public void onResultData(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
}
EasePresenter類
首先要實(shí)現(xiàn)EaseConcacts契約類中的IPresenter接口壹将,并實(shí)現(xiàn)requestData方法,當(dāng)View調(diào)用requestData方法后Presenter會通過Mode進(jìn)行網(wǎng)絡(luò)請求,然后把結(jié)果回調(diào)過來。
public class EasePresenter implements EaseConcacts.IPresenter {
private EaseConcacts.IView iView;
private EaseModel iModle;
public EasePresenter(EaseConcacts.IView view) {
this.iView = view;
this.iModle = new EaseModel();
}
@Override
public void requestData(Context context) {
iModle.getData(context, test -> iView.onResultData(test));
}
EaseModel類
首先要實(shí)現(xiàn)EaseConcacts契約類中IModel接口缰盏,這里寫網(wǎng)絡(luò)業(yè)務(wù)代碼负溪,網(wǎng)絡(luò)請求完成后可以在此可以處理數(shù)據(jù) 增刪改查须尚、本地存儲等等密幔;然后把數(shù)據(jù)通過接口返回給EasePresenter類堪嫂;
public class EaseModel implements EaseConcacts.IModel {
@Override
public void getData(Context context, ModelListener modelListener) {
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
/**
* 在此可以處理數(shù)據(jù) 增刪改查、本地存儲等等
*/
Logout.e("data= "+data.toString());
modelListener.onReslutJson(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
modelListener.onReslutJson("失敗= "+failuer.getEmsg());
}
});
}
}
Model弱化
可以弱化Model的作用,把RetrofitClient網(wǎng)絡(luò)請求當(dāng)做Modle,可以省略Modle接口和類文件
public class EasePresenter implements EaseConcacts.IPresenter {
private EaseConcacts.IView iView;
private EaseModel iModle;
public EasePresenter(EaseConcacts.IView view) {
this.iView = view;
this.iModle = new EaseModel();
}
@Override
public void requestData(Context context) {
/**
* 方式一:調(diào)用Modle進(jìn)行調(diào)用接口
*/
//iModle.getData(context, test -> iView.onResultData(test));
/**
* 方式二:弱化了Model的作用流纹,這里RetrofitClient網(wǎng)絡(luò)請求就是Modle,可以省略一個(gè)Modle文件
*/
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
Logout.e("data= " + data.toString());
iView.onResultData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
iView.onResultData("失敗= " + failuer.getEmsg());
}
});
}
}
封裝MVP使用
上面說過MVP缺點(diǎn),Presenter和View相互持有引用感论,解除不及時(shí)的話容易出現(xiàn)內(nèi)存泄漏囊陡。下面我們要創(chuàng)建一些Base類來處理一些公共的代碼邏輯和防止內(nèi)存泄漏。
創(chuàng)建Base類
BaseActivity:通過泛型接收View和Presenter,進(jìn)行V和P的綁定和解綁觸發(fā)倚聚。
BaseFragment:通過泛型接收View和Presenter枯跑,進(jìn)行V和P的綁定和解綁觸發(fā)屋确。
BasePresenter:動態(tài)判斷View是否已銷毀刨啸,防止內(nèi)存泄漏离例。
BaseView:為了處理公共View邏輯雕拼。比如在BaseView中寫公共處理UI方法、通過泛型對View進(jìn)行綁定和解綁粘招。
BaseView類
在BaseActivity啥寇、BaseFragment、BasePresenter中都要處理View的邏輯洒扎,都需要用泛型來接收View辑甜,所以所有的IView都要繼承BaseView。
public interface BaseView {
//添加公共處理UI方法 比如處理接口失敗的UI
}
BasePresenter類
所有的Presenter類都要繼承BasePresenter袍冷,在Base里對View做了統(tǒng)一的綁定和解綁的代碼處理磷醋。還統(tǒng)一對View做了是否已解綁的判斷,防止調(diào)用View中的回調(diào)方法(防止內(nèi)存泄漏)胡诗。
public abstract class BasePresenter<V extends BaseView> {
private V view;
private V proxyView;
public V getView() {
return proxyView;
}
/**
* 綁定view
*/
public void attachView(V view) {
this.view = view;
//參數(shù)一:類加載器
ClassLoader classLoader = view.getClass().getClassLoader();
//參數(shù)二:代理接口
Class<?>[] interfaces = view.getClass().getInterfaces();
//參數(shù)三:方法回調(diào)
BaseViewInvocationHandler handler = new BaseViewInvocationHandler(view);
proxyView = (V) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
/**
* 解綁view
*/
public void detachView() {
this.view = null;
}
private class BaseViewInvocationHandler implements InvocationHandler {
private BaseView view;
BaseViewInvocationHandler(BaseView view) {
this.view = view;
}
//統(tǒng)一判斷->控制對象訪問權(quán)限
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//檢查是不是為null
if (isViewNull()) {
//不用回調(diào)
return null;
}
//執(zhí)行回調(diào)
return method.invoke(view, objects);
}
}
private boolean isViewNull() {
if (view == null) {
return true;
}
return false;
}
}
BaseActivity類
通過泛型接收View和Presenter邓线,進(jìn)行V和P的綁定和解綁觸發(fā)。還有提供創(chuàng)建Presenter類的抽象方法煌恢,和獲取Presenter類的構(gòu)造方法骇陈。
public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends
Activity implements BaseView {
private P presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
presenter = createPresenter();
}
/**
* 綁定view
*/
if (presenter != null) {
presenter.attachView((V) this);
}
}
/**
* 得到當(dāng)前的Presenter類
*/
public P getPresenter() {
return presenter;
}
/**
* 創(chuàng)建Presenter類
*/
public abstract P createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 解綁view
*/
if (presenter != null) {
presenter.detachView();
}
}
}
BaseFragment類
通過泛型接收View和Presenter,進(jìn)行V和P的綁定和解綁觸發(fā)瑰抵。還有提供創(chuàng)建Presenter類的抽象方法你雌,和獲取Presenter類的構(gòu)造方法。和BaseActivity處理方式是一樣的二汛。
public abstract class BaseFragment<V extends BaseView, P extends BasePresenter<V>> extends
Fragment implements BaseView {
private P presenter;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (presenter == null) {
presenter = createPresenter();
}
if (presenter != null) {
presenter.attachView((V) this);
}
}
public P getPresenter() {
return presenter;
}
public abstract P createPresenter();
@Override
public void onDestroyView() {
super.onDestroyView();
if (presenter != null) {
presenter.detachView();
}
}
}
Activity中使用
LoginConcacts契約類
來統(tǒng)一管理IView婿崭、IPresenter拨拓、IModel接口∶フ唬可以對Model進(jìn)行弱化渣磷,不僅少定義了IModel接口還可以少寫一個(gè)實(shí)現(xiàn)IModel接口類,從而減少了代碼量授瘦。
public class LoginConcacts {
interface IView extends BaseView {
/**
* 校驗(yàn)賬號密碼錯(cuò)誤的提示信息
*/
void showToast(String msg);
/**
* View中獲取接口信息的回調(diào)
*/
void onResultData(String data);
}
interface IPresenter {
/**
* 校驗(yàn)賬號密碼
*/
void checkData(String userName, String userPwd);
/**
* 請求數(shù)據(jù)
*/
void requestData(String userName, String userPwd);
}
/**
* Model可以弱化掉醋界,節(jié)省代碼量
*/
interface IModel {
void getData(Context context, String userName, String userPwd, ModelListener modelListener);
interface ModelListener {
void onReslutJson(String test);
}
}
}
LoginPresenter類
首先要繼承BasePresenter,通過泛型傳入LoginConcacts.IView奥务,再實(shí)現(xiàn)LoginConcacts.IPresenter接口類并實(shí)現(xiàn)里面的方法物独。在LoginPresenter里可以直接進(jìn)行網(wǎng)絡(luò)請求,把Retrofit網(wǎng)絡(luò)請求當(dāng)做Model(把Model弱化)氯葬,也可以調(diào)用LoginModel進(jìn)行網(wǎng)絡(luò)請求挡篓。
public class LoginPresenter extends BasePresenter<LoginConcacts.IView> implements
LoginConcacts.IPresenter {
private Context context;
private LoginModel modle;
public LoginPresenter(Context context) {
this.context = context;
this.modle = new LoginModel();
}
@Override
public void checkData(String userName, String userPwd) {
if (TextUtils.isEmpty(userName)) {
getView().showToast("請輸入賬號!");
} else if (TextUtils.isEmpty(userPwd)) {
getView().showToast("請輸入密碼帚称!");
} else {
requestData(userName, userPwd);
}
}
@Override
public void requestData(String userName, String userPwd) {
/**
* 方式一:調(diào)用Modle進(jìn)行調(diào)用接口
*/
// modle.getData(context,userName,userPwd, test -> getView().onResultData(test));
/**
* 方式二:把Modle弱引用掉官研,這里RetrofitClient網(wǎng)絡(luò)請求就是Modle,可以省略一個(gè)Modle文件
*/
Map<String, String> map = new HashMap<>();
map.put("username", userName);
map.put("password", userPwd);
RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
new IResponseListener<LoginBean>() {
@Override
public void onSuccess(LoginBean data) {
Logout.e("data= " + data.toString());
getView().onResultData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
getView().onResultData("失敗= " + failuer.getEmsg());
}
});
}
}
LoginModel類
首先要實(shí)現(xiàn)IModel接口類并實(shí)現(xiàn)方法闯睹,Model中不光是進(jìn)行網(wǎng)絡(luò)請求的戏羽,其它業(yè)務(wù)邏輯都可以處理。這里是進(jìn)行網(wǎng)絡(luò)請求楼吃,然后通過接口返回給Presenter始花。
public class LoginModel implements IModel {
@Override
public void getData(Context context, String userName, String userPwd,
ModelListener modelListener) {
Map<String, String> map = new HashMap<>();
map.put("username", userName);
map.put("password", userPwd);
RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
new IResponseListener<LoginBean>() {
@Override
public void onSuccess(LoginBean data) {
Logout.e("data= " + data.toString());
modelListener.onReslutJson(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
modelListener.onReslutJson("失敗= " + failuer.getEmsg());
}
});
}
}
LoginActivity類
LoginActivity就是View的實(shí)現(xiàn)類,首先要繼承BaseActivity類并通過泛型傳入View和Presenter孩锡。然后實(shí)現(xiàn)LoginConcacts.IView接口并實(shí)現(xiàn)其中的方法酷宵。
public class LoginActivity extends
BaseActivity<LoginConcacts.IView, LoginPresenter> implements LoginConcacts.IView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
findViewById(R.id.getJSON)
.setOnClickListener((view) -> getPresenter().checkData("賬號", "密碼"));
}
@Override
public LoginPresenter createPresenter() {
return new LoginPresenter(this);
}
@Override
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onResultData(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
}
Fragment中使用
BannerConcacts類
來統(tǒng)一管理IView、IPresenter的接口躬窜,這里我對Model弱化掉了不再提供Model的實(shí)現(xiàn)類了浇垦。
public class BannerConcacts {
interface IView extends BaseView {
/**
* 獲取接口數(shù)據(jù)回調(diào)方法
*/
void onReslutData(String data);
}
interface IPresenter {
/**
* 進(jìn)行網(wǎng)絡(luò)業(yè)務(wù)處理
*/
void requestData(Context context);
}
}
BannerPresenter類
首先繼承BasePresenter類通過泛型傳入相應(yīng)的IView,然后再實(shí)現(xiàn)BannerConcacts.IPresenter接口并實(shí)現(xiàn)其中的方法荣挨。這里沒有調(diào)用Model來進(jìn)行業(yè)務(wù)處理男韧,對Model弱化了
public class BannerPresenter extends BasePresenter<BannerConcacts.IView> implements
BannerConcacts.IPresenter {
@Override
public void requestData(Context context) {
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
Logout.e("Tag", "data= " + data.toString());
getView().onReslutData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
getView().onReslutData("失敗= " + failuer.getEmsg());
}
});
}
}
BannerFragment類
BannerFragment就是View的實(shí)現(xiàn)類,首先要繼承BaseFragment類并通過泛型傳入View和Presenter默垄。然后實(shí)現(xiàn)BannerConcacts.IView接口并實(shí)現(xiàn)其中的方法此虑。使用方式和Activity一樣。
public class BannerFragment extends BaseFragment<BannerConcacts.IView, BannerPresenter> implements
BannerConcacts.IView {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.banner, null);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.getJSON)
.setOnClickListener((v) -> getPresenter().requestData(getContext()));
}
@Override
public BannerPresenter createPresenter() {
return new BannerPresenter();
}
@Override
public void onReslutData(String data) {
Toast.makeText(getContext(), data, Toast.LENGTH_SHORT).show();
}
}
我對MVP的理解
有些人說MVP適合大型項(xiàng)目厕倍,我不這么認(rèn)為寡壮。之前我做過某網(wǎng)的項(xiàng)目使用MVC模式,有些Activity代碼行能達(dá)到兩千多行代碼讹弯,查找問題很麻煩况既。我說下我的看法,我認(rèn)為View層業(yè)務(wù)邏輯比較多的話就適合MVP模式组民,業(yè)務(wù)邏輯比較少的話看個(gè)人你可以用MVP也可以用MVC棒仍。還有就是封裝一個(gè)適合自己項(xiàng)目的框架。