高級MVP架構(gòu)封裝演變展示

先上項(xiàng)目代碼

MVP 全稱:Model-View-Presenter 孝偎;MVP 是從經(jīng)典的模式MVC演變而來惠昔,它們的基本思想有相通的地方
Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù),View負(fù)責(zé)顯示。
作為一種新的模式,MVP與MVC有著一個(gè)重大的區(qū)別:在MVP中View并不直接使用Model鱼响,它們之間的通信是通過Presenter (MVC中的Controller)來進(jìn)行的,所有的交互都發(fā)生在Presenter內(nèi)部组底,而在MVC中View會直接從Model中讀取數(shù)據(jù)而不是通過 Controller丈积。
優(yōu)點(diǎn):

  1. 模型與視圖完全分離筐骇,我們可以修改視圖而不影響模型
  2. 可以更高效地使用模型,因?yàn)樗械慕换ザ及l(fā)生在一個(gè)地方——Presenter內(nèi)部
  3. 我們可以將一個(gè)Presenter用于多個(gè)視圖江滨,而不需要改變Presenter的邏輯铛纬。這個(gè)特性非常的有用,因?yàn)橐晥D的變化總是比模型的變化頻繁唬滑。
  4. 如果我們把邏輯放在Presenter中告唆,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)

在MVC里,View是可以直接訪問Model的晶密!從而擒悬,View里會包含Model信息,不可避免的還要包括一些業(yè)務(wù)邏輯稻艰。 在MVC模型里懂牧,更關(guān)注的Model的不變,而同時(shí)有多個(gè)對Model的不同顯示尊勿,即View僧凤。所以,在MVC模型里运怖,Model不依賴于View拼弃,但是View是依賴于Model的夏伊。不僅如此摇展,因?yàn)橛幸恍I(yè)務(wù)邏輯在View里實(shí)現(xiàn)了,導(dǎo)致要更改View也是比較困難的溺忧,至少那些業(yè)務(wù)邏輯是無法重用的咏连。

雖然 MVC 中的 View 的確“可以”訪問 Model,但是我們不建議在 View 中依賴 Model鲁森,而是要求盡可能把所有業(yè)務(wù)邏輯都放在 Controller 中處理祟滴,而 View 只和 Controller 交互。

本文不講解什么是MVP歌溉,如果還不太了解MVP請自行查閱資料垄懂,并看我另外一個(gè)項(xiàng)目

本文重點(diǎn)是封裝一個(gè)高級MVP架構(gòu),會詳細(xì)的講解如何一步步從無到有的封裝成一個(gè)高級MVP架構(gòu)過程痛垛。
眾所周知普通的MVP模式存在內(nèi)存泄露草慧、代碼冗余、界面意外關(guān)閉后在重建數(shù)據(jù)緩存等問題匙头,本文最終封裝的成果為一一解決這些問題漫谷,而且在使用過程中盡量做到使用簡單而且可擴(kuò)展,當(dāng)然本文也只是提供了一種封裝思路而已,如果不能滿足你的需求還可以自行再進(jìn)行擴(kuò)展蹂析。

如果你覺得你不想看整個(gè)實(shí)現(xiàn)思路可以直接看最后的源碼舔示,望給點(diǎn)個(gè)star鼓勵一下

文章會以5個(gè)部分來整體優(yōu)化封裝MVP碟婆,也是一個(gè)從無到有的過程

一、不使用MVP的代碼

二惕稻、最簡單的MVP實(shí)現(xiàn)

三竖共、解決MVP內(nèi)存泄露

四、簡單抽象-解決MVP代碼冗余

五俺祠、高級抽象-使用注解肘迎、工廠模式、代理模式锻煌、策略模式整體解決代碼冗余妓布、內(nèi)存泄露、Presenter生命周期以及數(shù)據(jù)存儲問題

進(jìn)入正題宋梧。

場景:假如界面有一個(gè)按鈕匣沼,點(diǎn)擊后請求數(shù)據(jù),然后成功后將返回的數(shù)據(jù)設(shè)置到一個(gè)Textview中

一捂龄、不使用MVP的代碼,一般我們會這么寫

public class MainActivity extends AppCompatActivity {
    @FieldView(R.id.tv_text)
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewFind.bind(this);
    }

    //按鈕點(diǎn)擊事件
    public void request(View view) {
        clickRequest("101010100");
    }

    //發(fā)起請求
    public void clickRequest(final String cityId) {
        //請求接口
        Retrofit retrofit = new Retrofit.Builder()
                //代表root地址
                .baseUrl("http://www.weather.com.cn/")
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        ApiService apiService = retrofit.create(ApiService.class);

        //請求
        Call<WeatherBean> weatherBeanCall = apiService.requestWeather(cityId);

        weatherBeanCall.enqueue(new Callback<WeatherBean>() {
            @Override
            public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                //請求成功
                textView.setText(response.body().getWeatherinfo().toString());
            }

            @Override
            public void onFailure(Call<WeatherBean> call, Throwable t) {
                //請求失敗
            }
        });
    }

}

上面的代碼是最原始的寫法释涛,下面我們使用最簡單的MVP模式來改造這個(gè)代碼。

二倦沧、最簡單的MVP實(shí)現(xiàn)

思路如下:
1唇撬、首先我們先定義一個(gè)接口,用來規(guī)定針對這個(gè)界面邏輯View需要作出的動作的接口展融。
2窖认、讓Activity實(shí)現(xiàn)這個(gè)接口中的方法,也就是V層
3告希、創(chuàng)建一個(gè)類扑浸,用來封裝之前的網(wǎng)絡(luò)請求過程,也就是M層
4燕偶、再創(chuàng)建一個(gè)類喝噪,用來處理M層和V層之間的通信,也就是P層

下面來實(shí)現(xiàn)一下:

1指么、首先我們先定義一個(gè)接口酝惧,用來規(guī)定針對這個(gè)界面邏輯View需要作出的動作的接口。

/**
 * @author zhuxh
 * @date 2017/11/16
 * @description V層接口伯诬,定義V層需要作出的動作的接口
 */
public interface RequestView1 {
    //請求時(shí)展示加載
    void requestLoading();
    //請求成功
    void resultSuccess(WeatherBean result);
    //請求失敗
    void resultFailure(String result);
}

2晚唇、讓Activity實(shí)現(xiàn)這個(gè)接口中的方法,也就是V層

 /**
 *   第二步:對應(yīng)demo1
 * 2缺亮,最簡單的mvp模式,
 * 1.Activity需要實(shí)現(xiàn)v層接口
 * 2.Persenter需要持有v層引用和m層引用
 * 3.在實(shí)現(xiàn)類view中創(chuàng)建persenter
 *
 */
public class MainActivity extends AppCompatActivity implements RequestView1 {

    @FieldView(R.id.tv_text)
    private TextView textView;
    private RequestPresenter1 presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewFind.bind(this);

        //創(chuàng)建Presenter
        presenter = new RequestPresenter1(this);
    }

    //點(diǎn)擊事件
    public void request(View view) {
        presenter.clickRequest("101010100");
    }

    //請求時(shí)加載
    @Override
    public void requestLoading() {
        textView.setText("請求中,請稍后...");
    }

    //請求成功
    @Override
    public void resultSuccess(WeatherBean result) {
        //成功
        textView.setText(result.getWeatherinfo().toString());
    }

    //請求失敗
    @Override
    public void resultFailure(String result) {
        //失敗
        textView.setText(result);
    }
}

3、創(chuàng)建一個(gè)類,用來封裝之前的網(wǎng)絡(luò)請求過程萌踱,也就是M層

/**
 * @author zhuxh
 * @date 2017/11/16
 * @description M層 數(shù)據(jù)層
 */
public class RequestMode1 {

    private static final String BASE_URL = "http://www.weather.com.cn/";

    //http://www.weather.com.cn/data/cityinfo/101010100.html
   public void request(String detailId, Callback<WeatherBean> callback){
       //請求接口
       Retrofit retrofit  = new Retrofit.Builder()
               //代表root地址
               .baseUrl(BASE_URL)
               .addConverterFactory(ScalarsConverterFactory.create())
               .addConverterFactory(GsonConverterFactory.create())
               .build();

       ApiService apiService = retrofit.create(ApiService.class);

       //請求
       Call<WeatherBean> weatherBeanCall = apiService.requestWeather(detailId);

       weatherBeanCall.enqueue(callback);
   }
}

4葵礼、再創(chuàng)建一個(gè)類,用來處理M層和V層之間的通信并鸵,也就是P層

/**
 * @author zhuxh
 * @date 2017/11/16
 * @description P層
 * 特點(diǎn):需要持有M層和V層
 */
public class RequestPresenter1 {

    private final RequestView1 mRequestView;
    private final RequestMode1 mRequestMode;

    public RequestPresenter1(RequestView1 requestView) {
        this.mRequestView = requestView;
        this.mRequestMode = new RequestMode1();
    }

    public void clickRequest(final String cityId){
        //請求時(shí)顯示加載
        mRequestView.requestLoading();

        //模擬耗時(shí)鸳粉,可以展示出loading
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mRequestMode.request(cityId, new Callback<WeatherBean>() {
                    @Override
                    public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                        mRequestView.resultSuccess(response.body());
                    }

                    @Override
                    public void onFailure(Call<WeatherBean> call, Throwable t) {
                        mRequestView.resultFailure(Log.getStackTraceString(t));
                    }
                });
            }
        },1000);

    }
}

好了,上面的4步就是最基本的MVP模式的使用了园担,可是這樣寫會內(nèi)存泄露届谈,因?yàn)槿绻诰W(wǎng)絡(luò)請求的過程中Activity就關(guān)閉了,Presenter還持有了V層的引用弯汰,也就是MainActivity艰山,就會內(nèi)存泄露。

三咏闪、解決MVP內(nèi)存泄露

下面就來解決這個(gè)問題曙搬,我們將P層和V層的關(guān)聯(lián)抽出兩個(gè)方法,一個(gè)綁定鸽嫂,一個(gè)解綁纵装,在需要的時(shí)候進(jìn)行綁定V層,不需要的時(shí)候進(jìn)行解綁就可以了据某。我們只需要修改上面Presenter中的構(gòu)造代碼橡娄,不需要在構(gòu)造中傳遞V層了,然后再寫一個(gè)綁定和解綁的方法癣籽,最后修改Activity創(chuàng)建Presenter時(shí)進(jìn)行綁定挽唉,在onDestroy中進(jìn)行解綁。

修改后的Presenter:

/**
 * @author zhuxh
 * @date 2017/11/16
 * @description
 */
public class RequestPresenter2 {

    private RequestView2 mView;
    private RequestMode2 mMode;

    public RequestPresenter2() {
        mMode = new RequestMode2();
    }

    public void clickRequest(final String cityId) {
        if(mView != null){
            mView.requestLoading();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mMode.request(cityId, new Callback<WeatherBean>() {
                        @Override
                        public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                            if(mView != null){
                                mView.resultSuccess(response.body());
                            }
                        }

                        @Override
                        public void onFailure(Call<WeatherBean> call, Throwable t) {
                            if(mView != null){
                                mView.resultFailure(Log.getStackTraceString(t));
                            }
                        }
                    });
                }
            },1000);
        }
    }

    /**
     * 綁定
     * @param view
     */
    public void attach( RequestView2 view) {
        this.mView = view;
    }

    /**
     * 解除綁定
     */
    public void detach() {
        mView = null;
    }

    /**
     * 取消網(wǎng)絡(luò)請求
     */
    public void interruptHttp(){
        mMode.interruptHttp();
    }
}

修改后的MainActivity:

   /**
     * 第三步:對應(yīng)demo2
     * 上面的問題:
     * 1.是會內(nèi)存泄露才避,因?yàn)閜ersenter一直持有Activity橱夭,如果一個(gè)發(fā)了一個(gè)請求,但是網(wǎng)絡(luò)有點(diǎn)慢桑逝,這個(gè)時(shí)候退出Activity,那么請求回來后還是會調(diào)用
     * Activity的回調(diào)方法俏让,這里還是因?yàn)橐恢背钟械膯栴}
     * 2.如果已經(jīng)退出了當(dāng)前界面楞遏,這個(gè)請求也沒有用了,這個(gè)時(shí)候我們可以斷開請求
     * <p>
     * 解決問題:
     * 1.增加綁定和解綁的方法來解決內(nèi)存泄露和退出后還會回調(diào)的問題
     * 2首昔、增加斷開網(wǎng)絡(luò)連接的方法
     */
public class MainActivity extends AppCompatActivity implements RequestView2 {

    @FieldView(R.id.tv_text)
    private TextView textView;
    private RequestPresenter2 presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewFind.bind(this);
        presenter = new RequestPresenter2();
        presenter.attach(this);
    }

    public void request(View view) {
        presenter.clickRequest("101010100");
    }

    @Override
    public void requestLoading() {
        textView.setText("請求中,請稍后...");
    }

    @Override
    public void resultSuccess(WeatherBean result) {
        //成功
        textView.setText(result.getWeatherinfo().toString());
    }

    @Override
    public void resultFailure(String result) {
        //失敗
        textView.setText(result);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detach();
        presenter.interruptHttp();
    }

}

這樣我們就解決了內(nèi)存泄露的問題寡喝,但是這樣還是不完美,應(yīng)用中肯定不可能只有一個(gè)模塊勒奇,每個(gè)模塊都對應(yīng)著一個(gè)V層和P層预鬓,那這樣的話每個(gè)Presenter中都要定義綁定和解綁的方法,而Activity中對應(yīng)的也要調(diào)用這綁定和解綁的兩個(gè)方法赊颠,代碼冗余格二。

四劈彪、簡單抽象-解決MVP代碼冗余

針對這個(gè)問題我們可以抽取出一個(gè)基類的Presenter和一個(gè)基類的Activity來做這個(gè)事情,讓子類不用在寫這些重復(fù)的代碼顶猜。但是問題又來了沧奴,既然是基類,肯定不止有一個(gè)子類來繼承基類长窄,那么也就是說子類當(dāng)中定義的View接口和需要創(chuàng)建的Presenter都不相同滔吠,我們肯定在基類當(dāng)中不能寫死吧,那就使用泛型來設(shè)計(jì)挠日。

思路:
1.創(chuàng)建一個(gè)基類View疮绷,讓所有View接口都必須實(shí)現(xiàn),這個(gè)View可以什么都不做只是用來約束類型的

2.創(chuàng)建一個(gè)基類的Presenter,在類上規(guī)定View泛型嚣潜,然后定義綁定和解綁的抽象方法矗愧,讓子類去實(shí)現(xiàn),對外在提供一個(gè)獲取View的方法郑原,
讓子類直接通過方法來獲取View

3.創(chuàng)建一個(gè)基類的Activity唉韭,聲明一個(gè)創(chuàng)建Presenter的抽象方法,因?yàn)橐獛妥宇惾ソ壎ê徒饨壞敲淳托枰玫阶宇惖腜resenter才行犯犁,但是又不能隨便一個(gè)類都能綁定的属愤,因?yàn)橹挥谢惖腜resenter中才定義了綁定和解綁的方法,所以同樣的在類上可以聲明泛型在酸役,方法上使用泛型來達(dá)到目的住诸。

4.修改Presenter和Activity中的代碼,各自繼承自己的基類并去除重復(fù)代碼

實(shí)現(xiàn)步驟:

1.創(chuàng)建一個(gè)基類View涣澡,讓所有View接口都必須實(shí)現(xiàn)

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description 所有mvpView的父接口
 */
public interface IMvpBaseView4 {
}

2.創(chuàng)建一個(gè)基類的Presenter贱呐,在類上規(guī)定View泛型,然后定義綁定和解綁的方法入桂,對外在提供一個(gè)獲取View的方法奄薇,讓子類直接通過方法來獲取View使用即可

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description 指定綁定的View必須繼承自IMvpBaseView4
 */
public abstract class AbstractMvpPersenter4<V extends IMvpBaseView4> {

    private V mMvpView;

    /**
     * 綁定V層
     * @param view
     */
    public void attachMvpView(V view){
        this.mMvpView = view;
    }

    /**
     * 解除綁定V層
     */
    public void detachMvpView(){
        mMvpView = null;
    }

    /**
     * 獲取V層
     * @return
     */
    public V getmMvpView() {
        return mMvpView;
    }
}

3.創(chuàng)建一個(gè)基類的Activity,聲明一個(gè)創(chuàng)建Presenter的抽象方法抗愁,因?yàn)橐獛妥宇惾ソ壎ê徒饨壞敲淳托枰玫阶宇惖腜resenter才行馁蒂,但是又不能隨便一個(gè)類都能綁定的镜廉,因?yàn)橹挥谢惖腜resenter中才定義了綁定和解綁的方法吧凉,所以同樣的在類上可以聲明泛型在方法上使用泛型來達(dá)到目的

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description MvpActivity
 * 指定子類具體的View必須繼承自IMvpBaseView4
 * 指定子類具體的Presenter必須繼承自AbstractMvpPersenter4
 */
public abstract class AbstractMvpActivity<V extends IMvpBaseView4, P extends AbstractMvpPersenter4<V>> extends AppCompatActivity implements IMvpBaseView4 {

    private P presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //創(chuàng)建Presenter
        if (presenter == null) {
            presenter = createPresenter();
        }

        if (presenter == null) {
            throw new NullPointerException("presenter 不能為空!");
        }
        //綁定view
        presenter.attachMvpView((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除綁定
        if (presenter != null) {
            presenter.detachMvpView();
        }
    }

    /**
     * 創(chuàng)建Presenter
     * @return 子類自己需要的Presenter
     */
    protected abstract P createPresenter();

    /**
     * 獲取Presenter
     * @return 返回子類創(chuàng)建的Presenter
     */
    public P getPresenter() {
        return presenter;
    }
}

4.修改Presenter和Activity中的代碼,各自繼承自己的基類并去除重復(fù)代碼

修改后的Presenter:

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description P層
 */
public class RequestPresenter4 extends AbstractMvpPersenter4<RequestView4> {

    private final RequestMode4 mRequestMode;

    public RequestPresenter4() {
        this.mRequestMode = new RequestMode4();
    }

    public void clickRequest(final String cityId){
        //獲取View
        if(getmMvpView() != null){
            getmMvpView().requestLoading();
        }
        //模擬網(wǎng)絡(luò)延遲俱饿,可以顯示出加載中
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mRequestMode.request(cityId, new Callback<WeatherBean>() {
                    @Override
                    public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                        //判斷View是否為空
                        if(getmMvpView() != null){
                            getmMvpView().resultSuccess(response.body());
                        }
                    }

                    @Override
                    public void onFailure(Call<WeatherBean> call, Throwable t) {
                        if(getmMvpView() != null){
                            getmMvpView().resultFailure(Log.getStackTraceString(t));
                        }
                    }
                });
            }
        },1000);
    }

    public void interruptHttp(){
        mRequestMode.interruptHttp();
    }
}

修改后的Activity:

public class MainActivity extends AbstractMvpActivity<RequestView4, RequestPresenter4> implements RequestView4 {

    @FieldView(R.id.tv_text)
    private TextView textView;
    private RequestPresenter3 presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewFind.bind(this);
    }

    @Override
    protected RequestPresenter4 createPresenter() {
        return new RequestPresenter4();
    }

    //點(diǎn)擊事件
    public void request(View view) {
        getPresenter().clickRequest("101010100");
    }

    @Override
    public void requestLoading() {
        textView.setText("請求中,請稍后...");
    }

    @Override
    public void resultSuccess(WeatherBean result) {
        //成功
        textView.setText(result.getWeatherinfo().toString());
    }

    @Override
    public void resultFailure(String result) {
        //失敗
        textView.setText(result);
    }
}   

到這里完美了嗎撮珠?沒有沮脖,還可以再抽,來分析一下還有哪些不完美的地方以及如何再優(yōu)化。

五勺届、高級抽象-使用注解驶俊、工廠模式、代理模式涮因、策略模式整體解決代碼冗余废睦、內(nèi)存泄露、Presenter生命周期以及數(shù)據(jù)存儲問題

1.每個(gè)子類都要重寫父類創(chuàng)建Presenter的方法养泡,創(chuàng)建一個(gè)Presenter并返回嗜湃,這一步我們也可以讓父類幫忙干了,怎么做呢澜掩?我們可以采用注解的方式购披,在子類上聲明一個(gè)注解并注明要創(chuàng)建的類型,剩下的事情就讓父類去做了,但是父類得考慮如果子類不想這么干怎么辦肩榕,那也還是不能寫死吧刚陡,可以使用策略模式加工廠模式來實(shí)現(xiàn),我們默認(rèn)使用這種注解的工廠
株汉,但是如果子類不喜歡可以通過父類提供的一個(gè)方法來創(chuàng)建自己的工廠筐乳。

2.Presenter真正的創(chuàng)建過程,我們可以將它放到真正使用Presenter的時(shí)候再創(chuàng)建乔妈,這樣的話可以稍微優(yōu)化一下性能問題

3.界面有可能會意外銷毀并重建蝙云,Activity、Fragment路召、View都可以在銷毀的時(shí)候通過onDestroy釋放一些資源并在onSaveInstanceState方法中存儲一些數(shù)據(jù)然后在重建的時(shí)候恢復(fù)勃刨,但是有可能Presenter中也需要釋放一些資源存儲一些數(shù)據(jù),那么上面的結(jié)構(gòu)就不能滿足了股淡,我們可以給Presenter增加生命周期的方法身隐,讓Presenter和V層生命周期同步就可以做到了

4.第三步中我們又給Presenter加入了一些生命周期的方法,再加上Presenter的創(chuàng)建綁定和解綁的方法唯灵,那么如果我們在創(chuàng)建一個(gè)MvpFragment基類贾铝,或者View的基類那么這么多的代碼豈不是都要copy一份嗎,而且看起來也很不清晰早敬,這里我們可以采用代理模式來優(yōu)化一下

好了下面來實(shí)現(xiàn):

1.我們既然要采用工廠模式才創(chuàng)建Presenter忌傻,那么我們首先來創(chuàng)建一個(gè)工廠接口

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description Presenter工廠接口
 */
public interface PresenterMvpFactory<V extends BaseMvpView,P extends BaseMvpPresenter<V>> {

    /**
     * 創(chuàng)建Presenter的接口方法
     * @return 需要創(chuàng)建的Presenter
     */
    P createMvpPresenter();
}

2.然后我們需要創(chuàng)建一個(gè)默認(rèn)使用注解創(chuàng)建的工廠,那么首先要創(chuàng)建一個(gè)注解

注解:

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description 標(biāo)注創(chuàng)建Presenter的注解
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
    Class<? extends BaseMvpPresenter> value();
}

注解工廠:

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description Presenter工廠實(shí)現(xiàn)類
 */
public class PresenterMvpFactoryImpl<V extends BaseMvpView, P extends BaseMvpPresenter<V>> implements PresenterMvpFactory<V, P> {

    /**
     * 需要創(chuàng)建的Presenter的類型
     */
    private final Class<P> mPresenterClass;

    /**
     * 根據(jù)注解創(chuàng)建Presenter的工廠實(shí)現(xiàn)類
     * @param viewClazz 需要創(chuàng)建Presenter的V層實(shí)現(xiàn)類
     * @param <V> 當(dāng)前View實(shí)現(xiàn)的接口類型
     * @param <P> 當(dāng)前要創(chuàng)建的Presenter類型
     * @return 工廠類
     */
    public static <V extends BaseMvpView, P extends BaseMvpPresenter<V>> PresenterMvpFactoryImpl<V,P> createFactory(Class<?> viewClazz){
        CreatePresenter annotation = viewClazz.getAnnotation(CreatePresenter.class);
        Class<P> aClass = null;
        if(annotation != null){
            aClass = (Class<P>) annotation.value();
        }
        return aClass == null ? null : new PresenterMvpFactoryImpl<V, P>(aClass);
    }

    private PresenterMvpFactoryImpl(Class<P> presenterClass) {
        this.mPresenterClass = presenterClass;
    }

    @Override
    public P createMvpPresenter() {
        try {
            return mPresenterClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Presenter創(chuàng)建失敗!搞监,檢查是否聲明了@CreatePresenter(xx.class)注解");
        }
    }
}

3.我們說了不能寫死這個(gè)工廠,那么我們需要使用者可以自定義镰矿,那么我們還需要給使用者提供一個(gè)設(shè)置的方法琐驴,我們定義一個(gè)接口提供設(shè)置工廠、獲取工廠、獲取Presenter的方法绝淡,然后讓V層來實(shí)現(xiàn)這個(gè)接口宙刘,這樣V層的子類就可以通過相應(yīng)的方法使用了

/**
 * @author zhuxh
 * @date 2017/11/20
 * @description 代理接口
 */
public interface PresenterProxyInterface<V extends BaseMvpView,P extends BaseMvpPresenter<V>> {

    /**
     * 設(shè)置創(chuàng)建Presenter的工廠
     * @param presenterFactory PresenterFactory類型
     */
    void setPresenterFactory(PresenterMvpFactory<V,P> presenterFactory);

    /**
     * 獲取Presenter的工廠類
     * @return 返回PresenterMvpFactory類型
     */
    PresenterMvpFactory<V,P> getPresenterFactory();

    /**
     * 獲取創(chuàng)建的Presenter
     * @return 指定類型的Presenter
     */
    P getMvpPresenter();

}

4.給Presenter增加生命周期的方法

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description 所有Presenter的基類,并不強(qiáng)制實(shí)現(xiàn)這些方法牢酵,有需要在重寫
 */
public class BaseMvpPresenter<V extends BaseMvpView> {

    /**
     * V層view
     */
    private V mView;

    /**
     * Presenter被創(chuàng)建后調(diào)用
     *
     * @param savedState 被意外銷毀后重建后的Bundle
     */
    public void onCreatePresenter(@Nullable Bundle savedState) {
        Log.e("perfect-mvp","P onCreatePersenter = ");
    }

    /**
     * 綁定View
     */
    public void onAttachMvpView(V mvpView) {
        mView = mvpView;
        Log.e("perfect-mvp","P onResume");
    }

    /**
     * 解除綁定View
     */
    public void onDetachMvpView() {
        mView = null;
        Log.e("perfect-mvp","P onDetachMvpView = ");
    }

    /**
     * Presenter被銷毀時(shí)調(diào)用
     */
    public void onDestroyPresenter() {
        Log.e("perfect-mvp","P onDestroy = ");
    }

    /**
     * 在Presenter意外銷毀的時(shí)候被調(diào)用悬包,它的調(diào)用時(shí)機(jī)和Activity、Fragment馍乙、View中的onSaveInstanceState
     * 時(shí)機(jī)相同
     *
     * @param outState
     */
    public void onSaveInstanceState(Bundle outState) {
        Log.e("perfect-mvp","P onSaveInstanceState = ");
    }

    /**
     * 獲取V層接口View
     *
     * @return 返回當(dāng)前MvpView
     */
    public V getMvpView() {
        return mView;
    }
}

5.創(chuàng)建一個(gè)代理來管理Presenter的生命周期方法

/**
 * @author zhuxh
 * @date 2017/11/20
 * @description 代理實(shí)現(xiàn)類布近,用來管理Presenter的生命周期,還有和view之間的關(guān)聯(lián)
 */
public class BaseMvpProxy<V extends BaseMvpView, P extends BaseMvpPresenter<V>> implements PresenterProxyInterface<V, P>{

    /**
     * 獲取onSaveInstanceState中bundle的key
     */
    private static final String PRESENTER_KEY = "presenter_key";

    /**
     * Presenter工廠類
     */
    private PresenterMvpFactory<V, P> mFactory;
    private P mPresenter;
    private Bundle mBundle;
    private boolean mIsAttchView;

    public BaseMvpProxy(PresenterMvpFactory<V, P> presenterMvpFactory) {
        this.mFactory = presenterMvpFactory;
    }

    /**
     * 設(shè)置Presenter的工廠類,這個(gè)方法只能在創(chuàng)建Presenter之前調(diào)用,也就是調(diào)用getMvpPresenter()之前丝格,如果Presenter已經(jīng)創(chuàng)建則不能再修改
     *
     * @param presenterFactory PresenterFactory類型
     */
    @Override
    public void setPresenterFactory(PresenterMvpFactory<V, P> presenterFactory) {
        if (mPresenter != null) {
            throw new IllegalArgumentException("這個(gè)方法只能在getMvpPresenter()之前調(diào)用撑瞧,如果Presenter已經(jīng)創(chuàng)建則不能再修改");
        }
        this.mFactory = presenterFactory;
    }

    /**
     * 獲取Presenter的工廠類
     *
     * @return PresenterMvpFactory類型
     */
    @Override
    public PresenterMvpFactory<V, P> getPresenterFactory() {
        return mFactory;
    }

    /**
     * 獲取創(chuàng)建的Presenter
     *
     * @return 指定類型的Presenter
     * 如果之前創(chuàng)建過,而且是以外銷毀則從Bundle中恢復(fù)
     */
    @Override
    public P getMvpPresenter() {
        Log.e("perfect-mvp","Proxy getMvpPresenter");
        if (mFactory != null) {
            if (mPresenter == null) {
                mPresenter = mFactory.createMvpPresenter();
                mPresenter.onCreatePersenter(mBundle == null ? null : mBundle.getBundle(PRESENTER_KEY));
            }
        }
        Log.e("perfect-mvp","Proxy getMvpPresenter = " + mPresenter);
        return mPresenter;
    }

    /**
     * 綁定Presenter和view
     * @param mvpView
     */
    public void onResume(V mvpView) {
        getMvpPresenter();
        Log.e("perfect-mvp","Proxy onResume");
        if (mPresenter != null && !mIsAttchView) {
            mPresenter.onAttachMvpView(mvpView);
            mIsAttchView = true;
        }
    }

    /**
     * 銷毀Presenter持有的View
     */
    private void onDetachMvpView() {
        Log.e("perfect-mvp","Proxy onDetachMvpView = ");
        if (mPresenter != null && mIsAttchView) {
            mPresenter.onDetachMvpView();
            mIsAttchView = false;
        }
    }

    /**
     * 銷毀Presenter
     */
    public void onDestroy() {
        Log.e("perfect-mvp","Proxy onDestroy = ");
        if (mPresenter != null ) {
            onDetachMvpView();
            mPresenter.onDestroyPersenter();
            mPresenter = null;
        }
    }

    /**
     * 意外銷毀的時(shí)候調(diào)用
     * @return Bundle显蝌,存入回調(diào)給Presenter的Bundle和當(dāng)前Presenter的id
     */
    public Bundle onSaveInstanceState() {
        Log.e("perfect-mvp","Proxy onSaveInstanceState = ");
        Bundle bundle = new Bundle();
        getMvpPresenter();
        if(mPresenter != null){
            Bundle presenterBundle = new Bundle();
            //回調(diào)Presenter
            mPresenter.onSaveInstanceState(presenterBundle);
            bundle.putBundle(PRESENTER_KEY,presenterBundle);
        }
        return bundle;
    }

    /**
     * 意外關(guān)閉恢復(fù)Presenter
     * @param savedInstanceState 意外關(guān)閉時(shí)存儲的Bundler
     */
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.e("perfect-mvp","Proxy onRestoreInstanceState = ");
        Log.e("perfect-mvp","Proxy onRestoreInstanceState Presenter = " + mPresenter);
        mBundle = savedInstanceState;

    }
}

6.最后V層實(shí)現(xiàn)预伺,首先實(shí)現(xiàn)設(shè)置工廠的接口,然后創(chuàng)建一個(gè)代理并傳入默認(rèn)工廠曼尊,在V層生命周期中使用代理去實(shí)現(xiàn)管理Presenter的生命周期

/**
 * @author zhuxh
 * @date 2017/11/17
 * @description 繼承自Activity的基類MvpActivity
 * 使用代理模式來代理Presenter的創(chuàng)建酬诀、銷毀、綁定骆撇、解綁以及Presenter的狀態(tài)保存,其實(shí)就是管理Presenter的生命周期
 */
public abstract class AbstractMvpActivitiy<V extends BaseMvpView, P extends BaseMvpPresenter<V>> extends Activity implements PresenterProxyInterface<V,P> {

    private static final String PRESENTER_SAVE_KEY = "presenter_save_key";
    /**
     * 創(chuàng)建被代理對象,傳入默認(rèn)Presenter的工廠
     */
    private BaseMvpProxy<V,P> mProxy = new BaseMvpProxy<>(PresenterMvpFactoryImpl.<V,P>createFactory(getClass()));

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("perfect-mvp","V onCreate");
        Log.e("perfect-mvp","V onCreate mProxy = " + mProxy);
        Log.e("perfect-mvp","V onCreate this = " + this.hashCode());

        if(savedInstanceState != null){
            mProxy.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_SAVE_KEY));
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e("perfect-mvp","V onResume");
        mProxy.onResume((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("perfect-mvp","V onDestroy = " );
        mProxy.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e("perfect-mvp","V onSaveInstanceState");
        outState.putBundle(PRESENTER_SAVE_KEY,mProxy.onSaveInstanceState());
    }

    @Override
    public void setPresenterFactory(PresenterMvpFactory<V, P> presenterFactory) {
        Log.e("perfect-mvp","V setPresenterFactory");
        mProxy.setPresenterFactory(presenterFactory);
    }

    @Override
    public PresenterMvpFactory<V, P> getPresenterFactory() {
        Log.e("perfect-mvp","V getPresenterFactory");
        return mProxy.getPresenterFactory();
    }

    @Override
    public P getMvpPresenter() {
        Log.e("perfect-mvp","V getMvpPresenter");
        return mProxy.getMvpPresenter();
    }
}

最后看一下使用瞒御,首先在V層上定義需要創(chuàng)建的Presenter,聲明自己模塊具體的View接口類型和Presenter類型艾船,最后實(shí)現(xiàn)自己View接口就ok了葵腹,還有就是如果要設(shè)置自己的Presenter創(chuàng)建工廠,必須在調(diào)用onResume方法和getMvpPresenter方法之前設(shè)置

//聲明需要創(chuàng)建的Presenter
@CreatePresenter(RequestPresenter5.class)
public class MainActivity extends AbstractMvpAppCompatActivity<RequestView5, RequestPresenter5> implements RequestView5 {

    @FieldView(R.id.tv_text)
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewFind.bind(this);

        //設(shè)置自己的Presenter工廠屿岂,如果你想自定義的話
        // setPresenterFactory(xxx);

        if(savedInstanceState != null){
            Log.e("perfect-mvp","MainActivity  onCreate 測試  = " + savedInstanceState.getString("test") );
        }
    }

    //點(diǎn)擊事件
    public void request(View view) {
        Log.e("perfect-mvp","點(diǎn)擊事件");
        getMvpPresenter().clickRequest("101010100");
    }

    @Override
    public void requestLoading() {
        textView.setText("請求中,請稍后...");
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e("perfect-mvp","MainActivity onSaveInstanceState 測試");
        outState.putString("test","test_save");
    }

    @Override
    public void resultSuccess(WeatherBean result) {
        //成功
        textView.setText(result.getWeatherinfo().toString());
    }

    @Override
    public void resultFailure(String result) {
        //失敗
        textView.setText(result);
    }

}   

這時(shí)候如果界面意外銷毀,Presenter可以通過重寫以下方法進(jìn)行釋放資源践宴,存儲數(shù)據(jù),和恢復(fù)數(shù)據(jù)爷怀,例如:

@Override
public void onCreatePersenter(@Nullable Bundle savedState) {
    super.onCreatePersenter(savedState);
    if(savedState != null){
        Log.e("perfect-mvp","RequestPresenter5  onCreatePersenter 測試  = " + savedState.getString("test2") );
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.e("perfect-mvp","RequestPresenter5  onSaveInstanceState 測試 " );
    outState.putString("test2","test_save2");
}

@Override
public void onDestroyPersenter() {
    super.onDestroyPersenter();
}

哦了阻肩!大功告成,perfect运授!可能有的人會說這只是Activity烤惊,那么Fragment中怎么弄呢,其實(shí)是一模一樣的吁朦,我們將實(shí)現(xiàn)全部抽離到了
代理中柒室,那么Fragment中也只需要創(chuàng)建一個(gè)代理,然后在生命周期中使用代理調(diào)用相應(yīng)就好了逗宜,當(dāng)然最后我的庫中已經(jīng)實(shí)現(xiàn)了Fragment的基類和AppCompatActivity的基類雄右,至于View的如果有使用到的可以自行擴(kuò)展空骚,再次聲明本文只是提供一種思路和封裝的方法,并不代表就是最好的擂仍,如果有更好的想法和思路可以一起探討囤屹。

最后奉上github地址

GitHub地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逢渔,隨后出現(xiàn)的幾起案子肋坚,更是在濱河造成了極大的恐慌,老刑警劉巖肃廓,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件智厌,死亡現(xiàn)場離奇詭異,居然都是意外死亡亿昏,警方通過查閱死者的電腦和手機(jī)峦剔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來角钩,“玉大人吝沫,你說我怎么就攤上這事〉堇瘢” “怎么了惨险?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長脊髓。 經(jīng)常有香客問我辫愉,道長,這世上最難降的妖魔是什么将硝? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任恭朗,我火速辦了婚禮,結(jié)果婚禮上依疼,老公的妹妹穿的比我還像新娘痰腮。我一直安慰自己,他們只是感情好律罢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布膀值。 她就那樣靜靜地躺著,像睡著了一般误辑。 火紅的嫁衣襯著肌膚如雪沧踏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天巾钉,我揣著相機(jī)與錄音翘狱,去河邊找鬼。 笑死砰苍,一個(gè)胖子當(dāng)著我的面吹牛盒蟆,可吹牛的內(nèi)容都是我干的踏烙。 我是一名探鬼主播师骗,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼历等,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辟癌?” 一聲冷哼從身側(cè)響起寒屯,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黍少,沒想到半個(gè)月后寡夹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厂置,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年菩掏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昵济。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡智绸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出访忿,到底是詐尸還是另有隱情瞧栗,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布海铆,位于F島的核電站迹恐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏卧斟。R本人自食惡果不足惜殴边,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望珍语。 院中可真熱鬧锤岸,春花似錦、人聲如沸廊酣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亡驰。三九已至晓猛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凡辱,已是汗流浹背戒职。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留透乾,地道東北人洪燥。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓磕秤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捧韵。 傳聞我的和親對象是個(gè)殘疾皇子市咆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容