美文网首页古德斯达迪MVP
Android MVP模式简单使用和封装使用

Android MVP模式简单使用和封装使用

作者: 艾曼大山 | 来源:发表于2020-04-20 10:33 被阅读0次

    MVP+Retrofit2+RxJava2

    本Demo使用MVP+Retrofit2+RxJava2来写的,如果大家对Retrofit2+RxJava都不会使用进行网络请求的话,请先查看我之前写的文章Retrofit2.0+RxJava2.0封装使用,因为我这里面网络请求是直接使用之前封装好的,这里就不再介绍了。这里只介绍Android MVP的简单使用和封装使用。

    Demo地址:https://github.com/pengjunshan/MVPRetrofitRxJava

    其它文章

    Retrofit2+RxJava2 封装使用
    OkHttp3简单使用和封装使用
    Android开发 多语言、指纹登录、手势登录
    Android使用IconFont阿里矢量图标
    Android Studio 使用SVN 主干和分支合并代码

    主要讲解内容
    • MVP封装前简单使用
    • MVP对Activity封装使用
    • MVP对Frgment封装使用
    效果图
    MVP介绍

    MVP全名Mode View Presenter,Presenter处理逻辑业务,Model提供数据,View更新展示界面。完全隔离界面显示与业务逻辑。

    流程图

    优点:

    • 分离了视图逻辑和业务逻辑,降低了耦合。
    • 单一职责, Model, View, Presenter只处理单一逻辑。
    • Model层的修改和View层的修改互不影响。
    • 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性

    缺点:

    • 接口类过多,代码量增加。
    • Presenter和View相互持有引用,解除不及时的话容易出现内存泄漏。

    说明:

    • 这里对MVP不做过多的解释,网上对MVP的解释有一大堆基本说的都差不多。
    • 下面我会讲解怎么尽可能的减少代码量,和避免内存泄漏的方法。
    • MVP并没有一个统一标准模式,只要遵循View和Model相互分离就行。(按照自己喜欢的模式写就行)

    MVP简单使用方法

    用到的类

    Concacts:契约类 将Model、View、Presenter 进行约束管理,方便后期类的查找、维护。
    View:Activity 和Fragment 视为View层,负责处理 UI。
    Model:包含着具体的数据请求,数据源、本地存储等等。
    Presenter :为业务处理层,既能调用View逻辑,又能调用Model请求数据。

    需要的类

    EaseConcacts 类

    契约类 将Model、View、Presenter 进行约束管理。在各自的接口类中定义不同需求的方法。IView接口类中创建一个接收Data数据的回调方法;IPresenter接口类中定义一个IView触发网络请求的方法;IModel接口类中定义一个真正网络请求的方法和一个回调给IPresenter类的回调方法;

    public class EaseConcacts {
    
      interface IView {
    
        /**
         * View层获取数据回调方法
         */
        void onResultData(String data);
      }
    
      interface IPresenter {
    
        /**
         * View层向Presenter发送请求方法
         */
        void requestData(Context context);
      }
    
      interface IModel {
    
        /**
         * Persenter层向Model发送请求方法
         */
        void getData(Context context, ModelListener modelListener);
    
        interface ModelListener {
    
          /**
           * Model层请求接口完成后回调Persenter层方法
           */
          void onReslutJson(String test);
        }
      }
    }
    

    EaseActivity类

    IView类中实现Concacts中的IView接口,并且初始化Presenter,然后点击按钮触发Presenter类中的requestData方法;requestData方法中会触发IModel中真正网络请求方法;然后IView类通过onResultData回调方法接收数据;

    public class EaseActivity extends AppCompatActivity implements IView {
    
      private EasePresenter presenter;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ease);
        presenter = new EasePresenter(this);
        findViewById(R.id.getJSON).setOnClickListener((view) -> presenter.requestData(this));
      }
    
      @Override
      public void onResultData(String data) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
      }
    
    }
    

    EasePresenter类

    首先要实现EaseConcacts契约类中的IPresenter接口,并实现requestData方法,当View调用requestData方法后Presenter会通过Mode进行网络请求,然后把结果回调过来。

    public class EasePresenter implements EaseConcacts.IPresenter {
    
      private EaseConcacts.IView iView;
      private EaseModel iModle;
    
      public EasePresenter(EaseConcacts.IView view) {
        this.iView = view;
        this.iModle = new EaseModel();
      }
    
      @Override
      public void requestData(Context context) {
        iModle.getData(context, test -> iView.onResultData(test));
    }
    

    EaseModel类

    首先要实现EaseConcacts契约类中IModel接口,这里写网络业务代码,网络请求完成后可以在此可以处理数据 增删改查、本地存储等等;然后把数据通过接口返回给EasePresenter类;

    public class EaseModel implements EaseConcacts.IModel {
    
      @Override
      public void getData(Context context, ModelListener modelListener) {
        RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
            new IResponseListener<BannerBean>() {
              @Override
              public void onSuccess(BannerBean data) {
                /**
                 * 在此可以处理数据 增删改查、本地存储等等
                 */
                Logout.e("data= "+data.toString());
                modelListener.onReslutJson(data.toString());
              }
    
              @Override
              public void onFail(OkHttpException failuer) {
                modelListener.onReslutJson("失败= "+failuer.getEmsg());
              }
            });
      }
    
    }
    

    Model弱化

    可以弱化Model的作用,把RetrofitClient网络请求当做Modle,可以省略Modle接口和类文件

    public class EasePresenter implements EaseConcacts.IPresenter {
    
      private EaseConcacts.IView iView;
      private EaseModel iModle;
    
      public EasePresenter(EaseConcacts.IView view) {
        this.iView = view;
        this.iModle = new EaseModel();
      }
    
      @Override
      public void requestData(Context context) {
    
        /**
         * 方式一:调用Modle进行调用接口
         */
        //iModle.getData(context, test -> iView.onResultData(test));
    
        /**
         * 方式二:弱化了Model的作用,这里RetrofitClient网络请求就是Modle,可以省略一个Modle文件
         */
        RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
            new IResponseListener<BannerBean>() {
              @Override
              public void onSuccess(BannerBean data) {
                Logout.e("data= " + data.toString());
                iView.onResultData(data.toString());
              }
    
              @Override
              public void onFail(OkHttpException failuer) {
                iView.onResultData("失败= " + failuer.getEmsg());
              }
            });
      }
    
    }
    

    封装MVP使用

    上面说过MVP缺点,Presenter和View相互持有引用,解除不及时的话容易出现内存泄漏。下面我们要创建一些Base类来处理一些公共的代码逻辑和防止内存泄漏。

    创建Base类

    BaseActivity:通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。
    BaseFragment:通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。
    BasePresenter:动态判断View是否已销毁,防止内存泄漏。
    BaseView:为了处理公共View逻辑。比如在BaseView中写公共处理UI方法、通过泛型对View进行绑定和解绑。


    BaseView类

    在BaseActivity、BaseFragment、BasePresenter中都要处理View的逻辑,都需要用泛型来接收View,所以所有的IView都要继承BaseView。

    public interface BaseView {
      //添加公共处理UI方法 比如处理接口失败的UI
    
    }
    

    BasePresenter类

    所有的Presenter类都要继承BasePresenter,在Base里对View做了统一的绑定和解绑的代码处理。还统一对View做了是否已解绑的判断,防止调用View中的回调方法(防止内存泄漏)。

    public abstract class BasePresenter<V extends BaseView> {
    
      private V view;
      private V proxyView;
    
      public V getView() {
        return proxyView;
      }
    
      /**
       * 绑定view
       */
      public void attachView(V view) {
        this.view = view;
        //参数一:类加载器
        ClassLoader classLoader = view.getClass().getClassLoader();
        //参数二:代理接口
        Class<?>[] interfaces = view.getClass().getInterfaces();
        //参数三:方法回调
        BaseViewInvocationHandler handler = new BaseViewInvocationHandler(view);
        proxyView = (V) Proxy.newProxyInstance(classLoader, interfaces, handler);
      }
    
      /**
       * 解绑view
       */
      public void detachView() {
        this.view = null;
      }
    
      private class BaseViewInvocationHandler implements InvocationHandler {
    
        private BaseView view;
    
        BaseViewInvocationHandler(BaseView view) {
          this.view = view;
        }
    
        //统一判断->控制对象访问权限
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
          //检查是不是为null
          if (isViewNull()) {
            //不用回调
            return null;
          }
          //执行回调
          return method.invoke(view, objects);
        }
      }
    
      private boolean isViewNull() {
        if (view == null) {
          return true;
        }
        return false;
      }
    
    }
    

    BaseActivity类

    通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。还有提供创建Presenter类的抽象方法,和获取Presenter类的构造方法。

    public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends
        Activity implements BaseView {
    
      private P presenter;
    
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (presenter == null) {
          presenter = createPresenter();
        }
        /**
         * 绑定view
         */
        if (presenter != null) {
          presenter.attachView((V) this);
        }
    
      }
    
      /**
       * 得到当前的Presenter类
       */
      public P getPresenter() {
        return presenter;
      }
    
      /**
       * 创建Presenter类
       */
      public abstract P createPresenter();
    
      @Override
      protected void onDestroy() {
        super.onDestroy();
        /**
         * 解绑view
         */
        if (presenter != null) {
          presenter.detachView();
        }
      }
    }
    

    BaseFragment类

    通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。还有提供创建Presenter类的抽象方法,和获取Presenter类的构造方法。和BaseActivity处理方式是一样的。

    public abstract class BaseFragment<V extends BaseView, P extends BasePresenter<V>> extends
        Fragment implements BaseView {
    
      private P presenter;
    
      @Override
      public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (presenter == null) {
          presenter = createPresenter();
        }
        if (presenter != null) {
          presenter.attachView((V) this);
        }
      }
    
      public P getPresenter() {
        return presenter;
      }
    
      public abstract P createPresenter();
    
      @Override
      public void onDestroyView() {
        super.onDestroyView();
        if (presenter != null) {
          presenter.detachView();
        }
      }
    
    }
    
    Activity中使用
    Activity使用

    LoginConcacts契约类

    来统一管理IView、IPresenter、IModel接口。可以对Model进行弱化,不仅少定义了IModel接口还可以少写一个实现IModel接口类,从而减少了代码量。

    public class LoginConcacts {
    
      interface IView extends BaseView {
    
        /**
         * 校验账号密码错误的提示信息
         */
        void showToast(String msg);
    
        /**
         * View中获取接口信息的回调
         */
        void onResultData(String data);
      }
    
      interface IPresenter {
    
        /**
         * 校验账号密码
         */
        void checkData(String userName, String userPwd);
    
        /**
         * 请求数据
         */
        void requestData(String userName, String userPwd);
      }
    
      /**
       * Model可以弱化掉,节省代码量
       */
      interface IModel {
    
        void getData(Context context, String userName, String userPwd, ModelListener modelListener);
    
        interface ModelListener {
    
          void onReslutJson(String test);
        }
      }
    
    }
    

    LoginPresenter类

    首先要继承BasePresenter,通过泛型传入LoginConcacts.IView,再实现LoginConcacts.IPresenter接口类并实现里面的方法。在LoginPresenter里可以直接进行网络请求,把Retrofit网络请求当做Model(把Model弱化),也可以调用LoginModel进行网络请求。

    public class LoginPresenter extends BasePresenter<LoginConcacts.IView> implements
        LoginConcacts.IPresenter {
    
      private Context context;
      private LoginModel modle;
    
      public LoginPresenter(Context context) {
        this.context = context;
        this.modle = new LoginModel();
      }
    
      @Override
      public void checkData(String userName, String userPwd) {
        if (TextUtils.isEmpty(userName)) {
          getView().showToast("请输入账号!");
        } else if (TextUtils.isEmpty(userPwd)) {
          getView().showToast("请输入密码!");
        } else {
          requestData(userName, userPwd);
        }
      }
    
      @Override
      public void requestData(String userName, String userPwd) {
    
        /**
         * 方式一:调用Modle进行调用接口
         */
    //    modle.getData(context,userName,userPwd, test -> getView().onResultData(test));
    
        /**
         * 方式二:把Modle弱引用掉,这里RetrofitClient网络请求就是Modle,可以省略一个Modle文件
         */
        Map<String, String> map = new HashMap<>();
        map.put("username", userName);
        map.put("password", userPwd);
    
        RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
            new IResponseListener<LoginBean>() {
              @Override
              public void onSuccess(LoginBean data) {
                Logout.e("data= " + data.toString());
                getView().onResultData(data.toString());
              }
    
              @Override
              public void onFail(OkHttpException failuer) {
                getView().onResultData("失败= " + failuer.getEmsg());
              }
            });
      }
    }
    

    LoginModel类

    首先要实现IModel接口类并实现方法,Model中不光是进行网络请求的,其它业务逻辑都可以处理。这里是进行网络请求,然后通过接口返回给Presenter。

    public class LoginModel implements IModel {
    
      @Override
      public void getData(Context context, String userName, String userPwd,
          ModelListener modelListener) {
        Map<String, String> map = new HashMap<>();
        map.put("username", userName);
        map.put("password", userPwd);
    
        RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
            new IResponseListener<LoginBean>() {
              @Override
              public void onSuccess(LoginBean data) {
                Logout.e("data= " + data.toString());
                modelListener.onReslutJson(data.toString());
              }
    
              @Override
              public void onFail(OkHttpException failuer) {
                modelListener.onReslutJson("失败= " + failuer.getEmsg());
              }
            });
      }
    
    }
    

    LoginActivity类

    LoginActivity就是View的实现类,首先要继承BaseActivity类并通过泛型传入View和Presenter。然后实现LoginConcacts.IView接口并实现其中的方法。

    public class LoginActivity extends
        BaseActivity<LoginConcacts.IView, LoginPresenter> implements LoginConcacts.IView {
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        findViewById(R.id.getJSON)
            .setOnClickListener((view) -> getPresenter().checkData("账号", "密码"));
      }
    
      @Override
      public LoginPresenter createPresenter() {
        return new LoginPresenter(this);
      }
    
      @Override
      public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
      }
    
      @Override
      public void onResultData(String data) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
      }
    }
    
    Fragment中使用
    Fragment使用

    BannerConcacts类

    来统一管理IView、IPresenter的接口,这里我对Model弱化掉了不再提供Model的实现类了。

    public class BannerConcacts {
    
      interface IView extends BaseView {
    
        /**
         * 获取接口数据回调方法
         */
        void onReslutData(String data);
      }
    
      interface IPresenter {
    
        /**
         * 进行网络业务处理
         */
        void requestData(Context context);
      }
    
    }
    

    BannerPresenter类

    首先继承BasePresenter类通过泛型传入相应的IView,然后再实现BannerConcacts.IPresenter接口并实现其中的方法。这里没有调用Model来进行业务处理,对Model弱化了

    public class BannerPresenter extends BasePresenter<BannerConcacts.IView> implements
        BannerConcacts.IPresenter {
    
      @Override
      public void requestData(Context context) {
    
        RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
            new IResponseListener<BannerBean>() {
              @Override
              public void onSuccess(BannerBean data) {
                Logout.e("Tag", "data= " + data.toString());
                getView().onReslutData(data.toString());
              }
    
              @Override
              public void onFail(OkHttpException failuer) {
                getView().onReslutData("失败= " + failuer.getEmsg());
              }
            });
      }
    }
    

    BannerFragment类

    BannerFragment就是View的实现类,首先要继承BaseFragment类并通过泛型传入View和Presenter。然后实现BannerConcacts.IView接口并实现其中的方法。使用方式和Activity一样。

    public class BannerFragment extends BaseFragment<BannerConcacts.IView, BannerPresenter> implements
        BannerConcacts.IView {
    
      @Nullable
      @Override
      public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
          @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.banner, null);
      }
    
      @Override
      public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.findViewById(R.id.getJSON)
            .setOnClickListener((v) -> getPresenter().requestData(getContext()));
      }
    
      @Override
      public BannerPresenter createPresenter() {
        return new BannerPresenter();
      }
    
      @Override
      public void onReslutData(String data) {
        Toast.makeText(getContext(), data, Toast.LENGTH_SHORT).show();
      }
    
    }
    
    我对MVP的理解

    有些人说MVP适合大型项目,我不这么认为。之前我做过某网的项目使用MVC模式,有些Activity代码行能达到两千多行代码,查找问题很麻烦。我说下我的看法,我认为View层业务逻辑比较多的话就适合MVP模式,业务逻辑比较少的话看个人你可以用MVP也可以用MVC。还有就是封装一个适合自己项目的框架。

    Demo地址:https://github.com/pengjunshan/MVPRetrofitRxJava

    相关文章

      网友评论

        本文标题:Android MVP模式简单使用和封装使用

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