android nucleus框架,Nucleus,一个好用的MVP框架

前言

今天给大家带来的是一个mvp框架,nucleus。这个框架是由国外的一位大神konmik搭建的,对mvp进行了一个封装,那么就先给大家说说MVP模式。

MVP模式

MVP是从经典的模式MVC演变而来,不同的是MVP中Model层并不会直接与View层有任何关系,而是通过Presenter来进行交互,至于MVP的好处这里我也不多陈述,网上有很多文章都对MVP的优点进行介绍,下面就先以传统的MVP给大家写一个登录的Demo感受一下。

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

MVP结构

传统MVP登录

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

目录结构

首先给大家看一下目录结构,当然,不同的人有不同的分包习惯,有的以功能模块分包,有的以传统分包,这个根据自己喜好来就行,这里是以Contract模式来分包,使用contract的好处是因为我们有一个插件,叫MVPHelper,一键生成Presenter和Model层的代码。

LoginContract

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

LoginContract结构

LoginActivity

首先用LoginActivity实现LoginContract.View并重写里面的方法,然后在onCreate中拿到对应Presenter层的引用,大家可以看到这里没有任何的逻辑判断,只有界面相关的操作。

package cn.lxt.mvpdemo.view.activity;

import android.app.ProgressDialog;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

import cn.lxt.mvpdemo.R;

import cn.lxt.mvpdemo.contract.LoginContract;

import cn.lxt.mvpdemo.presenter.LoginPresenter;

//首先要实现LoginContract.View

public class LoginActivity extends AppCompatActivity implements LoginContract.View, View.OnClickListener {

private ProgressDialog mProgressDialog;

private EditText mEtName;

private EditText mEtPsw;

private LoginContract.Presenter mPresenter;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//拿到Presenter层的引用

mPresenter = new LoginPresenter(this);

mEtName = (EditText) findViewById(R.id.et_name);

mEtPsw = (EditText) findViewById(R.id.et_psw);

Button button = (Button) findViewById(R.id.btn_login);

button.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_login:

//点击登录

String name = mEtName.getText().toString().trim();

String psw = mEtPsw.getText().toString().trim();

mPresenter.login(name, psw);

break;

}

}

//登陆成功后会走这里

@Override

public void loginSuccess() {

Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();

}

//登陆失败后会走这里

@Override

public void loginFailed(String msg) {

Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();

}

@Override

public void showDialog() {

mProgressDialog = new ProgressDialog(this);

mProgressDialog.setMessage("登陆中");

mProgressDialog.show();

}

@Override

public void hideDialog() {

if (mProgressDialog != null && mProgressDialog.isShowing())

mProgressDialog.dismiss();

}

}

LoginPresenter

用MvpHelper这个插件一键生成该类,同样的重写方法,在构造中拿到对应Model层的引用。

package cn.lxt.mvpdemo.presenter;

import cn.lxt.mvpdemo.contract.LoginContract;

import cn.lxt.mvpdemo.model.LoginModel;

import cn.lxt.mvpdemo.view.activity.LoginActivity;

/**

* Created by Administrator on 2017/8/17 0017.

*/

//这个类是自动生成的

public class LoginPresenter implements LoginContract.Presenter {

private final LoginActivity loginActivity;

private final LoginContract.Model mModel;

public LoginPresenter(LoginActivity loginActivity) {

this.loginActivity = loginActivity;

mModel = new LoginModel(this);

}

@Override

public void login(String name, String psw) {

//通知view层显示dialog

loginActivity.showDialog();

//通知model层调用登录

mModel.login(name, psw);

}

@Override

public void loginSuccess() {

//登陆成功之后的回调

loginActivity.hideDialog();

loginActivity.loginSuccess();

}

@Override

public void loginFailed(String msg) {

//登陆失败之后的回调

loginActivity.hideDialog();

loginActivity.loginFailed(msg);

}

}

LoginModel

这个类也是插件一键生成的,同样的,在构造中拿到Presenter的引用,在model层处理业务逻辑。

package cn.lxt.mvpdemo.model;

import android.text.TextUtils;

import java.util.concurrent.TimeUnit;

import cn.lxt.mvpdemo.contract.LoginContract;

import cn.lxt.mvpdemo.presenter.LoginPresenter;

import io.reactivex.Observable;

import io.reactivex.android.schedulers.AndroidSchedulers;

import io.reactivex.annotations.NonNull;

import io.reactivex.functions.Consumer;

import io.reactivex.schedulers.Schedulers;

/**

* Created by Administrator on 2017/8/17 0017.

*/

public class LoginModel implements LoginContract.Model {

private final LoginPresenter loginPresenter;

public LoginModel(LoginPresenter loginPresenter) {

this.loginPresenter = loginPresenter;

}

@Override

public void login(final String name, final String psw) {

//这里我们做逻辑处理

if (TextUtils.isEmpty(name)) {

loginPresenter.loginFailed("姓名不允许为空");

} else if (TextUtils.isEmpty(psw)) {

loginPresenter.loginFailed("密码不允许为空");

} else {

//这里模拟登录过程

Observable.timer(2, TimeUnit.SECONDS)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(@NonNull Long aLong) throws Exception {

loginPresenter.loginSuccess();

}

});

}

}

}

演示

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

登录演示

效果很明显,view层没有任何的逻辑处理,有的只是页面的显示,presenter层只是负责view层和model层的交互,model层只是负责逻辑的处理,分工明确,当然,这只是一个最基础的MVP模式,还有很多情况没有考虑到,那么接下来给大家带来一套成熟的MVP框架,也就上面说的nucleus。

Nucleus

Nucleus 是一个实现MVP+Rxjava的框架,首先给大家说一下他的好处

它支持在View/Fragment/Activity的Bundle中保存/恢复Presenter的状态,一个Presenter可以保存它的请求参数到bundles中,以便之后重启它们

它允许一个View实例持有多个Presenter对象

快速实现View和Presenter的绑定

提供线程的基类以便复用

支持在进程重启后,自动重新发起请求,在onDestroy方法中,自动退订RxJava订阅

使用相当简单

那么我们首先来看看他的整体目录结构

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

nucleus结构

整体逻辑都在第一个,下面两个是对v7和v4的扩展。

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

nucleus整体结构

nucleus使用(基于rxjava,不会rxjava的小伙伴请看我之前的文章)

第一步,添加依赖

compile 'info.android15.nucleus5:nucleus:5.0.0-beta1'

compile 'info.android15.nucleus5:nucleus-support-v4:5.0.0-beta1'

compile 'info.android15.nucleus5:nucleus-support-v7:5.0.0-beta1'

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

compile 'io.reactivex.rxjava2:rxjava:2.1.0'

compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'

第二步,继承

用你项目中的BaseActivity去继承他的NucleusAppCompatActivity,比如

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

继承关系

这样你所有继承BaseActivity的Activity就都可以使用了。

用你项目中的BaseFragment去继承他的NucleusSupportFragment

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

继承关系

用你的BasePresenter继承他的RxPresenter

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

继承关系

到此为止准备工作已经做完了。

第三步,网络请求

还是以刚才的登录为例子,不同的是这里用到了Retrofit+RxJava的模式做的联网请求,我这里用的是真实的网络请求,不是模拟的了。

首先,在LoginActivity中增加一个注解,里面的参数传入对应的Presenter类,直接上代码。

package cn.lxt.nucleusdemo.view.activity;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

import cn.lxt.nucleusdemo.R;

import cn.lxt.nucleusdemo.base.BaseActivity;

import cn.lxt.nucleusdemo.presenter.LoginPresenter;

import nucleus5.factory.RequiresPresenter;

//添加一个注解,参数为这个类所对应的Presenter

@RequiresPresenter(LoginPresenter.class)

public class LoginActivity extends BaseActivity implements View.OnClickListener {

private EditText mEtName;

private EditText mEtPsw;

private Button mButton;

@Override

protected void initLayout() {

setContentView(R.layout.activity_main);

}

@Override

protected void initView() {

mEtName = (EditText) findViewById(R.id.et_name);

mEtPsw = (EditText) findViewById(R.id.et_psw);

mButton = (Button) findViewById(R.id.btn_login);

}

@Override

protected void initData() {

}

@Override

protected void initClickListener() {

mButton.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_login:

String name = mEtName.getText().toString().trim();

String psw = mEtPsw.getText().toString().trim();

//注意,当你点击登录时,可以调用getPresenter()拿到对应P层的引用

getPresenter().login(this, name, psw);

break;

}

}

public void loginSuccess() {

Toast.makeText(this, "登陆成功", Toast.LENGTH_SHORT).show();

}

public void loginFailed() {

Toast.makeText(this, "登陆失败", Toast.LENGTH_SHORT).show();

}

}

重头戏来了,Presenter里面的逻辑代码

还是同样的,继承你自己的BasePresenter,不同的是你可以使用nucleus帮你封装的生命周期方法了,重写onCreate方法。

在你需要调用请求的地方,调用start()方法,参数为一个int值,你可以自己定义。

package cn.lxt.nucleusdemo.presenter;

import android.content.Context;

import android.os.Bundle;

import android.text.TextUtils;

import android.widget.Toast;

import cn.lxt.nucleusdemo.api.Service;

import cn.lxt.nucleusdemo.base.BasePresenter;

import cn.lxt.nucleusdemo.response.LoginResponse;

import cn.lxt.nucleusdemo.retrofit.RetrofitUtil;

import cn.lxt.nucleusdemo.view.activity.LoginActivity;

import io.reactivex.Observable;

import io.reactivex.android.schedulers.AndroidSchedulers;

import io.reactivex.annotations.NonNull;

import io.reactivex.disposables.Disposable;

import io.reactivex.functions.BiConsumer;

import io.reactivex.functions.Consumer;

import io.reactivex.schedulers.Schedulers;

import nucleus5.presenter.Factory;

/**

* Created by Administrator on 2017/8/17 0017.

*/

public class LoginPresenter extends BasePresenter {

private String name, psw;

private final int REQUSET_LOGIN = 0;

@Override

protected void onCreate(Bundle savedState) {

super.onCreate(savedState);

//调用该方法开始一个请求,第一个参数就是你start里面传入的int值,第二个参数就是一个Factory,所有的联网逻辑都写在里面,这里结合了Retrofit,第三个参数就是请求成功的回调,第四个参数就是请求失败的回调

restartableLatestCache(REQUSET_LOGIN, new Factory>() {

@Override

public Observable create() {

return RetrofitUtil.getRetrofit(context)

.create(Service.class)

.login(name, psw, "APP")

.subscribeOn(Schedulers.io())

.doOnSubscribe(new Consumer() {

@Override

public void accept(@NonNull Disposable disposable) throws Exception {

((LoginActivity) context).showDialog();

}

})

.observeOn(AndroidSchedulers.mainThread());

}

}, new BiConsumer() {

@Override

public void accept(LoginActivity loginActivity, LoginResponse loginResponse) throws Exception {

loginActivity.loginSuccess();

loginActivity.hideDialog();

//请求成功之后调用stop(),参数为start里面传入的参数

stop(REQUSET_LOGIN);

}

}, new BiConsumer() {

@Override

public void accept(LoginActivity loginActivity, Throwable throwable) throws Exception {

loginActivity.loginFailed();

loginActivity.hideDialog();

//请求失败之后调用stop(),参数为start里面传入的参数

stop(REQUSET_LOGIN);

}

});

}

public void login(Context context, String name, String psw) {

if (TextUtils.isEmpty(name)) {

Toast.makeText(context, "用户名不能为空", Toast.LENGTH_SHORT).show();

} else if (TextUtils.isEmpty(psw)) {

Toast.makeText(context, "密码不能为空", Toast.LENGTH_SHORT).show();

} else {

this.context = context;

this.name = name;

this.psw = psw;

start(REQUSET_LOGIN);

}

}

}

演示

这里不是模拟的登录了,而是真实的登录

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Nucleus登录成功

Nucleus源码浅析

到了这里,可能很多人就说,我做这么多道理有什么用呢?别着急,接下来带大家看看他的源码。

首先,我们看看继承他的NucleusAppCompatActivity做了什么事

在这个类中有一句这样的代码,拿到了我们定义的presenter层对象

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

然后把这个presenter对象与我们的activity的生命周期进行绑定,大家可以看到,在onSaveInstanceState里面,他帮我们保存了数据,这也是上面说的它支持在View/Fragment/Activity的Bundle中保存/恢复Presenter的状态,一个Presenter可以保存它的请求参数到bundles中,以便之后重启它们

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

再看RxPresenter又做了一些什么事

首先我们找到了start方法,首先不管有没有开启,nucleus先帮我们停止了任务,然后在把我们的id添加到了requested这个集合中,然后把id和我们即将开启的Factory放到了hashmap中进行对应,也就是第二个参数中的Factory,这也是为什么我们能通过stop(id)或者start(id)来控制的原因

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

start方法

接下来看看我们在onCreate中调用的restartableLatestCache方法

8df3c46baf14?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

restartable方法就是把id和factory进行绑定,然后开启了rxjava并将onNext和onError回调给我们,也就是我们传入的第三个和第四个参数。

总结

今天的Nucleus框架使用就给大家讲到这里,如果本文中有任何错误欢迎指出,同时也欢迎喜欢这个框架的朋友一起讨论,我们一起学习一起进步。

以上纯属于个人平时工作和学习的一些总结分享,如果有什么错误欢迎随时指出,大家可以讨论一起进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值