Android之通用MVP模式框架
在最近的學習中,我寫代碼都一直在使用通用的MVP模式框架,在使用的過程中,最讓我感觸非常深的是,整個代碼的層次感非常清晰,耦合度非常低,擴展非常方便,以及能很好的處理Presenter和View直接內存溢出情況。如果你在寫代碼,我非常建議你使用這種框架下,下面請跟着我的思路走吧。
1、先看以下的構架:
在這裏你將看到model層下的BaseModelInter、presenter層下的BasePresenter、view層下的BaseViewInter。下面我們來看看這兩個接口,以及一個抽象類的代碼。
BaseModelInter.java
public interface BaseModelInter {
}
BaseViewInter.java
public interface BaseViewInter {
}
BasePresenter.java
public abstract class BasePresenter<T extends BaseViewInter, M extends BaseModelInter> {
private WeakReference<T> weakReference;
protected M model;
public void attach(T t) {
weakReference = new WeakReference<>(t);
model = getModel();
}
public void deAttach() {
if (weakReference != null) {
weakReference.clear();
weakReference = null;
}
}
public boolean isViewAttached() {
return weakReference != null && weakReference.get() != null;
}
public T getView() {
if (weakReference != null) {
return weakReference.get();
}
return null;
}
protected abstract M getModel();
}
然後你會發現,我艹,怎麼那兩個接口中怎麼什麼都沒寫,你也把和這個代碼貼上來了,是不是找幹啊?沒錯,我就是要貼上來,就是不怕你幹我!好了,開個小玩笑,迴歸正題,其實這樣定義兩個接口主要是爲了擴展子接口,擴展子功能的,因爲我們的主題是爲了打造通用的框架,那它到底能擴展什麼呢?接着往下看你就知道了。然後再看這個抽象類BasePresenter,這個抽象類,最關鍵的就是解決Presenter和view直接在長時間請求網絡數據的情況下,爲了防止內存溢出而設計的,我們通過一個虛引用就能很好的解決這個問題了。然後attach方法主要是將需要與具體的presenter類做綁定的view關聯起來,並且將對應的model也綁定起來。而這個view類剛好是屬於BaseViewInter下的子類。deAttach方法主要是當view向presenter請求數據時,由於網絡請求時間過長,而view直接銷燬了,而presenter沒有即使返回數據,則deAttach就會把對應的view取消關聯。isViewAttached方法判斷view與presente是否關聯,getView是返回一個綁定好的view對象,getModel方法則是讓具體的presenter子類去創建具體的model對象。
2.下面看一個具體的實例:
FirstActiviy界面一需要向他對應的presenter層的FirstActivityPresenter請求數據AAAA,然後presenter去找與其具體對應的model層FirstActivityModelImp去拿數據。然後將數據返回給FirstActivity。
框架結構如下:
首先先看看BaseActivity.java和FirstActivity.java的代碼:
BaseActivity.java
public abstract class BaseActivity<T extends BasePresenter,V extends BaseViewInter> extends AppCompatActivity {
protected T presenter;//③
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);//④
presenter = getPresenter();//⑤
presenter.attach((V)this);//⑦
}
protected abstract T getPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
presenter.deAttach();
}
}
FirstActivity.java
public class FirstActivity extends BaseActivity<FirstActivityPresenter,FirstActivity> implements FirstActivityViewInter {
private TextView mTv; //①
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);//②
setContentView(R.layout.first_activity);//⑧
mTv = (TextView)findViewById(R.id.mTv);//⑨
presenter.load("我是第一個view,我要獲取數據,請presenter爲我拿回一個字符串AAAA");//10
}
@Override
public void doSth(String data) {
Log.i("IT_Real", "doSth: 我獲取了字符串" + data);
mTv.setText("我拿到的數據是:" + data);
}
@Override
protected FirstActivityPresenter getPresenter() {//⑥
return new FirstActivityPresenter();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
從上面代碼看firstActivity繼承了BaseActivity類。大家看看上面的執行順序吧。從上面的10個順序中,大家應該能理解吧, FirstActivity類首先開始執行,然後第6步將一個具體的FirstActivityPresenter返回給了父類的presenter。然後第7步是將FirstActivity和presenter進行關聯。最後執行第10步向具體的FirstActivityPresenter請求數據AAAA。
FirstActivityPresenter.java
public class FirstActivityPresenter extends BasePresenter<FirstActivity,FirstActivityModelImp> {
@Override
protected FirstActivityModelImp getModel() {
return new FirstActivityModelImp();
}
public void load(String request){
model.dealSth(request,new OnSendStrListener() {
@Override
public void sendAAAA(String aaaa) {
getView().doSth(aaaa);
}
});
}
}
load方法主要是向model去拿具體的數據,然後將具體的數據返回給view層。這個類繼承了BasePresenter類,可以看到,我們可以通過getView很好的拿到關聯的FirstActivity類的具體實例,也能通過model拿到FirstActivityModelImp的具體實例。這樣FirstActivityPresenter不持有FirstActivity的引用,就避免了內存溢出的發生。
FirstActivityViewInter.java
public interface FirstActivityViewInter extends BaseViewInter{
void doSth(String data);
}
上面這個接口中提供的就是所有FirstActivity中需要實現的功能,包括以後需要具體擴展FirstActivity中功能等,非常方便。
最後看看Model層中的類
FirstActivityModelInter.java
/**
* 所有FirstModelImp中需要實現的數據操作功能,以後需要操作上面功能,直接在這個接口中添加即可,
* 擴展性好。
*/
public interface FirstActivityModelInter extends BaseModelInter{
void dealSth(String request,OnSendStrListener listener);
}
OnsendStrListener.java
/**
* 具體要處理數據的回調接口,因爲View需要一個字符串AAAA,所以就給他提供一個方法發回一個字符串
* 具體的功能自己定義即可,這裏不統一,根據實際需求
*/
public interface OnSendStrListener {
void sendAAAA(String aaaa);
}
FIrstActiviyModelImp.java
public class FirstActivityModelImp implements FirstActivityModelInter {
/**
* 將具體的數據返回給FirstActivityPresenter,實現具體的數據操作功能。
*/
@Override
public void dealSth(String request, OnSendStrListener listener) {
if (request.equals("我是第一個view,我要獲取數據,請presenter爲我拿回一個字符串AAAA")){
Log.i("IT_Real", "dealSth: presenter需要一個AAAA數據,我要返回一個AAAA數據");
//接口回調該方法,就是將AAAA發回給presenter
listener.sendAAAA("AAAA");
}
}
}
通過上面的具體實現,在以後的項目中,我們可以擴展非常方便,這裏我只實例了一個界面需要處理的功能,以及請求上面數據等。如果說以後你需要擴展多個界面,就像我一樣,在view包下的activity下新建一個SecondActiviy類即可,然後該類繼承BaseActivity,創建一個新的SecondViewInter (需要繼承BaseViewInter),將所有功能寫入到其中,然後讓SecondActivity類去實現即可,next創建對應的SecondPresenter,去繼承BasePresenter。處理一些事情即可,最後同樣寫一個SecondModelImp類,去實現SecondModleInter接口(需要繼承BaseModelInter)中的具體功能即可。。。這樣一來,我們的層次會非常清晰,然後擴展功能也非常方便,耦合也非常低,源代碼上傳一下,希望大家也可以養成這樣的好習慣,建議使用這種模式寫代碼,對以後的工作會有很大的幫助哦。