如何实现本身的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组件的生命周期及回调函数

相关文章
相关标签/搜索