一步一步带你认识MVP+Retrofit+Rxjava并封装(一)

本文已授权微信公众号 code小生 独家发布

转载请注明出处:一步一步带你认识MVP+Retrofit+Rxjava并封装(一)

一步一步带你认识MVP+Retrofit+Rxjava并封装(一)

一步一步带你认识MVP+Retrofit+Rxjava并封装(二)

序言:这本来是LZ一直想写的一个系列的文章(哎呀,说的好像自己挺牛逼似的>_<)当下最流行的设计模式之一的MVP再配上当下最流行的网络请求框架之一的Retrofit+Rxjava(这里我也不引发战争了,PHP是世界上最好的语言grin: :grin: :grin: ),相信大部分人看简书博客的时候都会经常看到高仿某某APP(基于MVP+Retrofit+Rxjava)类的文章,反正LZ是经常看到,好了,扯淡就扯到这里,下面开始我们的正题:

1、首先我们来很不情愿的简单介绍一下MVP这个东东,为何说不情愿,首先,这些概念性的东西我自己都有点看不下去,其次网上讲这个的东西实在是太多了。官方的解释为经典MVC模式的演化版,这里我们也不详细讲啥是MVC了,就是一种古老而又神奇的模式。讲一个很简单的栗子:

有一家早餐店,他们家卖包子、馒头、油条、粥等等这些东西,他们需要用到最重要的东西是面粉(原材料:大米),每次早餐店老板都从一家面粉店(大米店)订购,而这些大米都是面粉店老板去农民伯伯那里收购的。在这个小例子当中,农民伯伯的任务就是生产出大米,这其实就相当于MVP模式中的Model,大米就是实体,生产大米就是业务的逻辑;面粉店老板负责收购大米然后加工成面粉,这其实就相当于MVP模式中的Presenter,负责完成农民伯伯和早餐店老板这两边的间接交易;最后早餐店老板的任务就是负责将面粉做成各种各样的早餐供大家享用,这其实就相当于MVP模式中的View,负责将面粉做成各种各样的食物呈现在大家眼前。好了,相信通过这个栗子大家应该对MVP模式有一定的了解了,下面来看一张图:

盗用洋神的图片

2、相信用过MVP的同志们都有体会,每个人对于MVP模式的理解都不一样,这样导致写出来的代码也都风格迥异,但是思想都是一样的(即降低ModelView之间的耦合度,使得代码变的更清晰),所以即将学习MVP的小伙伴们看博客的时候不要惊慌,因为你们会看到各种的代码,下面我们一起来看一下LZ写的(写的不好的地方欢迎指正):

(1)、首先我们来看一下Model,这部分我理解的就是数据获得的地方,换句话说就是进行网络请求的地方(或者本地数据的获取),这里我写了一个Base类,将所有的Model请求数据相同的部分都放到了一起:

public class BaseModel extends BaseRetrofit {

    private static final String TAG = "BaseModel";

    protected CygApi mServletApi;   //所有的注解接口

    protected Map<String, String> mParams = new HashMap<>();

    public BaseModel() {
        super();
        mServletApi = mRetrofit.create(CygApi.class);
    }

    //获取公共参数
    @Override
    protected Map<String, String> getCommonMap() {
        Map<String, String> commonMap = new HashMap<>();
        commonMap.put("user_id", String.valueOf(UserDao.getInstance().getUserId()));
        commonMap.put("token", String.valueOf(UserDao.getInstance().getToken()));
        return commonMap;
    }

    //添加一个参数
    protected void addParams(String key, String value) {
        if (TextUtils.isEmpty(key)) {
            Log.e(TAG, "the key is null");
            return;
        }
        mParams.put(key, value);
    }

    //添加多个参数
    protected void addParams(Map<String, String> params) {
        if (null == params) {
            Log.e(TAG, "the map is null");
            return;
        }
        mParams.putAll(params);
    }
}

这里网络请求用的是Retrofit+RxJava,这一部分我打算放到下一篇再讲,在这个Base类里面主要添加了公共参数和添加普通参数。来看看一个登录的Model

public class LoginModel extends BaseModel {

    public static LoginModel getInstance() {
        return getPresent(LoginModel.class);
    }

    public void execute(String phone, String password, Observer<User>   observer) {
        addParams("username", phone);
        addParams("password", password);
        Observable observable = mServletApi.getUserInfo(mParams).map(new HttpFunction());
        toSubscribe(observable, observer);
    }
}

(2)、这里我是用的单例模式(其实我也不知道这样写单例会不会有错,用类去实例化一个对象>_<我觉得没啥问题,哈哈哈),然后execute方法就是进行网络请求了,在Presenter中调用这个方法就行了。然后我们来看一下BasePresenter

public class BasePresenter<VIEW> {

    private WeakReference<VIEW> mViews;

    protected void attachView(VIEW view) {
        mViews = new WeakReference<VIEW>(view);
    }

    protected VIEW getView() {
        return isViewAttached() ? mViews.get() : null;
    }

    private boolean isViewAttached() {
        return null != mViews && null != mViews.get();
    }

    protected void detachView() {
        if (null != mViews) {
            mViews.clear();
            mViews = null;
        }
    }
}

BasePresenter里面我只是关注了View,按照MVP模式的理解,我们应该在这个里面同时关注ViewModel,确实,很多demo都是这样干的,但是LZ前面是用的单例来写Model,所以在BasePresenter里面就暂时先关注View,还有一点需要说明的是,这里对View使用的弱引用,我们都知道View通常来说都是很大只的存在,为了防止内存泄漏,使用弱引用来及时释放内存。来看看一个登录的LoginPresenter

public class LoginPresenter extends BasePresenter<LoginView<User>> {

    public LoginPresenter(LoginView<User> loginView) {
        attachView(loginView);
    }


    public void getUserInfo(BaseImpl baseImpl) {
        LoginModel.getInstance().execute(getView().getUserName(), getView().getPassword(), new CygBaseObserver<User>(baseImpl, "正在登录") {
            @Override
            protected void onBaseNext(User data) {
                UserInfo userInfo = new UserInfo();
                userInfo.setId(data.getId());
                userInfo.setUsername(getView().getUserName());
                userInfo.setToken(data.getToken());
                UserDao.getInstance().deleteAll(UserInfo.class);
                UserDao.getInstance().insertObject(userInfo);
                getView().onRequestSuccessData(data);
            }
        });
    }

    public void toMainActivity(Activity activity) {
        activity.startActivity(new Intent(activity, MainActivity.class));
    }
}

(3)、在登录的LoginPresenter中调用LoginModel进行网络请求,只返回一个成功的回调,失败的回调我们在内部处理掉了,然后在回调成功之后做相应的数据操作(该回调给View的就回调给View,该存本地的就存本地)。然后来看看我们的View

public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView<User> {

    @BindView(R.id.al_et_user_name)
    TextInputEditText alEtUserName;
    @BindView(R.id.al_et_password)
    TextInputEditText alEtPassword;

    @Override
    protected int layoutRes() {
        return R.layout.activity_login;
    }

    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter(this);
    }

    @Override
    protected void initView() {

    }

    @Override
    public String getUserName() {
        return alEtUserName.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return alEtPassword.getText().toString().trim();
    }

    @OnClick(R.id.al_btn_login)
    public void onViewClicked() {
        if (TextUtils.isEmpty(getUserName())) {
            alEtPassword.setError("用户名不能为空");
            return;
        }
        if (TextUtils.isEmpty(getPassword())) {
            alEtPassword.setError("密码不能为空");
            return;
        }
        mPresenter.getUserInfo(this);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        moveTaskToBack(true);
    }

    @Override
    public void onRequestSuccessData(User data) {
        mPresenter.toMainActivity(this);
    }
}

View中,就是初始化Presenter,然后各种调Presenter中的方法,这里本来是可以在Presenter中直接调用toMainActivity()方法的,为了演示成功回调之后再回调给View,这里我就多做了一步操作。好了,接下来我们来看看接口:

public interface BaseRequestContract<T> {

    void onRequestSuccessData(T data);

}

这里写了一个Base接口,由于大多时候我们只关注成功的回调数据,这里我也只写了一个成功回调的方法(如果你有其他的需求,你可以在这里加一些公共的方法),如果你有需要的话你可以在子类中写错误回调的接口,接着我们来看看登录的接口有哪些方法:

public interface LoginView<T> extends BaseRequestContract<T>{

    String getUserName();

    String getPassword();

}

这里我需要获得用户的输入信息,所以只简单定义了两个方法用来获取用户名和密码。到这里我们的MVP模式就简单封装的差不多了,接下来我们来看一下最终的效果吧:

这里用eclipse+tomcat+mysql简单写了一个登录接口,这一部分LZ在之前的博客中有详细讲解,如有兴趣,请移步:

android开发怎么少的了后端(上)

android开发怎么少的了后端(中)

android开发怎么少的了后端(下)

好了,MVP的基本封装就讲到这里,下一节我们再来讲一下Retrofit+Rxjava的简单封装及使用,这里先奉上代码:

MVP 主工程代码

MVP module工程代码

可能很多人就会问了,为什么会有两份呢,这里我给大家看看我的项目工程

LZ把跟主项目无关的逻辑都写到module中去了,这样也是为了更好的重用代码。如果你直接去下载两个文件的话,那么请你下载之后把cygmodule-master文件夹的名字改成cygmodule,或者你也可以在主工程中将setting.gradle中的String jackchengPath = rootDir.absolutePath + '/../cygmodule'更换成你的路径

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容