71

Android MVP升级路(二)时尚版

 6 years ago
source link: http://mp.weixin.qq.com/s/iUcyerRmAXWD2rI4wpy1RA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Android MVP升级路(二)时尚版

Original JesseBraveMan 码农职场 2017-11-09 00:00 Posted on



关于作者

作者:JesseBraveMan

博客地址:http://www.jianshu.com/u/e19dfd08fca0

声明:本文由作者授权发布,未经原作者允许请勿转载

前言

第一篇文章的结尾对时尚版MVP结构做了一个简单的预告,下面继续从时尚版MVP说起。

时尚版MVP架构-Model层的优化

在从乞丐版MVP架构优化成平民版MVP架构的过程中,几乎每个单元都做了很大优化并封装到了base层,但是唯独Model层没什么变化。所以,时尚版MVP架构的优化主要就是对Model层的优化。

单独封装,集中管理

Model层相比其他单元来说比较特殊,因为它们更像一个整体,只是单纯的帮上层拿数据而已。再就是MVP的理念是让业务逻辑互相独立,这就导致每个的网络请求也被独立成了单个Model,这种方式在实际开发中就会出现一些问题:

  • 无法对所有Model统一管理。

  • 每个Model对外提供的获取数据方法不一样,上层请求数据没有规范。

  • 代码冗余高,网络数据请求除URL和参数外其他大概都一样的。

  • 对已存在的Model管理困难,不能直观的统计已存在的Model。

所以我们更希望Model层是一个庞大且独立单一模块,请求方式规范化,管理Model更加直观。

时尚版MVP架构Model层结构

如上图所示,时尚版MVP架构的Model层中,Presenter 请求数据不再直接调用具体的Model对象,统一以 DataModel 类作为数据请求层的入口,以常量类 Token 区别具体请求。 DataModel会根据Token的不同拉取底层对应的具体Model。

优化之后的Model层是一个庞大而且独立的模块,对外提供统一的请求数据方法与请求规则,这样做的好处有很多:

  • 数据请求单独编写,无需配合上层界面测试。

  • 统一管理,修改方便。

  • 利用Token类可以直观的统计出已存在的请求接口。

代码实现

根据上节结构图中的描述在考虑到实际情况,我们需要设计以下几个类:

  • DataModel: 数据层顶级入口,项目中所有数据都由该类流入和流出,负责分发所有的请求数据。

  • Token:数据请求标识类,定义了项目中所有的数据请求。

  • BaseModel:所有Model的顶级父类,负责对外提供数据请求标准,对内为所有Model提供请求的底层支持。

最后实现后理想的请求数据方法是:

BaseModel

BaseModel中定义了对外的请求数据规则,包括设置参数的方法和设置Callback的方法,还可以定义一些通用的数据请求方法,比如说网络请求的Get和Post方法。

public abstract class BaseModel<T>  {

    //数据请求参数
    protected String[] mParams;

    /**
     * 设置数据请求参数
     * @param args 参数数组
     */
    public  BaseModel params(String... args){
        mParams = args;
        return this;
    }

    // 添加Callback并执行数据请求
    // 具体的数据请求由子类实现
    public abstract void execute(Callback<T> callback);

    // 执行Get网络请求,此类看需求由自己选择写与不写
    protected void requestGetAPI(String url,Callback<T> callback){
        //这里写具体的网络请求
    }

    // 执行Post网络请求,此类看需求由自己选择写与不写
    protected void requestPostAPI(String url, Map params,Callback<T> callback){
        //这里写具体的网络请求
    }

}

写好了BaseModel后再看实现具体Model的方法:

public class UserDataModel extends BaseModel<String> {

    @Override
    public void execute(final Callback<String> callback) {

        // 模拟网络请求耗时操作
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                // mParams 是从父类得到的请求参数
                switch (mParams[0]){
                    case "normal":
                        callback.onSuccess("根据参数"+mParams[0]+"的请求网络数据成功");
                        break;

                    case "failure":
                        callback.onFailure("请求失败:参数有误");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }
        },2000);
    }
}

从上面代码段可以看出,实现具体的Model请求时必须要重写BaseModel的抽象方法execute

DataModel

由于DataModel负责数据请求的分发,所以最初打算作成一个简单工厂模式的样子,通过switch(token)语句判断要调用的Model。

但如果这样设计的话,在实际开发中我们每次添加一个数据请求接口,不光需要新建对应的Model和Token,还需要在DataModel类的switch(token)语句中新增加对应的判断,贼麻烦~

思来想去,我觉得利用反射机制会是一个比较理想的办法,请求数据时以具体Model的包名+类型作为Token,利用反射机制直接找到对应的Model。

public class DataModel {

    public static BaseModel request(String token){

        // 声明一个空的BaseModel
        BaseModel model = null;

        try {
            //利用反射机制获得对应Model对象的引用
            model = (BaseModel)Class.forName(token).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return model;
    }

}

Token

由于上节中DataModel使用反射机制获取对应Model的引用,所以Token中存的就应该是对应Model的包名+类名:

public class Token {
    // 包名
    private static final String PACKAGE_NAME = "com.jesse.mvp.data.model.";
    // 具体Model
    public static final String API_USER_DATA = PACKAGE_NAME + "UserDataModel"; 
}

使用方式

完成了Model层之后再去Presenter调用数据时的样子就舒服多了:

 DataModel
    // 设置请求标识token
    .request(Token.API_USER_DATA)
    // 添加请求参数,没有则不添加
    .params(userId)
    // 注册监听回调
    .execute(new Callback<String>() {

           @Override
           public void onSuccess(String data) {
               //调用view接口显示数据
               mView.showData(data);
           }

           @Override
           public void onFailure(String msg) {
               //调用view接口提示失败信息
               mView.showFailureMessage(msg);
           }

           @Override
           public void onError() {
               //调用view接口提示请求异常
               mView.showErrorMessage();
           }

           @Override
           public void onComplete() {
               // 隐藏正在加载进度条
               mView.hideLoading();
           }
 });

添加model步骤

  1. 新建一个Model并继承BaseModel,完成具体的数据请求。

  2. 在Token中添加对用的Model包名+类名。注意写好注释,方便以后查阅。

总结

经过优化的Model层很好的统一化了请求方法规范,利用BaseModel不仅有效的减少了数据请求的冗余代码,最关键的还是得到了将所有Model的集中控制权,例如我们想给所有的请求都加上coockies,直接在BaseModel层做处理即可。

时尚版MVP虽然只对Model层进行了优化,实际开发中已经能发挥很大的作用。

下面一章旗舰版将三层同时优化。

推荐阅读

仿bilibili刷新按钮的实现

一个精致的打勾小动画

带你实现漂亮的滑动卷尺

Image

分享是一种美德,关注是一种智慧。Image

我就是马云飞

长按识别二维码,关注公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK