Android MVP BaseMVP 基础框架设计

作者: Android架构师丨小熊 | 来源:发表于2019-08-09 22:06 被阅读38次

    上篇文章(Andoid MVP架构 MVP软件架构介绍与实战运用),我们讲述了如何构建一个基于 MVP 的 HelloWord 级别的程序,让我们粗浅的了解了 MVC 与 MVP 之间的优缺点和联系,以及写了一个具有代表性的 MVP 的小 demo,通过这个 demo 我们可以发现很多问题,比如,还是有代码的重复。如果你没看过上一篇文章,强烈建议你先过一遍,否则这篇文章你可能会觉得我不知所云,且看以下的代码示例:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    
        mPresenter = new MainPresenter(this);
        mPresenter.handlerData();
    }
    

    先来看看这一版本的分包结构,相比之前 v1 的版本就多了一个 basemvp 包,然这个包下的代码却很关键。

    我们的 View 层要持有 Presenter 层的引用,就必须要先实例化 P 层的对象,然后才能调用 P 层的相关方法,否则就会造成空指针异常。这里的 new MainPresenter() 是一个具体实现类,因为不管哪个 View 层,都是强制性要求实例化 P 层,为了防止我们忘了实例化,所以在此把实例化方法抽象到父类中,实现它的子类必须实现抽象方法,也就避免了我们忘记。

    还有不同的 Presenter 层,自然而然它的具体实现类就不同了,所以应该考虑用到泛型,这里要明白谁在引用 Presenter,就是谁要传入泛型。显然,我们的 IBaseView 的实现类 BaseActivity 就得传入一个 Presenter 的泛型参数,那先来看一看 IBaseView 接口如何写:

    View 层,新建 IBaseView 接口:

    package com.test.mvp.mvpdemo.mvp.v2.basemvp;
     
    import android.content.Context;
     
    public interface IBaseView {
     
       Context getContext();
    }
    

    很简单,获取一个上下文对象,我们在 Activity 中或多或少的用到 Context,所以就干脆写个在这里写统一了,为了避免我们在匿名内部类中要传入 MainActivity.this 这样的麻烦事。既然有了 IBaseView 的接口,我们就该考虑写它的具体实现类,因为这里的 View 层就是 Activity,所以我们就得写一个 BaseActivity 基类,既然是基类,就要声明为抽象类,把共有的方法提取到基类中,这里使用到的就是模板模式。

    View 层,新建 BaseActivity 基类:

    package com.test.mvp.mvpdemo.mvp.v2.basemvp;
     
    import android.content.Context;
    import android.os.Bundle;
    import android.support.annotation.IdRes;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
     
    public abstract class BaseActivity<P extends IBasePresenter> extends AppCompatActivity implements IBaseView {
     
        protected P mPresenter;
     
        protected abstract void initLayout(@Nullable Bundle savedInstanceState);
     
        protected abstract void initViews();
     
        protected abstract void initData();
     
        protected abstract P setPresenter();
     
        protected <T extends View> T $(@IdRes int viewId) {
            return findViewById(viewId);
        }
     
        @SuppressWarnings("unchecked")
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
     
            initLayout(savedInstanceState);
     
            /**
             * 实例化和绑定 P 层
             */
            mPresenter = setPresenter();
            mPresenter.attech(this);
     
            initViews();
            initData();
        }
     
        @Override
        protected void onDestroy() {
            super.onDestroy();
            /**
             * 解绑,避免内存泄漏
             */
            mPresenter.detech();
            mPresenter = null;
        }
     
        @Override
        public Context getContext() {
            return this;
        }
    }
    

    从上面的代码中,我把 new Presenter() 实现类写成了一个泛型,泛型应该是 Java 的基础和基本操作,我就不多说了。这里的抽象方法 setPresenter() 就相当于 new Presenter() 一样,子类继承 BaseActivity 时,只需要返回一个具体的 Presenter() 实例就行了,这样 View 层就持有了 Presenter 的引用。

    然后看 presenter.attech(this) 这个方法,目的是传入一个 IBaseView 的接口类型,将 View 层的引用给了 Presenter 层,这样就形成了关联,就可以互相调用对象的方法了。

    这里注意,一定要在 onDestory() 时释放 P 层引用,否则可能会造成内存泄漏,这个我们之后再说。然后我们来看看 IBasePresenter 接口的方法:

    Presenter 层,新建 IBasePresenter 接口:

     
    public interface IBasePresenter<V extends IBaseView> {
     
        void attech(V view);
     
        void detech();
    }
    

    这里也很简单,就是做了绑定 View 和解绑 View 的操作。那我们的 BasePresenter 实现类就需要持有 BaseView 的引用,所以必须要传入一个泛型的 View 层接口,具体实现代码如下。

    Presenter 层,新建 BasePresenter 基类:

    package com.test.mvp.mvpdemo.mvp.v2.basemvp;
     
    public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter {
        protected V mView;
     
        @SuppressWarnings("unchecked")
        @Override
        public void attech(IBaseView view) {
            mView = (V) view;
        }
     
        @Override
        public void detech() {
            mView = null;
        }
    }
    

    结束了这两个基类的封装,我们的 BaseMVP 差不多就可以形成了。剩下的就是要修改 MainActivity 和 MainPresenter 的操作了,这里的 Model 层是无需修改的,具体业务逻辑也是不用修改的。所以,我们在之前 v1 版本的 MVP 架构的基础之上做一些简单的修改。(请看我的上一篇文章:Andoid MVP架构MVP软件架构介绍与实战运用)

    首先,我们需要修改的一个地方,就是契约类,这个类中的业务逻辑处理都不需要修改,还记得我们之前写过的那两个 IBaseView 与 IBasePresenter 吗?这时候就派上用场了。来看代码:

    修改 MainContract 契约类:

    package com.test.mvp.mvpdemo.mvp.v2;
     
    import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBasePresenter;
    import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;
     
    import okhttp3.Callback;
     
    /**
     * 契约接口,可以很直观的看到 M、V、P 层接口中提供的方法
     */
    public interface MainContract {
        interface IMainModel {
            void requestBaidu(Callback callback);
        }
     
        interface IMainView extends IBaseView{
            void showDialog();
     
            void succes(String content);
        }
     
        interface IMainPresenter extends IBasePresenter {
            void handlerData();
        }
    }
    

    为了支持泛型,这里的 IMainView 和 IMainPresenter 就需要继承它们各自的基类接口了。

    其次,MainActivity 不再继承 AppCompatActivity 了,要继承我们刚刚写好的基类:BaseActivity,其中修改的代码如下:
    

    View 层,修改 MainActivity 实现类:

    package com.test.mvp.mvpdemo.mvp.v2.view;
     
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Handler;
    import android.support.annotation.Nullable;
    import android.widget.TextView;
    import android.widget.Toast;
     
    import com.test.mvp.mvpdemo.R;
    import com.test.mvp.mvpdemo.mvp.v2.MainContract;
    import com.test.mvp.mvpdemo.mvp.v2.basemvp.BaseActivity;
    import com.test.mvp.mvpdemo.mvp.v2.presenter.MainPresenter;
     
    /**
     * MVP 的写法,Version 2: 使用模板方法模式 + 泛型 封装 mvp 基类
     *
     * @author 神探丶威威猫
     * @blog https://blog.csdn.net/smile_running
     * @warning 点个赞哦,评个论哦
     */
    public class MainActivity extends BaseActivity<MainContract.IMainPresenter> implements MainContract.IMainView {
     
        private TextView tv;
     
        @Override
        protected void initLayout(@Nullable Bundle savedInstanceState) {
            setContentView(R.layout.activity_main);
        }
     
        @Override
        protected void initViews() {
            tv = $(R.id.tv);
        }
     
        @Override
        protected void initData() {
            mPresenter.handlerData();
        }
     
        @Override
        protected MainContract.IMainPresenter setPresenter() {
            return new MainPresenter();
        }
     
        @Override
        public void showDialog() {
            ProgressDialog dialog = new ProgressDialog(getContext());
            dialog.show();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    dialog.dismiss();
                }
            }, 1500);
        }
     
        @Override
        public void succes(String content) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show();
                    tv.setText(content);
                }
            });
        }
    }
    

    就这么简单,只要稍微的修改就可以了。然后我们看看 MainPresenter 实现类的代码,为了持有 View 层的引用,这里就需要传入泛型的接口了,代码修改成如下:

    Presenter 层,修改 MainPresenter 实现类:

    package com.test.mvp.mvpdemo.mvp.v2.presenter;
     
    import android.util.Log;
     
    import com.test.mvp.mvpdemo.mvp.v2.MainContract;
    import com.test.mvp.mvpdemo.mvp.v2.basemvp.BasePresenter;
    import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;
    import com.test.mvp.mvpdemo.mvp.v2.model.DataModel;
     
    import java.io.IOException;
     
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.Response;
     
    /**
     * presenter 层,承担业务逻辑处理,数据源处理等
     */
    public class MainPresenter extends BasePresenter<MainContract.IMainView> implements MainContract.IMainPresenter {
     
        private MainContract.IMainModel mModel;
     
        @Override
        public void attech(IBaseView view) {
            super.attech(view);
            mModel = new DataModel();
        }
     
        @Override
        public void handlerData() {
            if (mView != null) {
                mView.showDialog();
            }
            /**
             * 发起请求,获得回调数据
             */
            mModel.requestBaidu(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                }
     
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    String content = response.body().string();
                    if (mView != null) {
                        mView.succes(content);
                    }
                }
            });
        }
     
        @Override
        public void detech() {
            super.detech();
            /**
             * 释放内存、关闭网络请求、关闭线程等操作
             */
            Log.d("==========", "detech: 解除绑定,释放内存");
        }
    }
    

    终于修改完了一个基础框架,是不是觉得非常有成就感。不过呢,这个框架还会有一些隐患,我会在下一篇的文章中去解决这个隐患:那就是内存泄漏与动态代理的问题。

    那么,这篇文章到此为止,我们就把新的一个版本的 MVP 架构给修改完了,其实运用的都是一些基础知识,比如模板模式、抽象类和抽象方法、泛型、继承、接口等的使用,每一个都是 Java 的基础知识,是不是很简单啊。如果你可以基于上一篇的 MVP 版本,自己手写一个泛型框架,那说明你掌握的基础知识很不错。不然,还是多推敲推敲别人的写法,多想为什么要这样写?这样才能有效的提高自己的水平。

    最后

    如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。谢谢!

    相关文章

      网友评论

        本文标题:Android MVP BaseMVP 基础框架设计

        本文链接:https://www.haomeiwen.com/subject/qfvkjctx.html