MVP+Retrofit+RxJava的demo

MVP架构:

  • View:对应于Activity,负责处理用户事件和视图部分的展示,它可能是Activity或者Fragment类。
  • Model:业务逻辑和实体模型,负责访问数据。数据可以是远端的Server API,本地数据库或者SharedPreference等
  • Presenter:负责完成Model和View之间的交互

MVP的优点:

减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。降低了代码的耦合度,方便测试,使后期维护更容易。

图解:

这里写图片描述

从MVP图解中,很容易可以看出Model与View之间的交互由Presenter完成,而Presenter与View之间的交互则是通过接口的。Presenter 从View中获取所需要的参数交给Model去执行,执行过程中需要的反馈操作及结果再由View做出对应的显示(在下面demo代码中能体现出来)。Model 中主要是写一些耗时操作和对数据库的处理等操作。


Retrofit 2.0

Retrofit是一个类型安全的网络请求库,它和OKHttp共同出自于square公司。Retrofit是对OKHttp做了一层封装,把网络请求都交给了OKHttp,使我们只需要通过简单的配置就能使用retrofit来进行网络请求了。
Retrofit简化了网络请求流程,它可以通过注解(GET、POST、PUT、DELETE、HEAD)来配置请求参数,通过工厂来生成CallAdapter,Converter,可以使用不同的请求适配器CallAdapter(Rxjava、java等)[这篇文章中的demo用的是RxJava作为请求适配器],也可以使用不同的反序列化工具Converter(json、xml等)。它基于OKHttp做的封装使解耦更加彻底。


RxJava+ Retrofit 不多说,直接在例子中分析和介绍用法。

RxJava + Retrofit + MVP 登录例子demo
效果图如图所示:
这里写图片描述
首先,看下项目结构:
这里写图片描述

实现思路

(一) Model

model中主要是存放数据处理和业务逻辑等,从效果图可以看出至少有一个登录login()方法和一个实体类user,那么就先完成login()方法和User类:

User类:

public class User extends BaseResponseBean {

    /**
     "status": 1,
     "uid": "241",
     "upwd": "123456",
     "type": "2",
     "token": "e1eeffb878dc660226599be2abcdfb34",
     "errcode": "00001",
     "errinfo": "登录成功"
     */

    public String status;
    public String uid;
    public String upwd;
    public String type;
    public String token;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }
    public String getUpwd() {
        return upwd;
    }

    public void setUpwd(String upwd) {
        this.upwd= upwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "status='" + status + '\'' +
                ", uid='" + uid + '\'' +
                ",upwd = '" + upwd + '\'' +
                ", type='" + type + '\'' +
                ", token='" + token + '\'' +
                '}';
    }

}

ILoginModel接口:

public interface ILoginModel {
    /**
     * 网络请求 --- 登录
     */
    void login(String account, String password, String type);
}

ILoginModel 接口实现类 LoginModel

public class LoginModel1 implements ILoginModel {

    private ResponseOnListener mLoginResponseOnListener;

    public LoginModel1(ResponseOnListener mLoginResponseOnListener)
    {
        this.mLoginResponseOnListener = mLoginResponseOnListener;
    }

    @Override
    public void login(String account, String password,String type) {

        RequestUrl service = ServiceGenerator.createService(RequestUrl.class);
        Observable<User> stringObservable = service.LoginVery(CodeUtils.encodeToString(account),
                                                                 CodeUtils.encodeToString(password),
                                                                 CodeUtils.encodeToString(type));
        stringObservable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<User>() {
                    @Override
                    public void call(User s) {
                        Log.e("loginResponse1", s.toString());
                        User loginResponse = s;

                        if (loginResponse.errcode.equals("00001")) {
                            mLoginResponseOnListener.onSuccess(loginResponse);
                        } else {
                            mLoginResponseOnListener.onFailure(loginResponse.errinfo);
                        }
                    }
                });


    }


}

在login方法中我们可以看到,它网络链接是用retrofit+RxJava来实现的。Retrofit本身对RxJava提供了支持。使用方法如下:
首先添加:

compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
compile 'io.reactivex:rxjava:1.1.5'

然后创建Retrofit,代码如下:

/**
 * 封装的Retrofit工具类
 */
public class ServiceGenerator {

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
                                                        .connectTimeout(5, TimeUnit.SECONDS);
    private static Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl("http://zthdwl.com/wap.php/")
            .addConverterFactory(FastJsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create());

    public static <S> S createService(Class<S> serviceClass) {
        Retrofit retrofit = builder.client(httpClient.build()).build();
        return retrofit.create(serviceClass);
    }

}

定义RequestUrl:

public interface RequestUrl {

    @FormUrlEncoded
    @POST("Public/loginAction")
    Observable<User> LoginVery(@Field("phone") String phone, @Field("password") String password, @Field("type") String type);
}

然后就在login()方法中调用,login()方法中完成了Retrofit和RxJava的结合。

(二) View

View对应于activity,负责view的绘制及与用户的交互,它和presenter之间通过接口之间交互。故这里要定义一个接口类ILoginView,那么ILoginView接口中需要定义哪些方法呢?根据效果图,考虑全面的话不外乎有这几种方法:

  • 得到账号和密码 getAccount()getPassword()
  • 登录失败的时候清除密码 clearPwd()
  • 判断用户名和密码是否可用 isUserNameVaild(String userName)isPasswordVaild(String password)
  • 若用户名和密码可用改变登录按钮背景为高亮 changeLoginBtn()
  • 登录成功后跳转主界面方法 startHomeActivity()
  • 登录进行中时的progressbar的显示 showLoading()hideLoading
    综上,ILoginView接口完整代码如下:
public interface ILoginView extends IBaseView {

    /**
     * 得到账号和密码
     */
    String getAccount();

    String getPassword();

    /**
     * 登录失败清除密码
     */
    void clearPwd();

    /**
     * 判断用户名和密码是否可用
     */
    Boolean isUserNameVaild(String userName);

    Boolean isPasswordVaild(String password);

    /**
     * 改变按钮状态
     */
    void changeLoginBtn();

    void resetLoginBtn();

    /**
     * 登录成功后跳转
     */
    void startHomeActivity();

    /**
     * 找回密码
     */
    void findPasswordActivity();

    /**
     * 立即注册
     */
    void startRegistActivity();
}

view对应于activity,那么ILoginView接口的实现类就是 LoginAct 了,LoginAct 类:

public class LoginAct extends BaseActivity implements ILoginView {

    private LoginPresenter mLoginPresenter;
    @InjectView(R.id.login_top_bar)
    TopBarView loginTopBar;
    @InjectView(R.id.login_et_userName)
    EditText loginEtUserName;
    @InjectView(R.id.login_et_userPwd)
    ShowPwdEditText loginEtUserPwd;
    @InjectView(R.id.login_bt_login)
    Button loginBtLogin;
    @InjectView(R.id.login_tv_findPwd)
    TextView loginTvFindPwd;
    @InjectView(R.id.login_tv_regist)
    TextView loginTvRegist;

    private  Subscription subscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_login);
        ButterKnife.inject(this);

        mLoginPresenter = new LoginPresenter(this);
        initEvents();
    }

    private void initEvents() {
        Observable<CharSequence> observableUserName = RxTextView.textChanges(loginEtUserName);
        Observable<CharSequence> observablePassword = RxTextView.textChanges(loginEtUserPwd);
        /**
         *用combineLastest将ObservableEmail和ObservablePassword联合起来进行验证,
         * 若两者都满足条件,改变登录按钮为高亮背景和可点击状态
         * 可以在combineLatest继续添加其他条件
         * 相当于几个addTextChangedListener(TextWatcher())的累加作用
         */
        Observable.combineLatest(observableUserName, observablePassword, new Func2<CharSequence, CharSequence, Boolean>() {
            @Override
            public Boolean call(CharSequence userName, CharSequence password) {
                return isUserNameVaild(userName.toString()) && isPasswordVaild(password.toString());
            }
        }).subscribe(new Subscriber<Boolean>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Boolean verify) {
                if (verify) {
                    changeLoginBtn();
                } else {
                    resetLoginBtn();
                }

            }
        });
        /**用RxBinding的RxView和throttleFirst防止抖动点击*/
        RxView.clicks(loginBtLogin)
                .throttleFirst(2, TimeUnit.MILLISECONDS)//两秒内只取一个点击事件
                .subscribe(new Action1<Object>() {
                    @Override
                    public void call(Object o) {
                        /**做一些点击按钮的响应操作*/
                        mLoginPresenter.login(getAccount(), getPassword(), "2");
                    }
                });
    }

    /**
     * 得到用户账号
     */
    @Override
    public String getAccount() {
        return loginEtUserName.getText().toString();
    }

    /**
     * 得到密码
     */
    @Override
    public String getPassword() {
        return loginEtUserPwd.getText().toString();
    }

    /**
     * 清除密码
     */
    @Override
    public void clearPwd() {
        loginEtUserPwd.setText("");
    }

    /**
     * 验证用户手机号是否正确
     */
    @Override
    public Boolean isUserNameVaild(String userName) {
        /**
         移动:134、135、136、137、138、139、150、151、157(TD)、158、159、187、188
         联通:130、131、132、152、155、156、185、186
         电信:133、153、180、189、
         总结起来就是第一位必定为1,第二位必定为3或5或8,其他位置的可以为0-9
         */
        String telRegex = "[1][358]\\d{9}";//"[1]"代表第1位为数字1,"[358]"代表第二位可以为3、5、8中的一个,"\\d{9}"代表后面是可以是0~9的数字,有9位。
        return userName.matches(telRegex);
    }

    /**
     * 验证密码是否可用
     */
    @Override
    public Boolean isPasswordVaild(String password) {
        /**
         * 密码最小不少于四位数字
         */
        return password.length() > 4;
    }

    /**
     * 改变登录按钮的背景为高亮
     */
    @Override
    public void changeLoginBtn() {
        loginBtLogin.setEnabled(true);
        loginBtLogin.setBackgroundResource(R.drawable.btn_shape1);
    }

    /**
     * 恢复登录按钮初始状态
     */
    @Override
    public void resetLoginBtn() {
        loginBtLogin.setEnabled(false);
        loginBtLogin.setBackgroundResource(R.drawable.btn_shape);
    }

    /**
     * 跳转到主页面
     */
    @Override
    public void startHomeActivity() {
        startActivity(new Intent().setClass(LoginAct.this, HomeAct.class));
    }

    /**
     * 跳转到找回密码页面
     */
    @Override
    public void findPasswordActivity() {

    }

    /**
     * 跳转到注册页面
     */
    @Override
    public void startRegistActivity() {

    }

    @Override
    public void showLoading(String msg) {

    }
}

(三) Presenter

Presenter负责完成Model与View之间的交互,从view中获取需要的参数,交给model去执行业务逻辑处理,执行过程中需要的反馈及结果再通过Presenter交给view,让view做出反应和展示等。

经分析,LoginPresenter中有登录网络处理以及登录成功和失败的两种处理,LoginPresenter 类中代码如下:

public class LoginPresenter implements ResponseOnListener{

    private LoginModel1 iLoginModel;
    private ILoginView iLoginView;

    public LoginPresenter(ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel1(this);
    }
    /**登录处理
     * 显示加载进度条
     * 登录网络请求
     * */
    public void login(String account, String password,String type)
    {
        iLoginView.showLoading();
        iLoginModel.login(account, password,type);
    }

    /**登录成功的回调方法*/
    @Override
    public void onSuccess(Object tClass) {
        iLoginView.hideLoading();
        iLoginView.startHomeActivity();
        Log.e("object",tClass.toString());
    }

    /**登录失败处理*/
    @Override
    public void onFailure(String throwable) {
        iLoginView.hideLoading();
        iLoginView.clearPwd();
        Toast.makeText(iLoginView.getContext(),throwable.toString(),Toast.LENGTH_SHORT).show();
        Log.e("Throwable",throwable);
    }
}

MVP+Retrofit+RxJava结合的分析思路和方法就如上所示。

刚入行菜鸟的拙见,如有错误欢迎批评指出,定及时纠正,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值