如何實現本身的Android MVP框架?

 

       相信熟悉android開發的童鞋對MVP框架應該都不陌生吧,網上不少關於android中實現MVP的文章,你們能夠直接搜索學習。這些文章中,MVP的實現思路基本都是把Activity、Fragment做爲Presenter,這種方式不一樣於如今主流的MVP方式,不過卻好的解決了Activity生命週期帶來的問題,且讓MVP的實現更加輕鬆了。html

      小編以爲不少關於android MVP框架的實現的文章都寫的很是不錯,且今天在網上看到一位童鞋利用上面所說的思路實現本身的MVP框架 — MVPro,更是讓人眼前一亮。下面小編和你們分享分享這位牛叉叉的童鞋是怎麼實現他的MVPro的,一塊兒來圍觀吧。java

 

MVPro的原理android

       和以往你們實現MVP的思想相似,將Activity和Fragment做爲Presenter,爲了形象直觀,咱們經過下面這張圖,來展現MVPro的原理:架構

 

MVPro的實現很簡單,思想和上面兩篇文章介紹的同樣,都是將Activity和Fragment做爲Presenter,首先一張圖來解釋一下MVPro的原理,框架

 

Presenter即咱們的Activity或者Fragment, View呢?說白了就是咱們從Activity和Fragment中提取出來的和View操做相關的代碼,思路很簡單也很清晰,下面咱們就以一個簡單的demo詳細學習一下MVPro的使用吧。ide

 

MVPro的使用函數

由於MVPro是將Activity視爲Presenter,因此咱們代碼的主線應該是Presenter了,首先上一個Presenter的代碼,佈局

 

public class MainActivity extends ActivityPresenterImpl<DataListView>學習

        implements AdapterView.OnItemClickListener, View.OnClickListener {this

 

    @Override

    public void create(Bundle savedInstance) {

 

    }

 

    @Override

    public void created(Bundle savedInstance) {

 

    }

 

    @Override

    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        mView.toast(position);

    }

 

    @Override

    public void onClick(View v) {

        if(v.getId() == R.id.newdata) newData();

        else getData();

    }

 

    private void newData() {

        new MainBiz().data(new MainBiz.Callback<ArrayList<String>>() {

            @Override

            public void callback(ArrayList<String> data) {

                mView.setData(data);

            }

        });

    }

 

    private void getData() {

        new MainBiz().data(new MainBiz.Callback<ArrayList<String>>() {

            @Override

            public void callback(ArrayList<String> data) {

                mView.addData(data);

            }

        });

    }

}

 

MainActivity繼承自ActivityPresenterImpl類,並且在代碼中並無任何和Activity生命週期相關的代碼,兩個方法create和created是咱們惟一關心的重點,可是也是非必須重寫的,這兩個方法都是Presenter提供的方法,他們兩個的區別就是create在setContentView以前調用,而created是在setContentView以後調用。

剩下的幾個方法咱們能夠猜想到都是從View層發起的,那麼接下來咱們就來看看View層該如何去寫。從MainActivity實現的泛型中咱們能夠看出,這裏咱們對應的View層代碼在DataListView中。

 

/**

 * Created by www.maiziedu.com on 2015/11/15.

 */

public class DataListView extends ViewImpl {

 

    private Button mButton1, mButton2;

    private ListView mListView;

    private ArrayAdapter<String> mAdapter;

 

    @Override

    public void created() {

        mButton1 = findViewById(R.id.newdata);

        mButton2 = findViewById(R.id.adddata);

        mListView = findViewById(R.id.list);

    }

 

    @Override

    public void bindEvent() {

        EventHelper.click(mPresenter, mButton1, mButton2);

        EventHelper.itemClick(mPresenter, mListView);

    }

 

    @Override

    public int getLayoutId() {

        return R.layout.list_layout;

    }

 

    public void setData(ArrayList<String> datas) {

        mAdapter = new ArrayAdapter<String>(mRootView.getContext(),

                android.R.layout.simple_list_item_1, datas);

        mListView.setAdapter(mAdapter);

    }

 

    public void addData(ArrayList<String> datas) {

        mAdapter.addAll(datas);

    }

 

    public void toast(int position) {

        Toast.makeText(mRootView.getContext(),

                mAdapter.getItem(position), Toast.LENGTH_SHORT).show();

    }

}

在View層中,咱們並不關心佈局文件是怎麼設置到Activity上的,咱們僅僅在getLayoutId中返回咱們要使用的佈局文件,和created中去獲取咱們須要的控件便可, 不過咱們還重寫一個bindEvent方法,在這個方法中咱們爲控件綁定了事件,這裏須要注意一點就是MVPro但願各類事件都在Presenter上implements,由於EventHelper 提供了基於Presenter的事件監聽。

 

ok, MVPro的使用就這麼簡單,不過相信不少人仍是摸不着頭腦,因此下面咱們將會深刻到源碼中,來了解一下MVPro的實現流程。

 

MVPro源碼

首先咱們仍是要從Presenter入手,Presenter的源頭是一個接口,這裏面定義了Presenter的必須須要的幾個方法,

 

/**

 * Presenter的根接口<br />

 * Created by www.maiziedu.com on 2015/11/15.

 */

public interface IPresenter<T> {

    /**

     * 獲取當前presenter泛型的類型

     * @return

     */

    Class<T> getViewClass();

 

    /**

     * View初始化以前能夠在此方法作一些操做

     */

    void create(Bundle savedInstance);

 

    /**

     * View初始化完畢後調用

     */

    void created(Bundle savedInstance);

}

只有三個方法,其中getViewClass是獲取咱們對應的View層那個類的class, 例如上面的demo中對應的就是DataListView,create和created是兩個擴展方法分別在onCreate的setContentView以前和以後調用。接下來,咱們就來看看咱們上面MainActivity繼承的那個ActivityPresenterImpl的代碼,

 

/**

 * 將Activity做爲Presenter的基類 <br />

 * Created by www.maiziedu.com on 2015/11/15.

 */

public class ActivityPresenterImpl<T extends IView> extends Activity implements IPresenter<T> {

 

    protected T mView;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        create(savedInstanceState);

 

        try {

            mView = getViewClass().newInstance();

            mView.bindPresenter(this);

            setContentView(mView.create(getLayoutInflater(), null));

            mView.bindEvent();

            created(savedInstanceState);

        } catch(Exception e) {

            throw new RuntimeException(e.getMessage());

        }

    }

 

    @Override

    public Class<T> getViewClass() {

        return GenericHelper.getViewClass(getClass());

    }

 

    @Override

    public void create(Bundle savedInstance) {

 

    }

 

    @Override

    public void created(Bundle savedInstance) {

 

    }

}

 

估計不少人在沒看到ActivityPresenterImpl源碼以前都會認爲它應該很複雜,不過在看後大概都會忍不住吐槽一句:尼瑪,這麼少! 對代碼確實少,並且基本都集中在了onCreate中,不過,在看onCreate以前,咱們首先來看看getViewClass方法, 這個方法實現自IPresenter,並且僅僅有一行代碼,它的做用就是獲取當前Presenter泛型指定那個View層類,對應上面的demo中就是DataListView了。

 

接下來回到onCreate中,在onCreate中,咱們首先調用了create方法,以便於咱們執行一些在setContentView以前的代碼,例如設置無標題啥的。

而後經過getViewClass獲取到了View層的類,而且利用反射獲得他的對象mView,接下的幾步都和這個mView相關。

調用mView.bindPresenter方法,將當前Presenter關聯到當前View層上。

調用mView.create方法生成咱們佈局文件對應的View對象,而且經過setContentView設置佈局文件。

調用mView.bindEvent方法,在這個方法中咱們能夠對一些控件設置事件監聽。

最後咱們調用了created方法,以便在開發中書寫初始化控件完畢後的代碼。

 

Presenter的代碼很簡單,主要是一些流程性的東西,接下來咱們來看看View層的代碼是怎麼實現的。仍是從接口——IView開始,

 

/**

 * View層的根接口 <br />

 * Created by www.maiziedu.com on 2015/11/15.

 */

public interface IView {

    /**

     * 根據 {@link getLayoutId}方法生成生成setContentView須要的根佈局

     * @param inflater

     * @param container

     * @return

     */

    View create(LayoutInflater inflater, ViewGroup container);

 

    /**

     * 當Activity的onCreate完畢後調用

     */

    void created();

 

    /**

     * 返回當前視圖須要的layout的id

     * @return

     */

    int getLayoutId();

 

    /**

     * 根據id獲取view

     * @param id

     * @param <V>

     * @return

     */

    <V extends View> V findViewById(int id);

 

    /**

     * 綁定Presenter

     * @param presenter

     */

    void bindPresenter(IPresenter presenter);

 

    /**

     * {@link created}後調用,能夠調用{@link org.loader.helper.EventHelper.click}

     * 等方法爲控件設置點擊事件,通常推薦使用{@link org.loader.helper.EventHelper.click(IPresenter presenter, View ...views)}

     * 方法而且讓你的Presenter實現相應接口。

     */

    void bindEvent();

}

 

IView接口中定義的方法就相對多了一些,咱們一個個的來講一下這些方法存在的理由。

create方法根據提供的layout的id來生成佈局對象,上面Presenter的setContentView的參數就是他的返回值。

created會在inflater佈局完成後調用,以便咱們一些操做。

getLayoutId須要咱們去實現,返回須要的佈局文件的id。

findViewById提供了一個泛型的查找控件方法,有了它咱們不再用類型轉換了。

bindPresenter綁定對應的Presenter。

bindEvent咱們須要實現這個方法來爲控件設置監聽。

 

 在介紹完這些方法後,咱們就來看看咱們View層的基類都是作了什麼工做。

 

/**

 * View層的基類

 * Created by www.maiziedu.com on 2015/11/15.

 */

public abstract class ViewImpl implements IView {

 

    /**

     * create方法生成的view

     */

    protected View mRootView;

    /**

     * 綁定的presenter

     */

    protected IPresenter mPresenter;

 

    @Override

    public View create(LayoutInflater inflater, ViewGroup container) {

        mRootView = inflater.inflate(getLayoutId(), container, false);

        created();

        return mRootView;

    }

 

    @Override

    public <V extends View> V findViewById(int id) {

        return (V) mRootView.findViewById(id);

    }

 

    @Override

    public void created() {

 

    }

 

    @Override

    public void bindPresenter(IPresenter presenter) {

        mPresenter = presenter;

    }

 

    @Override

    public void bindEvent() {

 

    }

}

 

在create方法中,咱們使用LayoutInflater生成了View對象,而且返回給Presenter用來setContentView,在返回以前咱們還調用了created方法,在項目中咱們能夠在這個方法中查找咱們須要的控件。

created和bindEvent方法在這裏都是空實現,這樣作的目的就是在咱們本身的代碼中不須要任什麼時候候都要實現這兩個方法。

 

MVPro的代碼大致就這些,整個流程咱們也分析完了,那Model層呢?尚未看到關於Model層的代碼呢!在MVPro中並不關心你業務層是怎麼實現,你徹底可使用你如今正在使用的業務層代碼的架構,而不會對MVP產生任何影響。

 

最後,咱們仍是要來看看EventHelper是怎麼設置事件監聽的。

 

public class EventHelper {

 

    public static void click(IPresenter li, View ...views) {

        if(!(li instanceof View.OnClickListener)) return;

        click((View.OnClickListener) li, views);

    }

 

    public static void click(View.OnClickListener li, View ...views) {

        if(views == null || views.length == 0) return;

        for(View v : views) v.setOnClickListener(li);

    }

    ...

}

 

這裏拿click事件爲例,能夠看到咱們的參數是一個IPresenter類型, 並且是判斷了你傳遞的這個Presenter是否是實現了View.OnClickListener接口,若是實現了,就進行強制類型轉換使用,從這裏咱們也能夠看到,爲了儘可能減小咱們的Presenter和View層的代碼耦合,MVPro並無使用泛型,也不建議從Presenter傳遞OnClickListener對象,而是建議咱們的Presenter直接實現OnClickListener接口,這樣作,EventHelper會自動幫咱們完成類型的檢查和監聽的設置。

 

到這裏,咱們還有一個helper沒有看實現,就是那個獲取泛型類型的幫助類。

 

/**

 * Created by www.maiziedu.com on 2015/11/15.

 */

public class GenericHelper {

 

    public static <T> Class<T> getViewClass(Class<?> klass) {

        Type type = klass.getGenericSuperclass();

        if(type == null || !(type instanceof ParameterizedType)) return null;

        ParameterizedType parameterizedType = (ParameterizedType) type;

        Type[] types = parameterizedType.getActualTypeArguments();

        if(types == null || types.length == 0) return null;

        return (Class<T>) types[0];

    }

}

 

這個類中僅僅一個方法,不過可能大部分人對這裏面的代碼很是陌生,這裏作的事很直接,就是根據咱們提供的class,來獲取這個類實現的那個泛型的類型,打個比方,下面的代碼獲取到的就是java.lang.String,

 

class Child extends Super<String> {}

 

以上就是利用Android MVP框架的實現思想,實現本身MVP框架的具體方法和源碼,總的來講,掌握了思路和方法後,實現起來仍是不難的。若是你們在本身的MVP框架實現過程當中,有更好的思路或方法,也歡迎補充分享。

 

相關文章:《Android React Native組件的生命週期及回調函數

相關文章
相關標籤/搜索