Android MVP框架学习实践

五五六六七七八八        

        作为一名大三在读的学生,想着自己的大学生活只剩下一年了,有了些许紧迫感,于是就打算找一份实习工作来增加自己的项目实战经验。   

        想着互联网公司都基本都有现成的项目,实习的话最多也就是对原有的代码修改,增加功能?所以最后进了一家不大不小的外包公司,想体验一下一个商业项目从落地到成型的过程。进入公司以后,被分配到一个给国外发包方做的项目(我只是个实习生萌新啊,第一个项目就给外国人做,这样好吗喂)。

        作为一个有过Android开发经验的菜鸟,也算在各个服务外包比赛中拿过些奖的人(虽然大学的这种比赛很水,但是也是有些开发的工作量的,哈哈),我对一个项目的构成,如何开发等等,都有些了解。由于之前的比赛项目经验,都是我一个人独立开发Android端,所以对项目的架构、框架等都是怎么方便怎么来,不怎么考虑代码的可读性,可复用性之类的东西(进入公司实习之后才发现,这些东西相当重要啊),之前为了方便,把业务逻辑,视图绑定啥的都一股脑的放入activity类中,导致activity类过于臃肿,给代码的阅读和修改都带了极大的不便,但是由于自己的项目经验缺乏,从一开始就给自己挖下的坑,之后不但很难填,而且还越挖越深(单手捂眼笑哭.jpg),幸好只是我一个人自己开发,不然要是协作开发的话,估计会被小伙伴怒锤。

        进入公司以后,从Android带队开发大佬那边拿到了项目的基础开发框架


品了一下,从我裸考过六级的水平来看,嗯,这个presenter应该是之前在书上看到过的MVP中的P没差了。从网上找找资料学习学习。

        ok,进入正题。

        MVP是Model、View、Presenter的缩写。Model表示模型,主要负责数据的加载;VIew表示视图主要负责视图绑定,界面的展示,界面逻辑跳转等;Presenter是表示器,主要作为View和Model的中间人,完成他们之间的交互,从而实现高内聚低耦合的思想。画了一幅它们之间的关系图。


        可能这样有点抽象,talk is cheap, show you the code.

        先来看看我们项目组Android大佬的MVP框架实现,

MainActivity.java:

public class MainActivity extends BaseActivity<MainPresenter> implements MainContract.View {

    @BindView(R.id.tv_doubanbook)
    TextView mTvDoubanbook;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initTitleBar() {

    }

    @Override
    protected void initData() {
        //初始化Presenter
        mPresenter = new MainPresenter(this);
        //调用Presenter的getbook方法,去model调取数据
        mPresenter.getBooks();
    }

    @Override
    public void onBooksGeted(DouBanBook douBanBook) {
        //回调函数, douBanBook 即为Presenter获得的数据  可用于在界面上展示
        mTvDoubanbook.setText(douBanBook.toString());
    }

    @Override
    protected void initListener() {

    }

    @Override
    public void onClick(View view) {

    }
    
}
 
 

MainContract.java:

public interface MainContract {
    interface View extends IBaseView {
        //当数据presenter完成数据加载,调用此方法将数据展示在view上
        void onBooksGeted(DouBanBook douBanBook);
    }

    interface Presenter extends BasePresenter {
        //从服务器端获取数据
        void getBooks();
    }

}

MainPresenter.java:

public class MainPresenter extends BasePresenterImpl<MainContract.View> implements MainContract.Presenter {
    public MainPresenter(MainContract.View mView) {
        super(mView);
    }

    @Override
    public void getBooks() {
        mRxManager.add(RetrofitManager.getInstance()
                .getBooks()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new DisposableObserver<DouBanBook>() {
                    @Override
                    public void onNext(DouBanBook douBanBook) {
                        //数据获取成功,调用此方法,将数据展示到view上
                        mView.onBooksGeted(douBanBook);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                }));
    }
}

        大佬果然是大佬,已经对各种基类进行了封装,来方便我们这些萌新的开发。(上面的代码还用到了RxJava,emmmm其实这个对于我这个萌新来说也是还没有接触过的技术,之后也会更新RxJava相关的学习博客,要努力变强~)。

        我们会发现,其实上面这三个.java已经可以实现一个界面的完整逻辑,但是View有了,Presenter也有了,Model去哪了???还有Contract又是什么鬼???这还是MVP的设计模式吗???

        一开始,我也是懵的,又去大佬那边学习了一波。了解到,目前Android的MVP设计模式其实还没有一个统一的标准,基于MVP这个思想可以有很多的实现方式,上面展示的这个方式是Google官方提出的一种MVP实现方式。

         谷歌官方的MVP实现方式中,弱化了Model的作用,将Model融入到了Presenter中,我们可以看到MainPresenter中的getBook方法,其实就是Model的实现。而Contract类,中文翻译就是契约类,契约这个词很形象,它作为view和presenter之间的接口,定义了view和presenter上所要实现的方法,然后view和presenter再各自实现其方法,让整个界面的逻辑变得相当清晰。

        由于大佬的代码经过了封装,有些细节被隐藏了,所以我们来做个小demo,用谷歌官方的MVP实现方式来实现一个 --模拟从服务器拉取信息展示到界面--  的程序,更直观的了解MVP。

        

我的表演

        先看一下最后呈献的界面吧


界面展示

        界面布局很简单,就一个TextView和一个Button,布局文件xml就不贴出来了。主要的功能是点一下获取数据的按钮,模拟从服务器获取数据,然后在textview上显示出来。

        下面的代码结构,由Activity(也就是View)、Presenter、Contract组成:


代码结构

        为了讲解方便,我们先来看一下MainContract里的内容:

public interface MainContract {

    interface Presenter{
        void loadData();
        void functionA();
    }

    interface View {
        void onDataLoaded(String s);
        void onFunctionAFinished();
    }

}
MainContract代码

        相当简单,因为Contract是一个契约类,里面并没有实现任何功能,只是写明了Presenter和View分别要实现的接口,我在Presenter接口中定义了LoadData的方法,MainPresenter需要实现Presenter接口中的LoadData方法,来完成对数据从服务器的拉取;在接口View中定义了onDataLoaded的方法,同样,因为MainActivity应用了接口View,因此需要对onDataLoaded方法进行实现。

MainActivity:

public class MainActivity extends AppCompatActivity implements MainContract.View{

    private TextView mTextView;
    private Button mButton;
    private MainPrestener mPrestener;

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

        mTextView = findViewById(R.id.tv_display);
        mButton = findViewById(R.id.btn_loaddata);
        mPrestener = new MainPrestener(this);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPrestener.loadData();
                mPrestener.functionA();
            }
        });

    }

    @Override
    public void onDataLoaded(String s) {
        mTextView.setText(s);
    }

    @Override
    public void onFunctionAFinished() {
        //我被调用了   要更新界面了
        //界面更新的操作  巴拉巴拉
        mTextView.setText("更新了");
    }

}


MainActivity代码

        MainActivity主要是界面组件的绑定,以及一些成员变量的初始化,可以看到我这里初始化了textview、button还有一个presenter,之后界面关于数据的操作就通过presenter来实现。

        我们可以看到,这里重写了onDataLoaded的方法,因为MainActivity implements了契约类中的View接口,因此需要将接口中的onDataLoaded方法实现,该方法的主要功能就是将presenter中获取到的数据呈现在界面上,我们这边是将数据显示在界面的textview上。

MainPresenter:

public class MainPrestener implements MainContract.Presenter {

    MainContract.View mView;

    public MainPrestener(MainContract.View mView) {
        this.mView = mView;
    }

    @Override
    public void loadData() {

        String s = "假装我是服务器端获取的数据";
        mView.onDataLoaded(s);

    }

    @Override
    public void functionA() {
        //巴拉巴拉 functionA 干了啥 干了一堆东西
        //。。。。。

        //干完了之后 调用view的方法 更新界面
        mView.onFunctionAFinished();
    }
}
MainPresenter代码

        MainPresenter是界面数据操作实现的地方,他implements了契约类的Presenter接口,因此需要实现Presenter接口中定义的LoadData方法,该方法的功能是从服务器获取数据然后回调给Activity(也就是View),我这边偷懒了,没有服务器获取数据,直接hard code出数据(嘻嘻),嗯,假装获取到了服务器端的数据,然后调用View的onDataLoaded方法,将所获取到的数据传递到activity中。这样activity中就可以更新数据了。

        奥,对了,Presenter中必须有一个View的成员变量MainContract.View  mView;  不然就不能调用view上面的方法了,mView的初始化,在Presenter变量创建的时候复制,可以在MainActivity中看到



        看一下运行的结果


按钮点击后结果

        由此,我们已经实现了一个相当相当简单的基于MVP框架的app。

        如果之后需要加功能,只要在Contract中添加所需要功能的接口,然后再在Presenter和Activity中分别实现就行。

        比如说我要新增一functionA,执行完后需要在View上做出相应的界面改变。我们只需要这样做:

1、在Contract中定义Presenter中functionA方法,以及View中的onFunctionAFinished方法



2、在Presenter实现类中实现functionA具体方法,并调用更新界面的onFunctionAFinished



3、在Activity中实现onFunctionAFinished方法



        这样,又一个功能被添加了,是不是很方便,逻辑也很清晰。Activity只需负责界面更新的部分,而数据的获取,全都交由Presenter来实现,高内聚低耦合,对于界面的维护和开发相当有帮助。

        当然,以上只是MVP一个最简单不过的实现了,为了易于理解,我对很多基类的定义和封装都去掉了,在实际的开发中是不可能那么简单的,但是,如果对于MVP的思想理解了,实现起来还是思路很清晰的。

        以上。

        emmm......再贴上上面代码的github地址吧,虽然没几行代码,但是不贴显得不专业(嘿嘿

        公司的开发框架代码我是不会贴的,贴的是我的简单代码(逃

        GitHub - reggie1996/mvp_demo: 简单MVP框架学习




猜你喜欢

转载自blog.csdn.net/weixin_42247440/article/details/80380790